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 }