fix: add Windows compatibility for process management (#58)
Add build tags to separate Unix and Windows process handling in pkg/php: - services_unix.go: Unix-specific process group handling (Setpgid, Getpgid, Kill) - services_windows.go: Windows-compatible alternatives using os.Signal - services.go: Use platform-agnostic helper functions The pkg/php package now compiles on Windows. Process termination works via os.Interrupt/os.Kill instead of Unix signals. Fixes #56 Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
0c5e0c6435
commit
b02b57e6fb
3 changed files with 80 additions and 17 deletions
|
|
@ -10,7 +10,6 @@ import (
|
|||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/host-uk/core/pkg/cli"
|
||||
|
|
@ -120,10 +119,8 @@ func (s *baseService) startProcess(ctx context.Context, cmdName string, args []s
|
|||
s.cmd.Stderr = logFile
|
||||
s.cmd.Env = append(os.Environ(), env...)
|
||||
|
||||
// Set process group for clean shutdown
|
||||
s.cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||
Setpgid: true,
|
||||
}
|
||||
// Set platform-specific process attributes for clean shutdown
|
||||
setSysProcAttr(s.cmd)
|
||||
|
||||
if err := s.cmd.Start(); err != nil {
|
||||
logFile.Close()
|
||||
|
|
@ -159,13 +156,8 @@ func (s *baseService) stopProcess() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Send SIGTERM to process group
|
||||
pgid, err := syscall.Getpgid(s.cmd.Process.Pid)
|
||||
if err == nil {
|
||||
syscall.Kill(-pgid, syscall.SIGTERM)
|
||||
} else {
|
||||
s.cmd.Process.Signal(syscall.SIGTERM)
|
||||
}
|
||||
// Send termination signal to process (group on Unix)
|
||||
signalProcessGroup(s.cmd, termSignal())
|
||||
|
||||
// Wait for graceful shutdown with timeout
|
||||
done := make(chan struct{})
|
||||
|
|
@ -179,11 +171,7 @@ func (s *baseService) stopProcess() error {
|
|||
// Process exited gracefully
|
||||
case <-time.After(5 * time.Second):
|
||||
// Force kill
|
||||
if pgid, err := syscall.Getpgid(s.cmd.Process.Pid); err == nil {
|
||||
syscall.Kill(-pgid, syscall.SIGKILL)
|
||||
} else {
|
||||
s.cmd.Process.Kill()
|
||||
}
|
||||
signalProcessGroup(s.cmd, killSignal())
|
||||
}
|
||||
|
||||
s.running = false
|
||||
|
|
|
|||
41
pkg/php/services_unix.go
Normal file
41
pkg/php/services_unix.go
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
//go:build unix
|
||||
|
||||
package php
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// setSysProcAttr sets Unix-specific process attributes for clean process group handling.
|
||||
func setSysProcAttr(cmd *exec.Cmd) {
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||
Setpgid: true,
|
||||
}
|
||||
}
|
||||
|
||||
// signalProcessGroup sends a signal to the process group.
|
||||
// On Unix, this uses negative PID to signal the entire group.
|
||||
func signalProcessGroup(cmd *exec.Cmd, sig syscall.Signal) error {
|
||||
if cmd.Process == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
pgid, err := syscall.Getpgid(cmd.Process.Pid)
|
||||
if err == nil {
|
||||
return syscall.Kill(-pgid, sig)
|
||||
}
|
||||
|
||||
// Fallback to signaling just the process
|
||||
return cmd.Process.Signal(sig)
|
||||
}
|
||||
|
||||
// termSignal returns SIGTERM for Unix.
|
||||
func termSignal() syscall.Signal {
|
||||
return syscall.SIGTERM
|
||||
}
|
||||
|
||||
// killSignal returns SIGKILL for Unix.
|
||||
func killSignal() syscall.Signal {
|
||||
return syscall.SIGKILL
|
||||
}
|
||||
34
pkg/php/services_windows.go
Normal file
34
pkg/php/services_windows.go
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
//go:build windows
|
||||
|
||||
package php
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
// setSysProcAttr sets Windows-specific process attributes.
|
||||
// Windows doesn't support Setpgid, so this is a no-op.
|
||||
func setSysProcAttr(cmd *exec.Cmd) {
|
||||
// No-op on Windows - process groups work differently
|
||||
}
|
||||
|
||||
// signalProcessGroup sends a termination signal to the process.
|
||||
// On Windows, we can only signal the main process, not a group.
|
||||
func signalProcessGroup(cmd *exec.Cmd, sig os.Signal) error {
|
||||
if cmd.Process == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return cmd.Process.Signal(sig)
|
||||
}
|
||||
|
||||
// termSignal returns os.Interrupt for Windows (closest to SIGTERM).
|
||||
func termSignal() os.Signal {
|
||||
return os.Interrupt
|
||||
}
|
||||
|
||||
// killSignal returns os.Kill for Windows.
|
||||
func killSignal() os.Signal {
|
||||
return os.Kill
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue