Skip to content

Commit ec07f7c

Browse files
committed
Fix cross-platform build: separate Unix and Windows process control
1 parent 7e1b1d8 commit ec07f7c

3 files changed

Lines changed: 81 additions & 14 deletions

File tree

pkg/process/manager.go

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import (
1010
"sort"
1111
"strconv"
1212
"strings"
13-
"syscall"
1413
"time"
1514

1615
"github.com/devports/devpt/pkg/models"
@@ -60,10 +59,8 @@ func (m *Manager) Start(service *models.ManagedService) (int, error) {
6059
cmd := exec.Command(argv[0], argv[1:]...)
6160
cmd.Dir = service.CWD
6261

63-
// Set up process group to manage all child processes
64-
cmd.SysProcAttr = &syscall.SysProcAttr{
65-
Setpgid: true,
66-
}
62+
// Set up process group to manage all child processes (platform-specific)
63+
setProcessGroup(cmd)
6764

6865
// Redirect output to log file
6966
cmd.Stdout = logFile
@@ -88,34 +85,33 @@ func (m *Manager) Stop(pid int, timeout time.Duration) error {
8885

8986
// First attempt graceful termination. For non-child processes we cannot use Wait(),
9087
// so we send signals and poll for liveness.
91-
if err := syscall.Kill(-pid, syscall.SIGTERM); err != nil {
92-
if err := syscall.Kill(pid, syscall.SIGTERM); err != nil {
93-
return fmt.Errorf("failed to send SIGTERM: %w", err)
88+
if err := terminateProcess(pid); err != nil {
89+
if err := terminateProcessFallback(pid); err != nil {
90+
return fmt.Errorf("failed to send termination signal: %w", err)
9491
}
9592
}
9693

9794
deadline := time.Now().Add(timeout)
9895
for time.Now().Before(deadline) {
99-
if !m.isAlive(pid) {
96+
if !isProcessAlive(pid) {
10097
return nil
10198
}
10299
time.Sleep(120 * time.Millisecond)
103100
}
104101

105102
// Escalate to hard kill.
106-
if err := syscall.Kill(-pid, syscall.SIGKILL); err != nil {
107-
_ = syscall.Kill(pid, syscall.SIGKILL)
103+
if err := killProcess(pid); err != nil {
104+
_ = killProcessFallback(pid)
108105
}
109106
time.Sleep(200 * time.Millisecond)
110-
if m.isAlive(pid) {
107+
if isProcessAlive(pid) {
111108
return ErrNeedSudo
112109
}
113110
return nil
114111
}
115112

116113
func (m *Manager) isAlive(pid int) bool {
117-
err := syscall.Kill(pid, syscall.Signal(0))
118-
if err != nil {
114+
if !isProcessAlive(pid) {
119115
return false
120116
}
121117
if st, stateErr := m.processState(pid); stateErr == nil {

pkg/process/proc_unix.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//go:build !windows
2+
3+
package process
4+
5+
import (
6+
"os/exec"
7+
"syscall"
8+
)
9+
10+
func setProcessGroup(cmd *exec.Cmd) {
11+
cmd.SysProcAttr = &syscall.SysProcAttr{
12+
Setpgid: true,
13+
}
14+
}
15+
16+
func terminateProcess(pid int) error {
17+
return syscall.Kill(-pid, syscall.SIGTERM)
18+
}
19+
20+
func terminateProcessFallback(pid int) error {
21+
return syscall.Kill(pid, syscall.SIGTERM)
22+
}
23+
24+
func killProcess(pid int) error {
25+
return syscall.Kill(-pid, syscall.SIGKILL)
26+
}
27+
28+
func killProcessFallback(pid int) error {
29+
return syscall.Kill(pid, syscall.SIGKILL)
30+
}
31+
32+
func isProcessAlive(pid int) bool {
33+
return syscall.Kill(pid, syscall.Signal(0)) == nil
34+
}

pkg/process/proc_windows.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//go:build windows
2+
3+
package process
4+
5+
import (
6+
"os/exec"
7+
"strconv"
8+
)
9+
10+
func setProcessGroup(cmd *exec.Cmd) {
11+
// Windows: no special process group setup needed for basic use
12+
// The process will be managed by its PID
13+
}
14+
15+
func terminateProcess(pid int) error {
16+
return terminateProcessFallback(pid)
17+
}
18+
19+
func terminateProcessFallback(pid int) error {
20+
// On Windows, use taskkill for graceful termination
21+
return exec.Command("taskkill", "/PID", strconv.Itoa(pid)).Run()
22+
}
23+
24+
func killProcess(pid int) error {
25+
return killProcessFallback(pid)
26+
}
27+
28+
func killProcessFallback(pid int) error {
29+
// On Windows, use taskkill /F for forceful termination
30+
return exec.Command("taskkill", "/F", "/PID", strconv.Itoa(pid)).Run()
31+
}
32+
33+
func isProcessAlive(pid int) bool {
34+
// Check if process exists using tasklist
35+
err := exec.Command("tasklist", "/FI", "PID eq "+strconv.Itoa(pid)).Run()
36+
return err == nil
37+
}

0 commit comments

Comments
 (0)