diff --git a/vfs/rc.go b/vfs/rc.go index bfe47fe32..7a010abb8 100644 --- a/vfs/rc.go +++ b/vfs/rc.go @@ -3,6 +3,7 @@ package vfs import ( "strconv" "strings" + "time" "github.com/ncw/rclone/fs" "github.com/ncw/rclone/fs/rc" @@ -166,6 +167,128 @@ starting with dir will refresh that directory, eg If the parameter recursive=true is given the whole directory tree will get refreshed. This refresh will use --fast-list if enabled. +`, + }) + rc.Add(rc.Call{ + Path: "vfs/poll-interval", + Fn: rcPollFunc(vfs), + Title: "Get the status or update the value of the poll-interval option.", + Help: ` +Without any parameter given this returns the current status of the +poll-interval setting. + +When the interval=duration parameter is set, the poll-interval value +is updated and the polling function is notified. +Setting interval=0 disables poll-interval. + + rclone rc vfs/poll-interval interval=5m + +The timeout=duration parameter can be used to specify a time to wait +for the current poll function to apply the new value. +If timeout is less or equal 0, which is the default, wait indefinitely. + +The new poll-interval value will only be active when the timeout is +not reached. + +If poll-interval is updated or disabled temporarily, some changes +might not get picked up by the polling function, depending on the +used remote. `, }) } + +func rcPollFunc(vfs *VFS) (rcPollFunc rc.Func) { + getDuration := func(k string, v interface{}) (time.Duration, error) { + s, ok := v.(string) + if !ok { + return 0, errors.Errorf("value must be string %q=%v", k, v) + } + interval, err := fs.ParseDuration(s) + if err != nil { + return 0, errors.Wrap(err, "parse duration") + } + return interval, nil + } + getInterval := func(in rc.Params) (time.Duration, bool, error) { + k := "interval" + v, ok := in[k] + if !ok { + return 0, false, nil + } + interval, err := getDuration(k, v) + if err != nil { + return 0, true, err + } + if interval < 0 { + return 0, true, errors.New("interval must be >= 0") + } + delete(in, k) + return interval, true, nil + } + getTimeout := func(in rc.Params) (time.Duration, error) { + k := "timeout" + v, ok := in[k] + if !ok { + return 10 * time.Second, nil + } + timeout, err := getDuration(k, v) + if err != nil { + return 0, err + } + delete(in, k) + return timeout, nil + } + + _status := func(in rc.Params) (out rc.Params, err error) { + for k, v := range in { + return nil, errors.Errorf("invalid parameter: %s=%s", k, v) + } + return rc.Params{ + "enabled": vfs.Opt.PollInterval != 0, + "supported": vfs.pollChan != nil, + "interval": map[string]interface{}{ + "raw": vfs.Opt.PollInterval, + "seconds": vfs.Opt.PollInterval / time.Second, + "string": vfs.Opt.PollInterval.String(), + }, + }, nil + } + return func(in rc.Params) (out rc.Params, err error) { + interval, intervalPresent, err := getInterval(in) + if err != nil { + return nil, err + } + timeout, err := getTimeout(in) + if err != nil { + return nil, err + } + for k, v := range in { + return nil, errors.Errorf("invalid parameter: %s=%s", k, v) + } + if vfs.pollChan == nil { + return nil, errors.New("poll-interval is not supported by this remote") + } + + if !intervalPresent { + return _status(in) + } + var timeoutHit bool + var timeoutChan <-chan time.Time + if timeout > 0 { + timer := time.NewTimer(timeout) + defer timer.Stop() + timeoutChan = timer.C + } + select { + case vfs.pollChan <- interval: + vfs.Opt.PollInterval = interval + case <-timeoutChan: + timeoutHit = true + } + out, err = _status(in) + if out != nil { + out["timeout"] = timeoutHit + } + return + } +}