diff --git a/cmd/npc/npc.go b/cmd/npc/npc.go index 67c1254..8253e6c 100644 --- a/cmd/npc/npc.go +++ b/cmd/npc/npc.go @@ -13,6 +13,7 @@ import ( "github.com/ccding/go-stun/stun" "github.com/kardianos/service" "os" + "os/exec" "runtime" "strings" "sync" @@ -37,31 +38,6 @@ var ( pprofAddr = flag.String("pprof", "", "PProf debug addr (ip:port)") ) -const systemdScript = `[Unit] -Description={{.Description}} -ConditionFileIsExecutable={{.Path|cmdEscape}} -{{range $i, $dep := .Dependencies}} -{{$dep}} {{end}} -[Service] -LimitNOFILE=65536 -StartLimitInterval=5 -StartLimitBurst=10 -ExecStart={{.Path|cmdEscape}}{{range .Arguments}} {{.|cmd}}{{end}} -{{if .ChRoot}}RootDirectory={{.ChRoot|cmd}}{{end}} -{{if .WorkingDirectory}}WorkingDirectory={{.WorkingDirectory|cmdEscape}}{{end}} -{{if .UserName}}User={{.UserName}}{{end}} -{{if .ReloadSignal}}ExecReload=/bin/kill -{{.ReloadSignal}} "$MAINPID"{{end}} -{{if .PIDFile}}PIDFile={{.PIDFile|cmd}}{{end}} -{{if and .LogOutput .HasOutputFileSupport -}} -StandardOutput=file:/var/log/{{.Name}}.out -StandardError=file:/var/log/{{.Name}}.err -{{- end}} -Restart=always -RestartSec=120 -[Install] -WantedBy=multi-user.target -` - func main() { flag.Parse() logs.Reset() @@ -91,7 +67,8 @@ func main() { svcConfig.Dependencies = []string{ "Requires=network.target", "After=network-online.target syslog.target"} - svcConfig.Option["SystemdScript"] = systemdScript + svcConfig.Option["SystemdScript"] = install.SystemdScript + svcConfig.Option["SysvScript"] = install.SysvScript } for _, v := range os.Args[1:] { switch v { @@ -137,17 +114,48 @@ func main() { } fmt.Printf("nat type: %s \npublic address: %s\n", nat.String(), host.String()) os.Exit(0) - case "install", "start", "stop", "uninstall", "restart": - if os.Args[1] == "install" { - service.Control(s, "stop") - service.Control(s, "uninstall") - install.InstallNpc() + case "start", "stop", "restart": + // support busyBox and sysV, for openWrt + if service.Platform() == "unix-systemv" { + logs.Info("unix-systemv service") + cmd := exec.Command("/etc/init.d/"+svcConfig.Name, os.Args[1]) + err := cmd.Run() + if err != nil { + logs.Error(err) + } + return } err := service.Control(s, os.Args[1]) if err != nil { logs.Error("Valid actions: %q\n%s", service.ControlAction, err.Error()) } return + case "install": + service.Control(s, "stop") + service.Control(s, "uninstall") + install.InstallNpc() + err := service.Control(s, os.Args[1]) + if err != nil { + logs.Error("Valid actions: %q\n%s", service.ControlAction, err.Error()) + } + if service.Platform() == "unix-systemv" { + logs.Info("unix-systemv service") + confPath := "/etc/init.d/" + svcConfig.Name + os.Symlink(confPath, "/etc/rc.d/S90"+svcConfig.Name) + os.Symlink(confPath, "/etc/rc.d/K02"+svcConfig.Name) + } + return + case "uninstall": + err := service.Control(s, os.Args[1]) + if err != nil { + logs.Error("Valid actions: %q\n%s", service.ControlAction, err.Error()) + } + if service.Platform() == "unix-systemv" { + logs.Info("unix-systemv service") + os.Remove("/etc/rc.d/S90" + svcConfig.Name) + os.Remove("/etc/rc.d/K02" + svcConfig.Name) + } + return } } s.Run() diff --git a/cmd/nps/nps.go b/cmd/nps/nps.go index 7746db7..0f39e76 100644 --- a/cmd/nps/nps.go +++ b/cmd/nps/nps.go @@ -12,6 +12,7 @@ import ( "flag" "log" "os" + "os/exec" "path/filepath" "runtime" "strings" @@ -29,31 +30,6 @@ var ( level string ) -const systemdScript = `[Unit] -Description={{.Description}} -ConditionFileIsExecutable={{.Path|cmdEscape}} -{{range $i, $dep := .Dependencies}} -{{$dep}} {{end}} -[Service] -LimitNOFILE=65536 -StartLimitInterval=5 -StartLimitBurst=10 -ExecStart={{.Path|cmdEscape}}{{range .Arguments}} {{.|cmd}}{{end}} -{{if .ChRoot}}RootDirectory={{.ChRoot|cmd}}{{end}} -{{if .WorkingDirectory}}WorkingDirectory={{.WorkingDirectory|cmdEscape}}{{end}} -{{if .UserName}}User={{.UserName}}{{end}} -{{if .ReloadSignal}}ExecReload=/bin/kill -{{.ReloadSignal}} "$MAINPID"{{end}} -{{if .PIDFile}}PIDFile={{.PIDFile|cmd}}{{end}} -{{if and .LogOutput .HasOutputFileSupport -}} -StandardOutput=file:/var/log/{{.Name}}.out -StandardError=file:/var/log/{{.Name}}.err -{{- end}} -Restart=always -RestartSec=120 -[Install] -WantedBy=multi-user.target -` - func main() { flag.Parse() // init log @@ -92,7 +68,8 @@ func main() { svcConfig.Dependencies = []string{ "Requires=network.target", "After=network-online.target syslog.target"} - svcConfig.Option["SystemdScript"] = systemdScript + svcConfig.Option["SystemdScript"] = install.SystemdScript + svcConfig.Option["SysvScript"] = install.SysvScript } prg := &nps{} prg.exit = make(chan struct{}) @@ -127,13 +104,39 @@ func main() { if err != nil { logs.Error("Valid actions: %q\n%s", service.ControlAction, err.Error()) } + if service.Platform() == "unix-systemv" { + logs.Info("unix-systemv service") + confPath := "/etc/init.d/" + svcConfig.Name + os.Symlink(confPath, "/etc/rc.d/S90"+svcConfig.Name) + os.Symlink(confPath, "/etc/rc.d/K02"+svcConfig.Name) + } return - case "start", "restart", "stop", "uninstall": + case "start", "restart", "stop": + if service.Platform() == "unix-systemv" { + logs.Info("unix-systemv service") + cmd := exec.Command("/etc/init.d/"+svcConfig.Name, os.Args[1]) + err := cmd.Run() + if err != nil { + logs.Error(err) + } + return + } err := service.Control(s, os.Args[1]) if err != nil { logs.Error("Valid actions: %q\n%s", service.ControlAction, err.Error()) } return + case "uninstall": + err := service.Control(s, os.Args[1]) + if err != nil { + logs.Error("Valid actions: %q\n%s", service.ControlAction, err.Error()) + } + if service.Platform() == "unix-systemv" { + logs.Info("unix-systemv service") + os.Remove("/etc/rc.d/S90" + svcConfig.Name) + os.Remove("/etc/rc.d/K02" + svcConfig.Name) + } + return case "update": install.UpdateNps() return diff --git a/lib/install/install.go b/lib/install/install.go index 8973d0d..01d484a 100644 --- a/lib/install/install.go +++ b/lib/install/install.go @@ -16,6 +16,124 @@ import ( "strings" ) +// Keep it in sync with the template from service_sysv_linux.go file +// Use "ps | grep -v grep | grep $(get_pid)" because "ps PID" may not work on OpenWrt +const SysvScript = `#!/bin/sh +# For RedHat and cousins: +# chkconfig: - 99 01 +# description: {{.Description}} +# processname: {{.Path}} +### BEGIN INIT INFO +# Provides: {{.Path}} +# Required-Start: +# Required-Stop: +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: {{.DisplayName}} +# Description: {{.Description}} +### END INIT INFO +cmd="{{.Path}}{{range .Arguments}} {{.|cmd}}{{end}}" +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 | grep -v grep | grep $(get_pid) > /dev/null 2>&1 +} +case "$1" in + start) + if is_running; then + echo "Already started" + else + echo "Starting $name" + {{if .WorkingDirectory}}cd '{{.WorkingDirectory}}'{{end}} + $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 $(seq 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 +` + +const SystemdScript = `[Unit] +Description={{.Description}} +ConditionFileIsExecutable={{.Path|cmdEscape}} +{{range $i, $dep := .Dependencies}} +{{$dep}} {{end}} +[Service] +LimitNOFILE=65536 +StartLimitInterval=5 +StartLimitBurst=10 +ExecStart={{.Path|cmdEscape}}{{range .Arguments}} {{.|cmd}}{{end}} +{{if .ChRoot}}RootDirectory={{.ChRoot|cmd}}{{end}} +{{if .WorkingDirectory}}WorkingDirectory={{.WorkingDirectory|cmdEscape}}{{end}} +{{if .UserName}}User={{.UserName}}{{end}} +{{if .ReloadSignal}}ExecReload=/bin/kill -{{.ReloadSignal}} "$MAINPID"{{end}} +{{if .PIDFile}}PIDFile={{.PIDFile|cmd}}{{end}} +{{if and .LogOutput .HasOutputFileSupport -}} +StandardOutput=file:/var/log/{{.Name}}.out +StandardError=file:/var/log/{{.Name}}.err +{{- end}} +Restart=always +RestartSec=120 +[Install] +WantedBy=multi-user.target +` + func UpdateNps() { destPath := downloadLatest("server") //复制文件到对应目录 @@ -36,7 +154,7 @@ type release struct { func downloadLatest(bin string) string { // get version - data, err := http.Get("https://api.github.com/repos/cnlh/nps/releases/latest") + data, err := http.Get("https://api.github.com/repos/ehang-io/nps/releases/latest") if err != nil { log.Fatal(err.Error()) }