safesocket: add connect retry loop to wait for tailscaled

Updates #2708

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick
2021-08-31 14:36:10 -07:00
committed by Brad Fitzpatrick
parent a59b389a6a
commit 21cb0b361f
4 changed files with 71 additions and 2 deletions

View File

@@ -10,6 +10,7 @@ import (
"errors"
"net"
"runtime"
"time"
)
type closeable interface {
@@ -29,9 +30,39 @@ func ConnCloseWrite(c net.Conn) error {
return c.(closeable).CloseWrite()
}
var processStartTime = time.Now()
var tailscaledProcExists = func() bool { return false } // set by safesocket_ps.go
// tailscaledStillStarting reports whether tailscaled is probably
// still starting up. That is, it reports whether the caller should
// keep retrying to connect.
func tailscaledStillStarting() bool {
d := time.Since(processStartTime)
if d < 2*time.Second {
// Without even checking the process table, assume
// that for the first two seconds that tailscaled is
// probably still starting. That is, assume they're
// running "tailscaled & tailscale up ...." and make
// the tailscale client block for a bit for tailscaled
// to start accepting on the socket.
return true
}
if d > 5*time.Second {
return false
}
return tailscaledProcExists()
}
// Connect connects to either path (on Unix) or the provided localhost port (on Windows).
func Connect(path string, port uint16) (net.Conn, error) {
return connect(path, port)
for {
c, err := connect(path, port)
if err != nil && tailscaledStillStarting() {
time.Sleep(250 * time.Millisecond)
continue
}
return c, err
}
}
// Listen returns a listener either on Unix socket path (on Unix), or

View File

@@ -0,0 +1,37 @@
// Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build linux || windows || darwin || freebsd
// +build linux windows darwin freebsd
package safesocket
import (
"strings"
ps "github.com/mitchellh/go-ps"
)
func init() {
tailscaledProcExists = func() bool {
procs, err := ps.Processes()
if err != nil {
return false
}
for _, proc := range procs {
name := proc.Executable()
const tailscaled = "tailscaled"
if len(name) < len(tailscaled) {
continue
}
// Do case insensitive comparison for Windows,
// notably, and ignore any ".exe" suffix.
if strings.EqualFold(name[:len(tailscaled)], tailscaled) {
return true
}
}
return false
}
}