From e1d34ef427a229334e54b27f6eed8dc9c77f93aa Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Thu, 23 Jul 2020 13:08:38 +0100 Subject: [PATCH] mount: factor Mount into mountlib and tidy signal handling This factors common code from mount, cmount and mount2 into mountlib. It also uses atexit for unregistering the mount. --- cmd/cmount/mount.go | 56 +-------------------------------- cmd/mount/mount.go | 73 +++++-------------------------------------- cmd/mount2/mount.go | 62 +----------------------------------- cmd/mountlib/mount.go | 63 ++++++++++++++++++++++++++++++++++--- 4 files changed, 68 insertions(+), 186 deletions(-) diff --git a/cmd/cmount/mount.go b/cmd/cmount/mount.go index 2ddb65f9c..64b1b69f0 100644 --- a/cmd/cmount/mount.go +++ b/cmd/cmount/mount.go @@ -11,17 +11,13 @@ package cmount import ( "fmt" "os" - "os/signal" "runtime" - "syscall" "time" "github.com/billziss-gh/cgofuse/fuse" - "github.com/okzk/sdnotify" "github.com/pkg/errors" "github.com/rclone/rclone/cmd/mountlib" "github.com/rclone/rclone/fs" - "github.com/rclone/rclone/lib/atexit" "github.com/rclone/rclone/vfs" ) @@ -37,10 +33,8 @@ func init() { if runtime.GOOS == "windows" { name = "mount" } - mountlib.NewMountCommand(name, false, Mount) - // Add mount to rc + mountlib.NewMountCommand(name, false, mount) mountlib.AddRc("cmount", mount) - } // mountOptions configures the options from the command line flags @@ -216,51 +210,3 @@ func mount(VFS *vfs.VFS, mountpoint string) (<-chan error, func() error, error) return errChan, unmount, nil } - -// Mount mounts the remote at mountpoint. -// -// If noModTime is set then it -func Mount(VFS *vfs.VFS, mountpoint string) error { - // Mount it - errChan, unmount, err := mount(VFS, mountpoint) - if err != nil { - return errors.Wrap(err, "failed to mount FUSE fs") - } - - // Note cgofuse unmounts the fs on SIGINT etc - - sigHup := make(chan os.Signal, 1) - signal.Notify(sigHup, syscall.SIGHUP) - - atexit.Register(func() { - _ = unmount() - }) - - if err := sdnotify.Ready(); err != nil && err != sdnotify.ErrSdNotifyNoSocket { - return errors.Wrap(err, "failed to notify systemd") - } - -waitloop: - for { - select { - // umount triggered outside the app - case err = <-errChan: - break waitloop - // user sent SIGHUP to clear the cache - case <-sigHup: - root, err := VFS.Root() - if err != nil { - fs.Errorf(VFS.Fs(), "Error reading root: %v", err) - } else { - root.ForgetAll() - } - } - } - - _ = sdnotify.Stopping() - if err != nil { - return errors.Wrap(err, "failed to umount FUSE fs") - } - - return nil -} diff --git a/cmd/mount/mount.go b/cmd/mount/mount.go index 7e34af28d..c49cb3b6f 100644 --- a/cmd/mount/mount.go +++ b/cmd/mount/mount.go @@ -6,23 +6,16 @@ package mount import ( "fmt" - "os" - "os/signal" - "syscall" "bazil.org/fuse" fusefs "bazil.org/fuse/fs" - "github.com/okzk/sdnotify" - "github.com/pkg/errors" "github.com/rclone/rclone/cmd/mountlib" "github.com/rclone/rclone/fs" - "github.com/rclone/rclone/lib/atexit" "github.com/rclone/rclone/vfs" ) func init() { - mountlib.NewMountCommand("mount", false, Mount) - // Add mount to rc + mountlib.NewMountCommand("mount", false, mount) mountlib.AddRc("mount", mount) } @@ -85,6 +78,12 @@ func mountOptions(VFS *vfs.VFS, device string) (options []fuse.MountOption) { // returns an error, and an error channel for the serve process to // report an error when fusermount is called. func mount(VFS *vfs.VFS, mountpoint string) (<-chan error, func() error, error) { + if mountlib.DebugFUSE { + fuse.Debug = func(msg interface{}) { + fs.Debugf("fuse", "%v", msg) + } + } + f := VFS.Fs() fs.Debugf(f, "Mounting on %q", mountpoint) c, err := fuse.Mount(mountpoint, mountOptions(VFS, f.Name()+":"+f.Root())...) @@ -120,61 +119,3 @@ func mount(VFS *vfs.VFS, mountpoint string) (<-chan error, func() error, error) return errChan, unmount, nil } - -// Mount mounts the remote at mountpoint. -// -// If noModTime is set then it -func Mount(VFS *vfs.VFS, mountpoint string) error { - if mountlib.DebugFUSE { - fuse.Debug = func(msg interface{}) { - fs.Debugf("fuse", "%v", msg) - } - } - - // Mount it - errChan, unmount, err := mount(VFS, mountpoint) - if err != nil { - return errors.Wrap(err, "failed to mount FUSE fs") - } - - sigInt := make(chan os.Signal, 1) - signal.Notify(sigInt, syscall.SIGINT, syscall.SIGTERM) - sigHup := make(chan os.Signal, 1) - signal.Notify(sigHup, syscall.SIGHUP) - atexit.IgnoreSignals() - atexit.Register(func() { - _ = unmount() - }) - - if err := sdnotify.Ready(); err != nil && err != sdnotify.ErrSdNotifyNoSocket { - return errors.Wrap(err, "failed to notify systemd") - } - -waitloop: - for { - select { - // umount triggered outside the app - case err = <-errChan: - break waitloop - // Program abort: umount - case <-sigInt: - err = unmount() - break waitloop - // user sent SIGHUP to clear the cache - case <-sigHup: - root, err := VFS.Root() - if err != nil { - fs.Errorf(VFS.Fs(), "Error reading root: %v", err) - } else { - root.ForgetAll() - } - } - } - - _ = sdnotify.Stopping() - if err != nil { - return errors.Wrap(err, "failed to umount FUSE fs") - } - - return nil -} diff --git a/cmd/mount2/mount.go b/cmd/mount2/mount.go index 148abb25b..a30515b92 100644 --- a/cmd/mount2/mount.go +++ b/cmd/mount2/mount.go @@ -7,27 +7,18 @@ package mount2 import ( "fmt" "log" - "os" - "os/signal" "runtime" - "syscall" fusefs "github.com/hanwen/go-fuse/v2/fs" "github.com/hanwen/go-fuse/v2/fuse" - "github.com/okzk/sdnotify" - "github.com/pkg/errors" "github.com/rclone/rclone/cmd/mountlib" "github.com/rclone/rclone/fs" - "github.com/rclone/rclone/lib/atexit" "github.com/rclone/rclone/vfs" ) func init() { - mountlib.NewMountCommand("mount2", true, Mount) - - // Add mount to rc + mountlib.NewMountCommand("mount2", true, mount) mountlib.AddRc("mount2", mount) - } // mountOptions configures the options from the command line flags @@ -229,54 +220,3 @@ func mount(VFS *vfs.VFS, mountpoint string) (<-chan error, func() error, error) fs.Debugf(f, "Mount started") return errs, umount, nil } - -// Mount mounts the remote at mountpoint. -// -// If noModTime is set then it -func Mount(VFS *vfs.VFS, mountpoint string) error { - // Mount it - errChan, unmount, err := mount(VFS, mountpoint) - if err != nil { - return errors.Wrap(err, "failed to mount FUSE fs") - } - - sigInt := make(chan os.Signal, 1) - signal.Notify(sigInt, syscall.SIGINT, syscall.SIGTERM) - sigHup := make(chan os.Signal, 1) - signal.Notify(sigHup, syscall.SIGHUP) - atexit.Register(func() { - _ = unmount() - }) - - if err := sdnotify.Ready(); err != nil && err != sdnotify.ErrSdNotifyNoSocket { - return errors.Wrap(err, "failed to notify systemd") - } - -waitloop: - for { - select { - // umount triggered outside the app - case err = <-errChan: - break waitloop - // Program abort: umount - case <-sigInt: - err = unmount() - break waitloop - // user sent SIGHUP to clear the cache - case <-sigHup: - root, err := VFS.Root() - if err != nil { - fs.Errorf(VFS.Fs(), "Error reading root: %v", err) - } else { - root.ForgetAll() - } - } - } - - _ = sdnotify.Stopping() - if err != nil { - return errors.Wrap(err, "failed to umount FUSE fs") - } - - return nil -} diff --git a/cmd/mountlib/mount.go b/cmd/mountlib/mount.go index c76abe637..0aca24465 100644 --- a/cmd/mountlib/mount.go +++ b/cmd/mountlib/mount.go @@ -4,16 +4,20 @@ import ( "io" "log" "os" + "os/signal" "path/filepath" "runtime" "strings" + "syscall" "time" + "github.com/okzk/sdnotify" "github.com/pkg/errors" "github.com/rclone/rclone/cmd" "github.com/rclone/rclone/fs" "github.com/rclone/rclone/fs/config" "github.com/rclone/rclone/fs/config/flags" + "github.com/rclone/rclone/lib/atexit" "github.com/rclone/rclone/vfs" "github.com/rclone/rclone/vfs/vfsflags" "github.com/spf13/cobra" @@ -44,8 +48,6 @@ type ( UnmountFn func() error // MountFn is called to mount the file system MountFn func(VFS *vfs.VFS, mountpoint string) (<-chan error, func() error, error) - // MountBlockingFn is called to mount the file system and block - MountBlockingFn func(VFS *vfs.VFS, mountpoint string) error ) // Global constants @@ -108,7 +110,7 @@ func checkMountpointOverlap(root, mountpoint string) error { } // NewMountCommand makes a mount command with the given name and Mount function -func NewMountCommand(commandName string, hidden bool, Mount MountBlockingFn) *cobra.Command { +func NewMountCommand(commandName string, hidden bool, mount MountFn) *cobra.Command { var commandDefinition = &cobra.Command{ Use: commandName + " remote:path /path/to/mountpoint", Hidden: hidden, @@ -349,7 +351,7 @@ be copied to the vfs cache before opening with --vfs-cache-mode full. } VFS := vfs.New(fdst, &vfsflags.Opt) - err := Mount(VFS, mountpoint) + err := Mount(VFS, mountpoint, mount) if err != nil { log.Fatalf("Fatal error: %v", err) } @@ -410,3 +412,56 @@ func ClipBlocks(b *uint64) { *b = max } } + +// Mount mounts the remote at mountpoint. +// +// If noModTime is set then it +func Mount(VFS *vfs.VFS, mountpoint string, mount MountFn) error { + // Mount it + errChan, unmount, err := mount(VFS, mountpoint) + if err != nil { + return errors.Wrap(err, "failed to mount FUSE fs") + } + + // Unmount on exit + fnHandle := atexit.Register(func() { + _ = unmount() + _ = sdnotify.Stopping() + }) + defer atexit.Unregister(fnHandle) + + // Notify systemd + if err := sdnotify.Ready(); err != nil && err != sdnotify.ErrSdNotifyNoSocket { + return errors.Wrap(err, "failed to notify systemd") + } + + // Reload VFS cache on SIGHUP + sigHup := make(chan os.Signal, 1) + signal.Notify(sigHup, syscall.SIGHUP) + +waitloop: + for { + select { + // umount triggered outside the app + case err = <-errChan: + break waitloop + // user sent SIGHUP to clear the cache + case <-sigHup: + root, err := VFS.Root() + if err != nil { + fs.Errorf(VFS.Fs(), "Error reading root: %v", err) + } else { + root.ForgetAll() + } + } + } + + _ = unmount() + _ = sdnotify.Stopping() + + if err != nil { + return errors.Wrap(err, "failed to umount FUSE fs") + } + + return nil +}