159 lines
3.6 KiB
Go
159 lines
3.6 KiB
Go
// 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 go1.18
|
|
// +build go1.18
|
|
|
|
package main
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
)
|
|
|
|
func init() {
|
|
installSystemDaemon = installSystemDaemonDarwin
|
|
uninstallSystemDaemon = uninstallSystemDaemonDarwin
|
|
}
|
|
|
|
// darwinLaunchdPlist is the launchd.plist that's written to
|
|
// /Library/LaunchDaemons/com.tailscale.tailscaled.plist or (in the
|
|
// future) a user-specific location.
|
|
//
|
|
// See man launchd.plist.
|
|
const darwinLaunchdPlist = `
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
<plist version="1.0">
|
|
<dict>
|
|
|
|
<key>Label</key>
|
|
<string>com.tailscale.tailscaled</string>
|
|
|
|
<key>ProgramArguments</key>
|
|
<array>
|
|
<string>/usr/local/bin/tailscaled</string>
|
|
</array>
|
|
|
|
<key>RunAtLoad</key>
|
|
<true/>
|
|
|
|
</dict>
|
|
</plist>
|
|
`
|
|
|
|
const sysPlist = "/Library/LaunchDaemons/com.tailscale.tailscaled.plist"
|
|
const targetBin = "/usr/local/bin/tailscaled"
|
|
const service = "com.tailscale.tailscaled"
|
|
|
|
func uninstallSystemDaemonDarwin(args []string) (ret error) {
|
|
if len(args) > 0 {
|
|
return errors.New("uninstall subcommand takes no arguments")
|
|
}
|
|
|
|
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
|
|
|
|
if running {
|
|
out, err := exec.Command("launchctl", "stop", "com.tailscale.tailscaled").CombinedOutput()
|
|
if err != nil {
|
|
fmt.Printf("launchctl stop com.tailscale.tailscaled: %v, %s\n", err, out)
|
|
ret = err
|
|
}
|
|
out, err = exec.Command("launchctl", "unload", sysPlist).CombinedOutput()
|
|
if err != nil {
|
|
fmt.Printf("launchctl unload %s: %v, %s\n", sysPlist, err, out)
|
|
if ret == nil {
|
|
ret = err
|
|
}
|
|
}
|
|
}
|
|
|
|
if err := os.Remove(sysPlist); err != nil {
|
|
if os.IsNotExist(err) {
|
|
err = nil
|
|
}
|
|
if ret == nil {
|
|
ret = err
|
|
}
|
|
}
|
|
if err := os.Remove(targetBin); err != nil {
|
|
if os.IsNotExist(err) {
|
|
err = nil
|
|
}
|
|
if ret == nil {
|
|
ret = err
|
|
}
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func installSystemDaemonDarwin(args []string) (err error) {
|
|
if len(args) > 0 {
|
|
return errors.New("install subcommand takes no arguments")
|
|
}
|
|
defer func() {
|
|
if err != nil && os.Getuid() != 0 {
|
|
err = fmt.Errorf("%w; try running tailscaled with sudo", err)
|
|
}
|
|
}()
|
|
|
|
// Best effort:
|
|
uninstallSystemDaemonDarwin(nil)
|
|
|
|
// Copy ourselves to /usr/local/bin/tailscaled.
|
|
if err := os.MkdirAll(filepath.Dir(targetBin), 0755); err != nil {
|
|
return err
|
|
}
|
|
exe, err := os.Executable()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to find our own executable path: %w", err)
|
|
}
|
|
tmpBin := targetBin + ".tmp"
|
|
f, err := os.Create(tmpBin)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
self, err := os.Open(exe)
|
|
if err != nil {
|
|
f.Close()
|
|
return err
|
|
}
|
|
_, err = io.Copy(f, self)
|
|
self.Close()
|
|
if err != nil {
|
|
f.Close()
|
|
return err
|
|
}
|
|
if err := f.Close(); err != nil {
|
|
return err
|
|
}
|
|
if err := os.Chmod(tmpBin, 0755); err != nil {
|
|
return err
|
|
}
|
|
if err := os.Rename(tmpBin, targetBin); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := ioutil.WriteFile(sysPlist, []byte(darwinLaunchdPlist), 0700); err != nil {
|
|
return err
|
|
}
|
|
|
|
if out, err := exec.Command("launchctl", "load", sysPlist).CombinedOutput(); err != nil {
|
|
return fmt.Errorf("error running launchctl load %s: %v, %s", sysPlist, err, out)
|
|
}
|
|
|
|
if out, err := exec.Command("launchctl", "start", service).CombinedOutput(); err != nil {
|
|
return fmt.Errorf("error running launchctl start %s: %v, %s", service, err, out)
|
|
}
|
|
|
|
return nil
|
|
}
|