diff --git a/cmd/syncthing/monitor.go b/cmd/syncthing/monitor.go index d15a449ef..fd76966a6 100644 --- a/cmd/syncthing/monitor.go +++ b/cmd/syncthing/monitor.go @@ -83,6 +83,15 @@ func monitorMain(options serveOptions) { } args := os.Args + binary := args[0] + if build.IsWindows { + var err error + binary, err = expandExecutableInCurrentDirectory(binary) + if err != nil { + l.Warnln("Error starting the main Syncthing process:", err) + panic("Error starting the main Syncthing process") + } + } var restarts [restartCounts]time.Time stopSign := make(chan os.Signal, 1) @@ -104,7 +113,7 @@ func monitorMain(options serveOptions) { copy(restarts[0:], restarts[1:]) restarts[len(restarts)-1] = time.Now() - cmd := exec.Command(args[0], args[1:]...) + cmd := exec.Command(binary, args[1:]...) cmd.Env = childEnv stderr, err := cmd.StderrPipe() @@ -180,7 +189,7 @@ func monitorMain(options serveOptions) { // Restart the monitor process to release the .old // binary as part of the upgrade process. l.Infoln("Restarting monitor...") - if err = restartMonitor(args); err != nil { + if err = restartMonitor(binary, args); err != nil { l.Warnln("Restart:", err) } os.Exit(exitCode) @@ -203,6 +212,21 @@ func monitorMain(options serveOptions) { } } +func expandExecutableInCurrentDirectory(args0 string) (string, error) { + // Works around a restriction added in go1.19 that executables in the + // current directory are not resolved when specifying just an executable + // name (like e.g. "syncthing") + if !strings.ContainsRune(args0, os.PathSeparator) { + // Check if it's in PATH + _, err := exec.LookPath(args0) + if err != nil { + // Try to get the path to the current executable + return os.Executable() + } + } + return args0, nil +} + func copyStderr(stderr io.Reader, dst io.Writer) { br := bufio.NewReader(stderr) @@ -309,7 +333,7 @@ func copyStdout(stdout io.Reader, dst io.Writer) { } } -func restartMonitor(args []string) error { +func restartMonitor(binary string, args []string) error { // Set the STRESTART environment variable to indicate to the next // process that this is a restart and not initial start. This prevents // opening the browser on startup. @@ -319,19 +343,20 @@ func restartMonitor(args []string) error { // syscall.Exec is the cleanest way to restart on Unixes as it // replaces the current process with the new one, keeping the pid and // controlling terminal and so on - return restartMonitorUnix(args) + return restartMonitorUnix(binary, args) } // but it isn't supported on Windows, so there we start a normal // exec.Command and return. - return restartMonitorWindows(args) + return restartMonitorWindows(binary, args) } -func restartMonitorUnix(args []string) error { - if !strings.ContainsRune(args[0], os.PathSeparator) { +func restartMonitorUnix(binary string, args []string) error { + if !strings.ContainsRune(binary, os.PathSeparator) { // The path to the binary doesn't contain a slash, so it should be // found in $PATH. - binary, err := exec.LookPath(args[0]) + var err error + binary, err = exec.LookPath(binary) if err != nil { return err } @@ -341,8 +366,8 @@ func restartMonitorUnix(args []string) error { return syscall.Exec(args[0], args, os.Environ()) } -func restartMonitorWindows(args []string) error { - cmd := exec.Command(args[0], args[1:]...) +func restartMonitorWindows(binary string, args []string) error { + cmd := exec.Command(binary, args[1:]...) // Retain the standard streams cmd.Stderr = os.Stderr cmd.Stdout = os.Stdout