diff --git a/backend/internal/pkg/sysutil/restart.go b/backend/internal/pkg/sysutil/restart.go index 4ccca9d7..a09f5d2e 100644 --- a/backend/internal/pkg/sysutil/restart.go +++ b/backend/internal/pkg/sysutil/restart.go @@ -3,12 +3,39 @@ package sysutil import ( "fmt" "log" + "os" "os/exec" "runtime" ) const serviceName = "sub2api" +// findExecutable finds the full path of an executable +// by checking common system paths +func findExecutable(name string) string { + // First try exec.LookPath (uses current PATH) + if path, err := exec.LookPath(name); err == nil { + return path + } + + // Fallback: check common paths + commonPaths := []string{ + "/usr/bin/" + name, + "/bin/" + name, + "/usr/sbin/" + name, + "/sbin/" + name, + } + + for _, path := range commonPaths { + if _, err := os.Stat(path); err == nil { + return path + } + } + + // Return the name as-is and let exec fail with a clear error + return name +} + // RestartService triggers a service restart via systemd. // // IMPORTANT: This function initiates the restart and returns immediately. @@ -30,10 +57,17 @@ func RestartService() error { log.Println("Initiating service restart...") + // Find full paths for sudo and systemctl + // This ensures the commands work even if PATH is limited in systemd service + sudoPath := findExecutable("sudo") + systemctlPath := findExecutable("systemctl") + + log.Printf("Using sudo: %s, systemctl: %s", sudoPath, systemctlPath) + // The sub2api user has NOPASSWD sudo access for systemctl commands // (configured by install.sh in /etc/sudoers.d/sub2api). // Use -n (non-interactive) to prevent sudo from waiting for password input - cmd := exec.Command("sudo", "-n", "systemctl", "restart", serviceName) + cmd := exec.Command(sudoPath, "-n", systemctlPath, "restart", serviceName) if err := cmd.Start(); err != nil { return fmt.Errorf("failed to initiate service restart: %w", err) }