Skip to content

Commit 44b603d

Browse files
riptlncw
authored andcommitted
lib: add plugin support
This enables loading plugins from RCLONE_PLUGIN_PATH if set.
1 parent 349112d commit 44b603d

File tree

4 files changed

+93
-1
lines changed

4 files changed

+93
-1
lines changed

CONTRIBUTING.md

+34
Original file line numberDiff line numberDiff line change
@@ -376,3 +376,37 @@ Add your fs to the docs - you'll need to pick an icon for it from [fontawesome](
376376
* `docs/content/about.md` - front page of rclone.org
377377
* `docs/layouts/chrome/navbar.html` - add it to the website navigation
378378
* `bin/make_manual.py` - add the page to the `docs` constant
379+
380+
## Writing a plugin ##
381+
382+
New features (backends, commands) can also be added "out-of-tree", through Go plugins.
383+
Changes will be kept in a dynamically loaded file instead of being compiled into the main binary.
384+
This is useful if you can't merge your changes upstream or don't want to maintain a fork of rclone.
385+
386+
Usage
387+
388+
- Naming
389+
- Plugins names must have the pattern `librcloneplugin_KIND_NAME.so`.
390+
- `KIND` should be one of `backend`, `command` or `bundle`.
391+
- Example: A plugin with backend support for PiFS would be called
392+
`librcloneplugin_backend_pifs.so`.
393+
- Loading
394+
- Supported on macOS & Linux as of now. ([Go issue for Windows support](https://github.com/golang/go/issues/19282))
395+
- Supported on rclone v1.50 or greater.
396+
- All plugins in the folder specified by variable `$RCLONE_PLUGIN_PATH` are loaded.
397+
- If this variable doesn't exist, plugin support is disabled.
398+
- Plugins must be compiled against the exact version of rclone to work.
399+
(The rclone used during building the plugin must be the same as the source of rclone)
400+
401+
Building
402+
403+
To turn your existing additions into a Go plugin, move them to an external repository
404+
and change the top-level package name to `main`.
405+
406+
Check `rclone --version` and make sure that the plugin's rclone dependency and host Go version match.
407+
408+
Then, run `go build -buildmode=plugin -o PLUGIN_NAME.so .` to build the plugin.
409+
410+
[Go reference](https://godoc.org/github.com/rclone/rclone/lib/plugin)
411+
412+
[Minimal example](https://gist.github.com/terorie/21b517ee347828e899e1913efc1d684f)

lib/plugin/package.go

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Package plugin implements loading out-of-tree storage backends
2+
// using https://golang.org/pkg/plugin/ on Linux and macOS.
3+
//
4+
// If the $RCLONE_PLUGIN_PATH is present, any Go plugins in that dir
5+
// named like librcloneplugin_NAME.so will be loaded.
6+
//
7+
// To create a plugin, write the backend package like it was in-tree
8+
// but set the package name to "main". Then, build the plugin with
9+
//
10+
// go build -buildmode=plugin -o librcloneplugin_NAME.so
11+
//
12+
// where NAME equals the plugin's fs.RegInfo.Name.
13+
package plugin
14+
15+
// Build for plugin for unsupported platforms to stop go complaining
16+
// about "no buildable Go source files".

lib/plugin/plugin.go

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// +build darwin linux
2+
3+
package plugin
4+
5+
import (
6+
"fmt"
7+
"io/ioutil"
8+
"os"
9+
"path/filepath"
10+
"plugin"
11+
"strings"
12+
)
13+
14+
func init() {
15+
dir := os.Getenv("RCLONE_PLUGIN_PATH")
16+
if dir == "" {
17+
return
18+
}
19+
// Get file names of plugin dir
20+
listing, err := ioutil.ReadDir(dir)
21+
if err != nil {
22+
fmt.Fprintln(os.Stderr, "Failed to open plugin directory:", err)
23+
}
24+
// Enumerate file names, load valid plugins
25+
for _, file := range listing {
26+
// Match name
27+
fileName := file.Name()
28+
if !strings.HasPrefix(fileName, "librcloneplugin_") {
29+
continue
30+
}
31+
if !strings.HasSuffix(fileName, ".so") {
32+
continue
33+
}
34+
// Try to load plugin
35+
_, err := plugin.Open(filepath.Join(dir, fileName))
36+
if err != nil {
37+
fmt.Fprintf(os.Stderr, "Failed to load plugin %s: %s\n",
38+
fileName, err)
39+
}
40+
}
41+
}

rclone.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ package main
66
import (
77
_ "github.com/rclone/rclone/backend/all" // import all backends
88
"github.com/rclone/rclone/cmd"
9-
_ "github.com/rclone/rclone/cmd/all" // import all commands
9+
_ "github.com/rclone/rclone/cmd/all" // import all commands
10+
_ "github.com/rclone/rclone/lib/plugin" // import plugins
1011
)
1112

1213
func main() {

0 commit comments

Comments
 (0)