cloudflared-mirror/cmd/cloudflare-warp/linux_service.go

265 lines
5.9 KiB
Go

// +build linux
package main
import (
"fmt"
"os"
cli "gopkg.in/urfave/cli.v2"
)
func runApp(app *cli.App) {
app.Commands = append(app.Commands, &cli.Command{
Name: "service",
Usage: "Manages the Cloudflare Warp system service",
Subcommands: []*cli.Command{
&cli.Command{
Name: "install",
Usage: "Install Cloudflare Warp as a system service",
Action: installLinuxService,
},
&cli.Command{
Name: "uninstall",
Usage: "Uninstall the Cloudflare Warp service",
Action: uninstallLinuxService,
},
},
})
app.Run(os.Args)
}
var systemdTemplates = []ServiceTemplate{
{
Path: "/etc/systemd/system/cloudflare-warp.service",
Content: `[Unit]
Description=Cloudflare Warp
After=network.target
[Service]
TimeoutStartSec=0
Type=notify
ExecStart={{ .Path }} --config /etc/cloudflare-warp.yml --autoupdate 0s
User=nobody
[Install]
WantedBy=multi-user.target
`,
},
{
Path: "/etc/systemd/system/cloudflare-warp-update.service",
Content: `[Unit]
Description=Update Cloudflare Warp
After=network.target
[Service]
ExecStart=/bin/bash -c '{{ .Path }} update; code=$?; if [ $code -eq 64 ]; then systemctl restart cloudflare-warp; exit 0; fi; exit $code'
`,
},
{
Path: "/etc/systemd/system/cloudflare-warp-update.timer",
Content: `[Unit]
Description=Update Cloudflare Warp
[Timer]
OnUnitActiveSec=1d
[Install]
WantedBy=timers.target
`,
},
}
var sysvTemplate = ServiceTemplate{
Path: "/etc/init.d/cloudflare-warp",
FileMode: 0755,
Content: `# For RedHat and cousins:
# chkconfig: 2345 99 01
# description: Cloudflare Warp agent
# processname: {{.Path}}
### BEGIN INIT INFO
# Provides: {{.Path}}
# Required-Start:
# Required-Stop:
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Cloudflare Warp
# Description: Cloudflare Warp agent
### END INIT INFO
cmd="{{.Path}} --config /etc/cloudflare-warp.yml --pidfile /var/run/$name.pid"
name=$(basename $(readlink -f $0))
pid_file="/var/run/$name.pid"
stdout_log="/var/log/$name.log"
stderr_log="/var/log/$name.err"
[ -e /etc/sysconfig/$name ] && . /etc/sysconfig/$name
get_pid() {
cat "$pid_file"
}
is_running() {
[ -f "$pid_file" ] && ps $(get_pid) > /dev/null 2>&1
}
case "$1" in
start)
if is_running; then
echo "Already started"
else
echo "Starting $name"
$cmd >> "$stdout_log" 2>> "$stderr_log" &
echo $! > "$pid_file"
if ! is_running; then
echo "Unable to start, see $stdout_log and $stderr_log"
exit 1
fi
fi
;;
stop)
if is_running; then
echo -n "Stopping $name.."
kill $(get_pid)
for i in {1..10}
do
if ! is_running; then
break
fi
echo -n "."
sleep 1
done
echo
if is_running; then
echo "Not stopped; may still be shutting down or shutdown may have failed"
exit 1
else
echo "Stopped"
if [ -f "$pid_file" ]; then
rm "$pid_file"
fi
fi
else
echo "Not running"
fi
;;
restart)
$0 stop
if is_running; then
echo "Unable to stop, will not attempt to start"
exit 1
fi
$0 start
;;
status)
if is_running; then
echo "Running"
else
echo "Stopped"
exit 1
fi
;;
*)
echo "Usage: $0 {start|stop|restart|status}"
exit 1
;;
esac
exit 0
`,
}
func isSystemd() bool {
if _, err := os.Stat("/run/systemd/system"); err == nil {
return true
}
return false
}
func installLinuxService(c *cli.Context) error {
etPath, err := os.Executable()
if err != nil {
return fmt.Errorf("error determining executable path: %v", err)
}
templateArgs := ServiceTemplateArgs{Path: etPath}
switch {
case isSystemd():
return installSystemd(&templateArgs)
default:
return installSysv(&templateArgs)
}
}
func installSystemd(templateArgs *ServiceTemplateArgs) error {
for _, serviceTemplate := range systemdTemplates {
err := serviceTemplate.Generate(templateArgs)
if err != nil {
return err
}
}
if err := runCommand("systemctl", "enable", "cloudflare-warp.service"); err != nil {
return err
}
if err := runCommand("systemctl", "start", "cloudflare-warp-update.timer"); err != nil {
return err
}
return runCommand("systemctl", "daemon-reload")
}
func installSysv(templateArgs *ServiceTemplateArgs) error {
confPath, err := sysvTemplate.ResolvePath()
if err != nil {
return err
}
if err := sysvTemplate.Generate(templateArgs); err != nil {
return err
}
for _, i := range [...]string{"2", "3", "4", "5"} {
if err := os.Symlink(confPath, "/etc/rc"+i+".d/S50et"); err != nil {
continue
}
}
for _, i := range [...]string{"0", "1", "6"} {
if err := os.Symlink(confPath, "/etc/rc"+i+".d/K02et"); err != nil {
continue
}
}
return nil
}
func uninstallLinuxService(c *cli.Context) error {
switch {
case isSystemd():
return uninstallSystemd()
default:
return uninstallSysv()
}
}
func uninstallSystemd() error {
if err := runCommand("systemctl", "disable", "cloudflare-warp.service"); err != nil {
return err
}
if err := runCommand("systemctl", "stop", "cloudflare-warp-update.timer"); err != nil {
return err
}
for _, serviceTemplate := range systemdTemplates {
if err := serviceTemplate.Remove(); err != nil {
return err
}
}
return nil
}
func uninstallSysv() error {
if err := sysvTemplate.Remove(); err != nil {
return err
}
for _, i := range [...]string{"2", "3", "4", "5"} {
if err := os.Remove("/etc/rc" + i + ".d/S50et"); err != nil {
continue
}
}
for _, i := range [...]string{"0", "1", "6"} {
if err := os.Remove("/etc/rc" + i + ".d/K02et"); err != nil {
continue
}
}
return nil
}