diff --git a/cmd/tailscaled/tailscaled.go b/cmd/tailscaled/tailscaled.go index 45f583300..ce0f46b52 100644 --- a/cmd/tailscaled/tailscaled.go +++ b/cmd/tailscaled/tailscaled.go @@ -202,6 +202,10 @@ func main() { } } + if fd, ok := envknob.LookupInt("TS_PARENT_DEATH_FD"); ok && fd > 2 { + go dieOnPipeReadErrorOfFD(fd) + } + if printVersion { fmt.Println(version.String()) os.Exit(0) @@ -769,3 +773,14 @@ func beChild(args []string) error { } return f(args[1:]) } + +// dieOnPipeReadErrorOfFD reads from the pipe named by fd and exit the process +// when the pipe becomes readable. We use this in tests as a somewhat more +// portable mechanism for the Linux PR_SET_PDEATHSIG, which we wish existed on +// macOS. This helps us clean up straggler tailscaled processes when the parent +// test driver dies unexpectedly. +func dieOnPipeReadErrorOfFD(fd int) { + f := os.NewFile(uintptr(fd), "TS_PARENT_DEATH_FD") + f.Read(make([]byte, 1)) + os.Exit(1) +} diff --git a/tstest/integration/integration_test.go b/tstest/integration/integration_test.go index f7bde738f..5fc17302c 100644 --- a/tstest/integration/integration_test.go +++ b/tstest/integration/integration_test.go @@ -934,6 +934,15 @@ func (n *testNode) StartDaemonAsIPNGOOS(ipnGOOS string) *Daemon { cmd.Stdout = os.Stdout cmd.Stderr = io.MultiWriter(cmd.Stderr, os.Stderr) } + if runtime.GOOS != "windows" { + pr, pw, err := os.Pipe() + if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { pw.Close() }) + cmd.ExtraFiles = append(cmd.ExtraFiles, pr) + cmd.Env = append(cmd.Env, "TS_PARENT_DEATH_FD=3") + } if err := cmd.Start(); err != nil { t.Fatalf("starting tailscaled: %v", err) }