Browse Source

safesocket: add connect retry loop to wait for tailscaled

Updates #2708

Signed-off-by: Brad Fitzpatrick <[email protected]>
Brad Fitzpatrick 4 years ago
parent
commit
21cb0b361f
4 changed files with 71 additions and 2 deletions
  1. 1 1
      cmd/tailscale/depaware.txt
  2. 1 0
      cmd/tailscaled/depaware.txt
  3. 32 1
      safesocket/safesocket.go
  4. 37 0
      safesocket/safesocket_ps.go

+ 1 - 1
cmd/tailscale/depaware.txt

@@ -4,7 +4,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
    W    github.com/alexbrainman/sspi/internal/common                 from github.com/alexbrainman/sspi/negotiate
    W 💣 github.com/alexbrainman/sspi/negotiate                       from tailscale.com/net/tshttpproxy
         github.com/kballard/go-shellquote                            from tailscale.com/cmd/tailscale/cli
-     💣 github.com/mitchellh/go-ps                                   from tailscale.com/cmd/tailscale/cli
+     💣 github.com/mitchellh/go-ps                                   from tailscale.com/cmd/tailscale/cli+
         github.com/peterbourgon/ff/v2                                from github.com/peterbourgon/ff/v2/ffcli
         github.com/peterbourgon/ff/v2/ffcli                          from tailscale.com/cmd/tailscale/cli
         github.com/tailscale/goupnp                                  from github.com/tailscale/goupnp/dcps/internetgateway2+

+ 1 - 0
cmd/tailscaled/depaware.txt

@@ -25,6 +25,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
    L 💣 github.com/mdlayher/netlink/nlenc                            from github.com/mdlayher/netlink+
    L    github.com/mdlayher/sdnotify                                 from tailscale.com/util/systemd
    L 💣 github.com/mdlayher/socket                                   from github.com/mdlayher/netlink
+     💣 github.com/mitchellh/go-ps                                   from tailscale.com/safesocket
    W    github.com/pkg/errors                                        from github.com/tailscale/certstore
    W 💣 github.com/tailscale/certstore                               from tailscale.com/control/controlclient
         github.com/tailscale/goupnp                                  from github.com/tailscale/goupnp/dcps/internetgateway2+

+ 32 - 1
safesocket/safesocket.go

@@ -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

+ 37 - 0
safesocket/safesocket_ps.go

@@ -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
+
+	}
+}