fix(webvnc): stop daemon child bridge

This commit is contained in:
Vincent Koc 2026-05-05 21:10:01 -07:00
parent fcda716aeb
commit 4060ba7afa
No known key found for this signature in database
5 changed files with 80 additions and 2 deletions

View File

@ -25,6 +25,7 @@
- Fixed brokered cloud server names so friendly-slug collisions with stale provider VMs do not block new leases.
- Fixed human WebVNC desktop launches to keep browser windows windowed by default and reserve fullscreen for explicit capture/video workflows.
- Fixed WebVNC portal status text and bridge commands so waiting/reset states explain the exact local bridge command to run.
- Fixed `crabbox webvnc --stop` so daemon shutdown terminates the active child bridge, not only the supervisor.
- Fixed portal command rows so their copy affordance copies the matching local command instead of only labelling the section.
- Fixed portal Windows target badges to show compact `win` and `win (wsl2)` labels instead of `windows / normal`.
- Fixed portal access and time columns to use compact capability icons, relative time labels, and sortable time metadata instead of wide action buttons and Zulu timestamps.

View File

@ -3,6 +3,7 @@
package cli
import (
"os"
"os/exec"
"syscall"
)
@ -10,3 +11,10 @@ import (
func configureDaemonCommand(cmd *exec.Cmd) {
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
}
func stopDaemonProcess(process *os.Process, pid int) error {
if err := syscall.Kill(-pid, syscall.SIGKILL); err != nil && err != syscall.ESRCH {
return process.Kill()
}
return nil
}

View File

@ -0,0 +1,62 @@
//go:build !windows
package cli
import (
"os"
"os/exec"
"strconv"
"strings"
"syscall"
"testing"
"time"
)
func TestStopDaemonProcessKillsProcessGroup(t *testing.T) {
pidPath := t.TempDir() + "/child.pid"
cmd := exec.Command("sh", "-c", "sleep 30 & echo $! >"+shellQuote(pidPath)+"; wait")
configureDaemonCommand(cmd)
if err := cmd.Start(); err != nil {
t.Fatal(err)
}
t.Cleanup(func() {
_ = stopDaemonProcess(cmd.Process, cmd.Process.Pid)
_, _ = cmd.Process.Wait()
})
childPID := waitForPIDFile(t, pidPath)
if err := stopDaemonProcess(cmd.Process, cmd.Process.Pid); err != nil {
t.Fatal(err)
}
_, _ = cmd.Process.Wait()
deadline := time.Now().Add(2 * time.Second)
for {
err := syscall.Kill(childPID, 0)
if err == syscall.ESRCH {
return
}
if time.Now().After(deadline) {
t.Fatalf("child process %d still alive after daemon stop; last kill probe err=%v", childPID, err)
}
time.Sleep(20 * time.Millisecond)
}
}
func waitForPIDFile(t *testing.T, path string) int {
t.Helper()
deadline := time.Now().Add(2 * time.Second)
for {
data, err := os.ReadFile(path)
if err == nil {
pid, parseErr := strconv.Atoi(strings.TrimSpace(string(data)))
if parseErr != nil {
t.Fatal(parseErr)
}
return pid
}
if time.Now().After(deadline) {
t.Fatalf("timed out waiting for pid file %s: %v", path, err)
}
time.Sleep(20 * time.Millisecond)
}
}

View File

@ -2,6 +2,13 @@
package cli
import "os/exec"
import (
"os"
"os/exec"
)
func configureDaemonCommand(_ *exec.Cmd) {}
func stopDaemonProcess(process *os.Process, _ int) error {
return process.Kill()
}

View File

@ -277,7 +277,7 @@ func (a App) stopWebVNCDaemon(leaseID string) error {
if err != nil {
return exit(5, "find WebVNC daemon pid %d: %v", pid, err)
}
if err := process.Kill(); err != nil {
if err := stopDaemonProcess(process, pid); err != nil {
return exit(5, "stop WebVNC daemon pid %d: %v", pid, err)
}
_ = os.Remove(pidPath)