diff --git a/internal/agentupdate/restart_unix.go b/internal/agentupdate/restart_unix.go new file mode 100644 index 000000000..26bd1aa29 --- /dev/null +++ b/internal/agentupdate/restart_unix.go @@ -0,0 +1,22 @@ +//go:build !windows + +package agentupdate + +import ( + "fmt" + "os" + "syscall" +) + +// restartProcess replaces the current process with a new instance. +// On Unix-like systems, this uses syscall.Exec for an in-place restart. +func restartProcess(execPath string) error { + args := os.Args + env := os.Environ() + + if err := syscall.Exec(execPath, args, env); err != nil { + return fmt.Errorf("failed to restart: %w", err) + } + + return nil +} diff --git a/internal/agentupdate/restart_windows.go b/internal/agentupdate/restart_windows.go new file mode 100644 index 000000000..4542ec418 --- /dev/null +++ b/internal/agentupdate/restart_windows.go @@ -0,0 +1,22 @@ +//go:build windows + +package agentupdate + +import ( + "os" +) + +// restartProcess triggers a restart on Windows. +// For Windows services, we exit cleanly and rely on the Service Control Manager +// to restart the service (services are typically configured with automatic recovery). +// For non-service processes, os.Exit(0) is still appropriate as the process will +// restart with the new binary on next invocation. +func restartProcess(execPath string) error { + // Exit with code 0 to signal clean shutdown. + // Windows Service Control Manager will restart the service if configured + // for automatic recovery (which is the default for our PowerShell installer). + os.Exit(0) + + // This line is never reached, but satisfies the compiler + return nil +} diff --git a/internal/agentupdate/update.go b/internal/agentupdate/update.go index 355b5db38..37d5235a6 100644 --- a/internal/agentupdate/update.go +++ b/internal/agentupdate/update.go @@ -18,7 +18,6 @@ import ( "path/filepath" "runtime" "strings" - "syscall" "time" "github.com/rcourtman/pulse-go-rewrite/internal/utils" @@ -430,15 +429,8 @@ func (u *Updater) performUpdate(ctx context.Context) error { } } - // Restart with same arguments - args := os.Args - env := os.Environ() - - if err := syscall.Exec(execPath, args, env); err != nil { - return fmt.Errorf("failed to restart: %w", err) - } - - return nil + // Restart the process using platform-specific implementation + return restartProcess(execPath) } // determineArch returns the architecture string for download URLs (e.g., "linux-amd64", "darwin-arm64").