Fix Windows agent self-update restart failure

syscall.Exec is not supported on Windows, causing self-update to fail
with "failed to restart: not supported by windows".

Split restart logic into platform-specific files:
- restart_unix.go: Uses syscall.Exec for in-place process replacement
- restart_windows.go: Uses os.Exit(0) to let Windows SCM restart service

Related to #735
This commit is contained in:
rcourtman 2025-11-30 12:02:43 +00:00
parent 151db60267
commit d89ba45284
3 changed files with 46 additions and 10 deletions

View file

@ -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
}

View file

@ -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
}

View file

@ -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").