mirror of
https://github.com/tailscale/tailscale.git
synced 2025-08-12 13:48:01 +00:00
ipn/ipnserver: move BabysitProc to tailscaled_windows.go
It's only used by Windows. No need for it to be in ipn/ipnserver, which we're trying to trim down. Change-Id: Idf923ac8b6cdae8b5338ec26c16fb8b5ea548071 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:

committed by
Brad Fitzpatrick

parent
699b39dec1
commit
f3ba268a96
@@ -9,18 +9,14 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
@@ -455,131 +451,6 @@ func (s *Server) Run(ctx context.Context, ln net.Listener) error {
|
||||
}
|
||||
}
|
||||
|
||||
// BabysitProc runs the current executable as a child process with the
|
||||
// provided args, capturing its output, writing it to files, and
|
||||
// restarting the process on any crashes.
|
||||
//
|
||||
// It's only currently (2020-10-29) used on Windows.
|
||||
func BabysitProc(ctx context.Context, args []string, logf logger.Logf) {
|
||||
|
||||
executable, err := os.Executable()
|
||||
if err != nil {
|
||||
panic("cannot determine executable: " + err.Error())
|
||||
}
|
||||
|
||||
var proc struct {
|
||||
mu sync.Mutex
|
||||
p *os.Process
|
||||
}
|
||||
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
interrupt := make(chan os.Signal, 1)
|
||||
signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM)
|
||||
var sig os.Signal
|
||||
select {
|
||||
case sig = <-interrupt:
|
||||
logf("BabysitProc: got signal: %v", sig)
|
||||
close(done)
|
||||
case <-ctx.Done():
|
||||
logf("BabysitProc: context done")
|
||||
sig = os.Kill
|
||||
close(done)
|
||||
}
|
||||
|
||||
proc.mu.Lock()
|
||||
proc.p.Signal(sig)
|
||||
proc.mu.Unlock()
|
||||
}()
|
||||
|
||||
bo := backoff.NewBackoff("BabysitProc", logf, 30*time.Second)
|
||||
|
||||
for {
|
||||
startTime := time.Now()
|
||||
log.Printf("exec: %#v %v", executable, args)
|
||||
cmd := exec.Command(executable, args...)
|
||||
|
||||
// Create a pipe object to use as the subproc's stdin.
|
||||
// When the writer goes away, the reader gets EOF.
|
||||
// A subproc can watch its stdin and exit when it gets EOF;
|
||||
// this is a very reliable way to have a subproc die when
|
||||
// its parent (us) disappears.
|
||||
// We never need to actually write to wStdin.
|
||||
rStdin, wStdin, err := os.Pipe()
|
||||
if err != nil {
|
||||
log.Printf("os.Pipe 1: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Create a pipe object to use as the subproc's stdout/stderr.
|
||||
// We'll read from this pipe and send it to logf, line by line.
|
||||
// We can't use os.exec's io.Writer for this because it
|
||||
// doesn't care about lines, and thus ends up merging multiple
|
||||
// log lines into one or splitting one line into multiple
|
||||
// logf() calls. bufio is more appropriate.
|
||||
rStdout, wStdout, err := os.Pipe()
|
||||
if err != nil {
|
||||
log.Printf("os.Pipe 2: %v", err)
|
||||
}
|
||||
go func(r *os.File) {
|
||||
defer r.Close()
|
||||
rb := bufio.NewReader(r)
|
||||
for {
|
||||
s, err := rb.ReadString('\n')
|
||||
if s != "" {
|
||||
logf("%s", s)
|
||||
}
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
}(rStdout)
|
||||
|
||||
cmd.Stdin = rStdin
|
||||
cmd.Stdout = wStdout
|
||||
cmd.Stderr = wStdout
|
||||
err = cmd.Start()
|
||||
|
||||
// Now that the subproc is started, get rid of our copy of the
|
||||
// pipe reader. Bad things happen on Windows if more than one
|
||||
// process owns the read side of a pipe.
|
||||
rStdin.Close()
|
||||
wStdout.Close()
|
||||
|
||||
if err != nil {
|
||||
log.Printf("starting subprocess failed: %v", err)
|
||||
} else {
|
||||
proc.mu.Lock()
|
||||
proc.p = cmd.Process
|
||||
proc.mu.Unlock()
|
||||
|
||||
err = cmd.Wait()
|
||||
log.Printf("subprocess exited: %v", err)
|
||||
}
|
||||
|
||||
// If the process finishes, clean up the write side of the
|
||||
// pipe. We'll make a new one when we restart the subproc.
|
||||
wStdin.Close()
|
||||
|
||||
if os.Getenv("TS_DEBUG_RESTART_CRASHED") == "0" {
|
||||
log.Fatalf("Process ended.")
|
||||
}
|
||||
|
||||
if time.Since(startTime) < 60*time.Second {
|
||||
bo.BackOff(ctx, fmt.Errorf("subproc early exit: %v", err))
|
||||
} else {
|
||||
// Reset the timeout, since the process ran for a while.
|
||||
bo.BackOff(ctx, nil)
|
||||
}
|
||||
|
||||
select {
|
||||
case <-done:
|
||||
return
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// getEngineUntilItWorksWrapper returns a getEngine wrapper that does
|
||||
// not call getEngine concurrently and stops calling getEngine once
|
||||
// it's returned a working engine.
|
||||
|
Reference in New Issue
Block a user