diff --git a/cmd/tailscaled/tailscaled_windows.go b/cmd/tailscaled/tailscaled_windows.go index 300e8083d..dd7b07642 100644 --- a/cmd/tailscaled/tailscaled_windows.go +++ b/cmd/tailscaled/tailscaled_windows.go @@ -25,6 +25,7 @@ import ( "encoding/json" "errors" "fmt" + "io" "log" "net/netip" "os" @@ -273,17 +274,24 @@ func beWindowsSubprocess() bool { log.Printf("Error reading environment config: %v", err) } + ctx, cancel := context.WithCancel(context.Background()) go func() { b := make([]byte, 16) for { _, err := os.Stdin.Read(b) + if err == io.EOF { + // Parent wants us to shut down gracefully. + log.Printf("subproc received EOF from stdin") + cancel() + return + } if err != nil { log.Fatalf("stdin err (parent process died): %v", err) } } }() - err := startIPNServer(context.Background(), log.Printf, logid) + err := startIPNServer(ctx, log.Printf, logid) if err != nil { log.Fatalf("ipnserver: %v", err) } @@ -365,8 +373,9 @@ func babysitProc(ctx context.Context, args []string, logf logger.Logf) { } var proc struct { - mu sync.Mutex - p *os.Process + mu sync.Mutex + p *os.Process + wStdin *os.File } done := make(chan struct{}) @@ -378,15 +387,18 @@ func babysitProc(ctx context.Context, args []string, logf logger.Logf) { case sig = <-interrupt: logf("babysitProc: got signal: %v", sig) close(done) + proc.mu.Lock() + proc.p.Signal(sig) + proc.mu.Unlock() case <-ctx.Done(): logf("babysitProc: context done") - sig = os.Kill close(done) + proc.mu.Lock() + // Closing wStdin gives the subprocess a chance to shut down cleanly, + // which is important for cleaning up DNS settings etc. + proc.wStdin.Close() + proc.mu.Unlock() } - - proc.mu.Lock() - proc.p.Signal(sig) - proc.mu.Unlock() }() bo := backoff.NewBackoff("babysitProc", logf, 30*time.Second) @@ -448,6 +460,7 @@ func babysitProc(ctx context.Context, args []string, logf logger.Logf) { } else { proc.mu.Lock() proc.p = cmd.Process + proc.wStdin = wStdin proc.mu.Unlock() err = cmd.Wait()