mirror of
https://github.com/tailscale/tailscale.git
synced 2025-01-07 08:07:42 +00:00
ipn/ipnserver: on Windows in unattended mode, wait for Engine forever
Updates #1187
This commit is contained in:
parent
0dde8fa0a8
commit
c3c59445ff
@ -537,6 +537,46 @@ func Run(ctx context.Context, logf logger.Logf, logid string, getEngine func() (
|
|||||||
eng, err := getEngine()
|
eng, err := getEngine()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logf("ipnserver: initial getEngine call: %v", err)
|
logf("ipnserver: initial getEngine call: %v", err)
|
||||||
|
|
||||||
|
// Issue 1187: on Windows, in unattended mode,
|
||||||
|
// sometimes we try 5 times and fail to create the
|
||||||
|
// engine before the system's ready. Hack until the
|
||||||
|
// bug if fixed properly: if we're running in
|
||||||
|
// unattended mode on Windows, keep trying forever,
|
||||||
|
// waiting for the machine to be ready (networking to
|
||||||
|
// come up?) and then dial our own safesocket TCP
|
||||||
|
// listener to wake up the usual mechanism that lets
|
||||||
|
// us surface getEngine errors to UI clients. (We
|
||||||
|
// don't want to just call getEngine in a loop without
|
||||||
|
// the listener.Accept, as we do want to handle client
|
||||||
|
// connections so we can tell them about errors)
|
||||||
|
|
||||||
|
bootRaceWaitForEngine, bootRaceWaitForEngineCancel := context.WithTimeout(context.Background(), time.Minute)
|
||||||
|
if runtime.GOOS == "windows" && opts.AutostartStateKey != "" {
|
||||||
|
logf("ipnserver: in unattended mode, waiting for engine availability")
|
||||||
|
getEngine = getEngineUntilItWorksWrapper(getEngine)
|
||||||
|
// Wait for it to be ready.
|
||||||
|
go func() {
|
||||||
|
defer bootRaceWaitForEngineCancel()
|
||||||
|
t0 := time.Now()
|
||||||
|
for {
|
||||||
|
time.Sleep(10 * time.Second)
|
||||||
|
if _, err := getEngine(); err != nil {
|
||||||
|
logf("ipnserver: unattended mode engine load: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
c, err := net.Dial("tcp", listen.Addr().String())
|
||||||
|
logf("ipnserver: engine created after %v; waking up Accept: Dial error: %v", time.Since(t0).Round(time.Second), err)
|
||||||
|
if err == nil {
|
||||||
|
c.Close()
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
} else {
|
||||||
|
bootRaceWaitForEngineCancel()
|
||||||
|
}
|
||||||
|
|
||||||
for i := 1; ctx.Err() == nil; i++ {
|
for i := 1; ctx.Err() == nil; i++ {
|
||||||
c, err := listen.Accept()
|
c, err := listen.Accept()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -544,6 +584,7 @@ func Run(ctx context.Context, logf logger.Logf, logid string, getEngine func() (
|
|||||||
bo.BackOff(ctx, err)
|
bo.BackOff(ctx, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
<-bootRaceWaitForEngine.Done()
|
||||||
logf("ipnserver: try%d: trying getEngine again...", i)
|
logf("ipnserver: try%d: trying getEngine again...", i)
|
||||||
eng, err = getEngine()
|
eng, err = getEngine()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@ -756,6 +797,27 @@ func FixedEngine(eng wgengine.Engine) func() (wgengine.Engine, error) {
|
|||||||
return func() (wgengine.Engine, error) { return eng, nil }
|
return func() (wgengine.Engine, error) { return eng, nil }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getEngineUntilItWorksWrapper returns a getEngine wrapper that does
|
||||||
|
// not call getEngine concurrently and stops calling getEngine once
|
||||||
|
// it's returned a working engine.
|
||||||
|
func getEngineUntilItWorksWrapper(getEngine func() (wgengine.Engine, error)) func() (wgengine.Engine, error) {
|
||||||
|
var mu sync.Mutex
|
||||||
|
var engGood wgengine.Engine
|
||||||
|
return func() (wgengine.Engine, error) {
|
||||||
|
mu.Lock()
|
||||||
|
defer mu.Unlock()
|
||||||
|
if engGood != nil {
|
||||||
|
return engGood, nil
|
||||||
|
}
|
||||||
|
e, err := getEngine()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
engGood = e
|
||||||
|
return e, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type dummyAddr string
|
type dummyAddr string
|
||||||
type oneConnListener struct {
|
type oneConnListener struct {
|
||||||
conn net.Conn
|
conn net.Conn
|
||||||
|
Loading…
x
Reference in New Issue
Block a user