package cli import ( "fmt" "os" "os/exec" "runtime" "gitea.epss.net.cn/klesh/ss/internal/storage" ) // ensureRoot re-execs the current command under sudo if it isn't already // running as root. It resolves the config directory *before* elevating and // passes it through explicitly as SF_CONFIG_DIR, because sudo resets $HOME // to /root's — without this, the elevated process would silently resolve // the default config directory under /root instead of the invoking user's // home. // // Windows is handled separately: UAC elevation launches a new process that // can't transparently inherit the current console, so internal/service's // runAsAdmin instead prints manual elevation instructions rather than // auto-elevating. func ensureRoot() error { if runtime.GOOS == "windows" { return nil } if os.Geteuid() == 0 { return nil } s, err := storage.New() if err != nil { return fmt.Errorf("failed to resolve config directory: %w", err) } self, err := os.Executable() if err != nil { return fmt.Errorf("failed to determine executable path: %w", err) } sudoPath, err := exec.LookPath("sudo") if err != nil { return fmt.Errorf("this command requires root privileges, but sudo was not found on PATH: %w", err) } args := append([]string{"env", "SF_CONFIG_DIR=" + s.ConfigDir, self}, os.Args[1:]...) cmd := exec.Command(sudoPath, args...) cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr runErr := cmd.Run() if cmd.ProcessState != nil { os.Exit(cmd.ProcessState.ExitCode()) } return fmt.Errorf("failed to re-exec under sudo: %w", runErr) }