diff --git a/safesocket/unixsocket.go b/safesocket/unixsocket.go index fd2a58852..8128bef57 100644 --- a/safesocket/unixsocket.go +++ b/safesocket/unixsocket.go @@ -13,6 +13,7 @@ "log" "net" "os" + "os/exec" "path/filepath" "runtime" "strconv" @@ -54,6 +55,9 @@ func listen(path string, port uint16) (ln net.Listener, _ uint16, err error) { c, err := net.Dial("unix", path) if err == nil { c.Close() + if tailscaledRunningUnderLaunchd() { + return nil, 0, fmt.Errorf("%v: address already in use; tailscaled already running under launchd (to stop, run: $ sudo launchctl stop com.tailscale.tailscaled)", path) + } return nil, 0, fmt.Errorf("%v: address already in use", path) } _ = os.Remove(path) @@ -86,6 +90,16 @@ func listen(path string, port uint16) (ln net.Listener, _ uint16, err error) { return pipe, 0, err } +func tailscaledRunningUnderLaunchd() bool { + if runtime.GOOS != "darwin" { + return false + } + plist, err := exec.Command("launchctl", "list", "com.tailscale.tailscaled").Output() + _ = plist // parse it? https://github.com/DHowett/go-plist if we need something. + running := err == nil + return running +} + // socketPermissionsForOS returns the permissions to use for the // tailscaled.sock. func socketPermissionsForOS() os.FileMode { diff --git a/wgengine/userspace.go b/wgengine/userspace.go index 5793232b4..f3ce131c9 100644 --- a/wgengine/userspace.go +++ b/wgengine/userspace.go @@ -171,16 +171,16 @@ func NewFakeUserspaceEngine(logf logger.Logf, listenPort uint16, impl FakeImplFu // NewUserspaceEngine creates the named tun device and returns a // Tailscale Engine running on it. -func NewUserspaceEngine(logf logger.Logf, tunname string, listenPort uint16) (Engine, error) { - if tunname == "" { +func NewUserspaceEngine(logf logger.Logf, tunName string, listenPort uint16) (Engine, error) { + if tunName == "" { return nil, fmt.Errorf("--tun name must not be blank") } - logf("Starting userspace wireguard engine with tun device %q", tunname) + logf("Starting userspace wireguard engine with tun device %q", tunName) - tun, err := tun.CreateTUN(tunname, minimalMTU) + tun, err := tun.CreateTUN(tunName, minimalMTU) if err != nil { - diagnoseTUNFailure(logf) + diagnoseTUNFailure(tunName, logf) logf("CreateTUN: %v", err) return nil, err } @@ -1363,16 +1363,27 @@ func (e *userspaceEngine) Ping(ip netaddr.IP, cb func(*ipnstate.PingResult)) { // the system and log some diagnostic info that might help debug why // TUN failed. Because TUN's already failed and things the program's // about to end, we might as well log a lot. -func diagnoseTUNFailure(logf logger.Logf) { +func diagnoseTUNFailure(tunName string, logf logger.Logf) { switch runtime.GOOS { case "linux": - diagnoseLinuxTUNFailure(logf) + diagnoseLinuxTUNFailure(tunName, logf) + case "darwin": + diagnoseDarwinTUNFailure(tunName, logf) default: logf("no TUN failure diagnostics for OS %q", runtime.GOOS) } } -func diagnoseLinuxTUNFailure(logf logger.Logf) { +func diagnoseDarwinTUNFailure(tunName string, logf logger.Logf) { + if os.Getuid() != 0 { + logf("failed to create TUN device as non-root user; use 'sudo tailscaled', or run under launchd with 'sudo tailscaled install-system-daemon'") + } + if tunName != "utun" { + logf("failed to create TUN device %q; try using tun device \"utun\" instead for automatic selection", tunName) + } +} + +func diagnoseLinuxTUNFailure(tunName string, logf logger.Logf) { kernel, err := exec.Command("uname", "-r").Output() kernel = bytes.TrimSpace(kernel) if err != nil {