github.com/10XDev/rclone@v1.52.3-0.20200626220027-16af9ab76b2a/vfs/rc.go (about)

     1  package vfs
     2  
     3  import (
     4  	"context"
     5  	"strconv"
     6  	"strings"
     7  	"time"
     8  
     9  	"github.com/pkg/errors"
    10  	"github.com/rclone/rclone/fs"
    11  	"github.com/rclone/rclone/fs/rc"
    12  )
    13  
    14  // Add remote control for the VFS
    15  func (vfs *VFS) addRC() {
    16  	rc.Add(rc.Call{
    17  		Path: "vfs/forget",
    18  		Fn: func(ctx context.Context, in rc.Params) (out rc.Params, err error) {
    19  			root, err := vfs.Root()
    20  			if err != nil {
    21  				return nil, err
    22  			}
    23  
    24  			forgotten := []string{}
    25  			if len(in) == 0 {
    26  				root.ForgetAll()
    27  			} else {
    28  				for k, v := range in {
    29  					path, ok := v.(string)
    30  					if !ok {
    31  						return out, errors.Errorf("value must be string %q=%v", k, v)
    32  					}
    33  					path = strings.Trim(path, "/")
    34  					if strings.HasPrefix(k, "file") {
    35  						root.ForgetPath(path, fs.EntryObject)
    36  					} else if strings.HasPrefix(k, "dir") {
    37  						root.ForgetPath(path, fs.EntryDirectory)
    38  					} else {
    39  						return out, errors.Errorf("unknown key %q", k)
    40  					}
    41  					forgotten = append(forgotten, path)
    42  				}
    43  			}
    44  			out = rc.Params{
    45  				"forgotten": forgotten,
    46  			}
    47  			return out, nil
    48  		},
    49  		Title: "Forget files or directories in the directory cache.",
    50  		Help: `
    51  This forgets the paths in the directory cache causing them to be
    52  re-read from the remote when needed.
    53  
    54  If no paths are passed in then it will forget all the paths in the
    55  directory cache.
    56  
    57      rclone rc vfs/forget
    58  
    59  Otherwise pass files or dirs in as file=path or dir=path.  Any
    60  parameter key starting with file will forget that file and any
    61  starting with dir will forget that dir, eg
    62  
    63      rclone rc vfs/forget file=hello file2=goodbye dir=home/junk
    64  
    65  `,
    66  	})
    67  	rc.Add(rc.Call{
    68  		Path: "vfs/refresh",
    69  		Fn: func(ctx context.Context, in rc.Params) (out rc.Params, err error) {
    70  			root, err := vfs.Root()
    71  			if err != nil {
    72  				return nil, err
    73  			}
    74  			getDir := func(path string) (*Dir, error) {
    75  				path = strings.Trim(path, "/")
    76  				segments := strings.Split(path, "/")
    77  				var node Node = root
    78  				for _, s := range segments {
    79  					if dir, ok := node.(*Dir); ok {
    80  						node, err = dir.stat(s)
    81  						if err != nil {
    82  							return nil, err
    83  						}
    84  					}
    85  				}
    86  				if dir, ok := node.(*Dir); ok {
    87  					return dir, nil
    88  				}
    89  				return nil, EINVAL
    90  			}
    91  
    92  			recursive := false
    93  			{
    94  				const k = "recursive"
    95  
    96  				if v, ok := in[k]; ok {
    97  					s, ok := v.(string)
    98  					if !ok {
    99  						return out, errors.Errorf("value must be string %q=%v", k, v)
   100  					}
   101  					recursive, err = strconv.ParseBool(s)
   102  					if err != nil {
   103  						return out, errors.Errorf("invalid value %q=%v", k, v)
   104  					}
   105  					delete(in, k)
   106  				}
   107  			}
   108  
   109  			result := map[string]string{}
   110  			if len(in) == 0 {
   111  				if recursive {
   112  					err = root.readDirTree()
   113  				} else {
   114  					err = root.readDir()
   115  				}
   116  				if err != nil {
   117  					result[""] = err.Error()
   118  				} else {
   119  					result[""] = "OK"
   120  				}
   121  			} else {
   122  				for k, v := range in {
   123  					path, ok := v.(string)
   124  					if !ok {
   125  						return out, errors.Errorf("value must be string %q=%v", k, v)
   126  					}
   127  					if strings.HasPrefix(k, "dir") {
   128  						dir, err := getDir(path)
   129  						if err != nil {
   130  							result[path] = err.Error()
   131  						} else {
   132  							if recursive {
   133  								err = dir.readDirTree()
   134  							} else {
   135  								err = dir.readDir()
   136  							}
   137  							if err != nil {
   138  								result[path] = err.Error()
   139  							} else {
   140  								result[path] = "OK"
   141  							}
   142  
   143  						}
   144  					} else {
   145  						return out, errors.Errorf("unknown key %q", k)
   146  					}
   147  				}
   148  			}
   149  			out = rc.Params{
   150  				"result": result,
   151  			}
   152  			return out, nil
   153  		},
   154  		Title: "Refresh the directory cache.",
   155  		Help: `
   156  This reads the directories for the specified paths and freshens the
   157  directory cache.
   158  
   159  If no paths are passed in then it will refresh the root directory.
   160  
   161      rclone rc vfs/refresh
   162  
   163  Otherwise pass directories in as dir=path. Any parameter key
   164  starting with dir will refresh that directory, eg
   165  
   166      rclone rc vfs/refresh dir=home/junk dir2=data/misc
   167  
   168  If the parameter recursive=true is given the whole directory tree
   169  will get refreshed. This refresh will use --fast-list if enabled.
   170  
   171  `,
   172  	})
   173  	rc.Add(rc.Call{
   174  		Path:  "vfs/poll-interval",
   175  		Fn:    rcPollFunc(vfs),
   176  		Title: "Get the status or update the value of the poll-interval option.",
   177  		Help: `
   178  Without any parameter given this returns the current status of the
   179  poll-interval setting.
   180  
   181  When the interval=duration parameter is set, the poll-interval value
   182  is updated and the polling function is notified.
   183  Setting interval=0 disables poll-interval.
   184  
   185      rclone rc vfs/poll-interval interval=5m
   186  
   187  The timeout=duration parameter can be used to specify a time to wait
   188  for the current poll function to apply the new value.
   189  If timeout is less or equal 0, which is the default, wait indefinitely.
   190  
   191  The new poll-interval value will only be active when the timeout is
   192  not reached.
   193  
   194  If poll-interval is updated or disabled temporarily, some changes
   195  might not get picked up by the polling function, depending on the
   196  used remote.
   197  `,
   198  	})
   199  }
   200  
   201  func rcPollFunc(vfs *VFS) (rcPollFunc rc.Func) {
   202  	getDuration := func(k string, v interface{}) (time.Duration, error) {
   203  		s, ok := v.(string)
   204  		if !ok {
   205  			return 0, errors.Errorf("value must be string %q=%v", k, v)
   206  		}
   207  		interval, err := fs.ParseDuration(s)
   208  		if err != nil {
   209  			return 0, errors.Wrap(err, "parse duration")
   210  		}
   211  		return interval, nil
   212  	}
   213  	getInterval := func(in rc.Params) (time.Duration, bool, error) {
   214  		k := "interval"
   215  		v, ok := in[k]
   216  		if !ok {
   217  			return 0, false, nil
   218  		}
   219  		interval, err := getDuration(k, v)
   220  		if err != nil {
   221  			return 0, true, err
   222  		}
   223  		if interval < 0 {
   224  			return 0, true, errors.New("interval must be >= 0")
   225  		}
   226  		delete(in, k)
   227  		return interval, true, nil
   228  	}
   229  	getTimeout := func(in rc.Params) (time.Duration, error) {
   230  		k := "timeout"
   231  		v, ok := in[k]
   232  		if !ok {
   233  			return 10 * time.Second, nil
   234  		}
   235  		timeout, err := getDuration(k, v)
   236  		if err != nil {
   237  			return 0, err
   238  		}
   239  		delete(in, k)
   240  		return timeout, nil
   241  	}
   242  
   243  	_status := func(in rc.Params) (out rc.Params, err error) {
   244  		for k, v := range in {
   245  			return nil, errors.Errorf("invalid parameter: %s=%s", k, v)
   246  		}
   247  		return rc.Params{
   248  			"enabled":   vfs.Opt.PollInterval != 0,
   249  			"supported": vfs.pollChan != nil,
   250  			"interval": map[string]interface{}{
   251  				"raw":     vfs.Opt.PollInterval,
   252  				"seconds": vfs.Opt.PollInterval / time.Second,
   253  				"string":  vfs.Opt.PollInterval.String(),
   254  			},
   255  		}, nil
   256  	}
   257  	return func(ctx context.Context, in rc.Params) (out rc.Params, err error) {
   258  		interval, intervalPresent, err := getInterval(in)
   259  		if err != nil {
   260  			return nil, err
   261  		}
   262  		timeout, err := getTimeout(in)
   263  		if err != nil {
   264  			return nil, err
   265  		}
   266  		for k, v := range in {
   267  			return nil, errors.Errorf("invalid parameter: %s=%s", k, v)
   268  		}
   269  		if vfs.pollChan == nil {
   270  			return nil, errors.New("poll-interval is not supported by this remote")
   271  		}
   272  
   273  		if !intervalPresent {
   274  			return _status(in)
   275  		}
   276  		var timeoutHit bool
   277  		var timeoutChan <-chan time.Time
   278  		if timeout > 0 {
   279  			timer := time.NewTimer(timeout)
   280  			defer timer.Stop()
   281  			timeoutChan = timer.C
   282  		}
   283  		select {
   284  		case vfs.pollChan <- interval:
   285  			vfs.Opt.PollInterval = interval
   286  		case <-timeoutChan:
   287  			timeoutHit = true
   288  		}
   289  		out, err = _status(in)
   290  		if out != nil {
   291  			out["timeout"] = timeoutHit
   292  		}
   293  		return
   294  	}
   295  }