From 2dd38978aa183d4ce5a70f538afa11c0414b34da Mon Sep 17 00:00:00 2001 From: ethan Date: Fri, 14 Feb 2025 19:43:40 +0800 Subject: [PATCH] Windows: add option to startup juicefs as system service. fix #347, link #1546 (#5643) --- cmd/mount.go | 3 ++ cmd/mount_windows.go | 8 +++-- pkg/winfsp/winfs.go | 69 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+), 2 deletions(-) diff --git a/cmd/mount.go b/cmd/mount.go index 994626ec36e5..68bb5936e0d7 100644 --- a/cmd/mount.go +++ b/cmd/mount.go @@ -676,6 +676,9 @@ func mount(c *cli.Context) error { } os.Setenv("JFS_SUPERVISOR", strconv.Itoa(os.Getppid())) return launchMount(mp, vfsConf) + } else if runtime.GOOS == "windows" && c.Bool("background") { + daemonRun(c, addr, vfsConf) + return nil } logger.Infof("JuiceFS version %s", version.Version()) diff --git a/cmd/mount_windows.go b/cmd/mount_windows.go index 9b65c2aa68b0..a73eba23dae1 100644 --- a/cmd/mount_windows.go +++ b/cmd/mount_windows.go @@ -47,12 +47,16 @@ func mountFlags() []cli.Flag { Name: "delay-close", Usage: "delay file closing in seconds.", }, + &cli.BoolFlag{ + Name: "d", + Aliases: []string{"background"}, + Usage: "run in background(Windows: as a system service. support ONLY 1 volume mounting at the same time)", + }, } } func makeDaemon(c *cli.Context, conf *vfs.Config) error { - logger.Warnf("Cannot run in background in Windows.") - return nil + return winfsp.RunAsSystemSerivce(conf.Format.Name, c.Args().Get(1)) } func makeDaemonForSvc(c *cli.Context, m meta.Meta, metaUrl, listenAddr string) error { diff --git a/pkg/winfsp/winfs.go b/pkg/winfsp/winfs.go index 9e778a3d1ee2..5dbbc8f82a19 100644 --- a/pkg/winfsp/winfs.go +++ b/pkg/winfsp/winfs.go @@ -22,6 +22,7 @@ package winfsp import ( "fmt" "os" + "os/exec" "path" "runtime" "strings" @@ -35,6 +36,8 @@ import ( "github.com/juicedata/juicefs/pkg/meta" "github.com/juicedata/juicefs/pkg/utils" "github.com/juicedata/juicefs/pkg/vfs" + + "golang.org/x/sys/windows/registry" ) var logger = utils.GetLogger("juicefs") @@ -669,3 +672,69 @@ func Serve(v *vfs.VFS, fuseOpt string, fileCacheTo float64, asRoot bool, delayCl logger.Debugf("mount point: %s, options: %s", conf.Meta.MountPoint, options) _ = host.Mount(conf.Meta.MountPoint, []string{"-o", options}) } + +func RunAsSystemSerivce(name string, mountpoint string) error { + // https://winfsp.dev/doc/WinFsp-Service-Architecture/ + logger.Info("Running as Windows system service.") + + var cmds []string + for _, v := range os.Args[1:] { + if v == "-d" || v == "--background" { + continue + } + cmds = append(cmds, v) + } + + cmdLine := strings.Join(cmds, " ") + + regKeyPath := "SOFTWARE\\WOW6432Node\\WinFsp\\Services\\juicefs" + k, err := registry.OpenKey(registry.LOCAL_MACHINE, regKeyPath, registry.ALL_ACCESS) + if err != nil { + if err == syscall.ERROR_FILE_NOT_FOUND || err == syscall.ERROR_PATH_NOT_FOUND { + logger.Info("Registry key not found, create it") + k, _, err = registry.CreateKey(registry.LOCAL_MACHINE, regKeyPath, registry.ALL_ACCESS) + if err != nil { + return fmt.Errorf("Failed to create registry key: %s", err) + } + } else { + return fmt.Errorf("Failed to open registry key: %s", err) + } + } + defer k.Close() + + err = k.SetStringValue("CommandLine", cmdLine) + if err != nil { + return fmt.Errorf("Failed to set registry key: %s", err) + } + + securityDescriptor := "D:P(A;;RPWPLC;;;WD)" + err = k.SetStringValue("Security", securityDescriptor) + if err != nil { + return fmt.Errorf("Failed to set registry key: %s", err) + } + + filePath, err := os.Executable() + if err != nil { + return fmt.Errorf("Failed to get current file path: %s", err) + } + + err = k.SetStringValue("Executable", filePath) + if err != nil { + return fmt.Errorf("Failed to set registry key: %s", err) + } + + err = k.SetDWordValue("JobControl", 1) + if err != nil { + return fmt.Errorf("Failed to set registry key: %s", err) + } + + logger.Debug("Starting juicefs service.") + cmd := exec.Command("net", "use", mountpoint, "\\\\juicefs\\"+name) + err = cmd.Run() + if err != nil { + return fmt.Errorf("Failed to mount juicefs: %s", err) + } + + logger.Info("Juicefs system service started successfully.") + return nil +}