2023-12-10 17:56:48 +08:00
|
|
|
package godaemon
|
|
|
|
|
|
|
|
import (
|
|
|
|
"flag"
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"os/exec"
|
|
|
|
"os/signal"
|
|
|
|
"path/filepath"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"syscall"
|
|
|
|
|
|
|
|
"git.pyer.club/kingecg/gologger"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
daemon_env_key = "_go_daemon"
|
|
|
|
daemon_process = "g_daemon"
|
|
|
|
daemon_task = "g_dtask"
|
|
|
|
daemon_taskargs = "g_args"
|
|
|
|
)
|
|
|
|
|
|
|
|
type GoDaemon struct {
|
2023-12-10 18:36:16 +08:00
|
|
|
pidFile string
|
|
|
|
flag *string
|
|
|
|
sigChan chan os.Signal
|
|
|
|
state string
|
2024-10-01 18:51:49 +08:00
|
|
|
*gologger.Logger
|
2023-12-10 18:36:16 +08:00
|
|
|
Running *exec.Cmd
|
|
|
|
|
|
|
|
StartFn func(*GoDaemon)
|
|
|
|
StopFn func(*GoDaemon)
|
2023-12-10 17:56:48 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func (g *GoDaemon) GetPid() int {
|
|
|
|
pids, ferr := os.ReadFile(g.pidFile)
|
|
|
|
pid, err := strconv.Atoi(string(pids))
|
|
|
|
if err != nil || ferr != nil {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
return pid
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *GoDaemon) Start() {
|
|
|
|
if g.flag == nil {
|
|
|
|
g.flag = flag.String("s", "", "send signal to daemon. support: reload and quit")
|
|
|
|
}
|
|
|
|
|
|
|
|
if IsMaster() {
|
|
|
|
flag.Parse()
|
|
|
|
if *g.flag == "" {
|
|
|
|
g.startDaemon()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var sig syscall.Signal
|
|
|
|
if *g.flag == "reload" {
|
|
|
|
sig = syscall.SIGHUP
|
|
|
|
} else if *g.flag == "quit" {
|
|
|
|
sig = syscall.SIGTERM
|
|
|
|
} else {
|
|
|
|
fmt.Println("Not supported signal")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
p := g.getDaemonProcess()
|
|
|
|
if p == nil {
|
|
|
|
fmt.Println("Daemon process not found")
|
|
|
|
return
|
|
|
|
}
|
2023-12-10 18:36:16 +08:00
|
|
|
g.Debug("Send signal:", p.Pid, sig)
|
2023-12-10 17:56:48 +08:00
|
|
|
p.Signal(sig)
|
|
|
|
} else if IsDaemon() {
|
|
|
|
pid := os.Getpid()
|
|
|
|
|
|
|
|
os.WriteFile(g.pidFile, []byte(strconv.Itoa(pid)), 0644)
|
|
|
|
g.sigChan = make(chan os.Signal)
|
2023-12-10 18:36:16 +08:00
|
|
|
|
2023-12-10 17:56:48 +08:00
|
|
|
signal.Notify(g.sigChan, syscall.SIGTERM, syscall.SIGHUP)
|
|
|
|
go g.serveSignal()
|
|
|
|
|
|
|
|
for {
|
2023-12-10 18:36:16 +08:00
|
|
|
|
|
|
|
g.Debug("Starting new task")
|
2023-12-10 17:56:48 +08:00
|
|
|
g.Running = g.startTask()
|
|
|
|
g.state = "running"
|
|
|
|
g.Running.Process.Wait()
|
|
|
|
if g.state == "stopped" {
|
2023-12-10 18:36:16 +08:00
|
|
|
g.Debug("daemon is stopped, exit now")
|
2023-12-10 17:56:48 +08:00
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
g.StartFn(g)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *GoDaemon) serveSignal() {
|
|
|
|
sig := <-g.sigChan
|
|
|
|
if sig == syscall.SIGTERM {
|
|
|
|
g.state = "stopped"
|
|
|
|
} else {
|
|
|
|
g.state = "restart"
|
|
|
|
}
|
2023-12-10 18:36:16 +08:00
|
|
|
|
|
|
|
g.Debug("Stop it")
|
2023-12-10 17:56:48 +08:00
|
|
|
g.StopFn(g)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *GoDaemon) getDaemonProcess() *os.Process {
|
|
|
|
pid := g.GetPid()
|
|
|
|
if pid == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
p, err := os.FindProcess(pid)
|
|
|
|
if err != nil {
|
2023-12-10 18:36:16 +08:00
|
|
|
g.Debug(err)
|
2023-12-10 17:56:48 +08:00
|
|
|
}
|
|
|
|
serr := p.Signal(syscall.Signal(0))
|
|
|
|
if serr != nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return p
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *GoDaemon) startDaemon() {
|
|
|
|
dp := g.getDaemonProcess()
|
|
|
|
|
|
|
|
if dp != nil {
|
|
|
|
fmt.Println("daemon is running with pid:", dp.Pid)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
execName, _ := os.Executable()
|
|
|
|
cmd := exec.Command(execName)
|
|
|
|
cmd.Env = append(cmd.Env, daemon_env_key+"="+daemon_process)
|
|
|
|
pargs := os.Args[1:]
|
|
|
|
cmd.Env = append(cmd.Env, daemon_taskargs+"="+strings.Join(pargs, ";"))
|
|
|
|
cmd.Start()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *GoDaemon) startTask() *exec.Cmd {
|
|
|
|
extraArgs, _ := os.LookupEnv(daemon_taskargs)
|
|
|
|
var cmd *exec.Cmd
|
|
|
|
execName, _ := os.Executable()
|
|
|
|
if extraArgs != "" {
|
|
|
|
eargs := strings.Split(extraArgs, ";")
|
|
|
|
cmd = exec.Command(execName, eargs...)
|
|
|
|
} else {
|
|
|
|
cmd = exec.Command(execName)
|
|
|
|
}
|
|
|
|
cmd.Env = append(cmd.Env, daemon_env_key+"="+daemon_task)
|
|
|
|
cmd.Start()
|
|
|
|
return cmd
|
|
|
|
}
|
|
|
|
func IsMaster() bool {
|
|
|
|
goDaemonEnv, _ := os.LookupEnv(daemon_env_key)
|
|
|
|
return goDaemonEnv == ""
|
|
|
|
}
|
|
|
|
|
|
|
|
func IsDaemon() bool {
|
|
|
|
goDaemonEnv, _ := os.LookupEnv(daemon_env_key)
|
|
|
|
return goDaemonEnv == daemon_process
|
|
|
|
}
|
|
|
|
|
|
|
|
func IsDaemonTask() bool {
|
|
|
|
goDaemonEnv, _ := os.LookupEnv(daemon_env_key)
|
|
|
|
return goDaemonEnv == daemon_task
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewGoDaemon(start, stop func(*GoDaemon)) *GoDaemon {
|
2023-12-10 18:36:16 +08:00
|
|
|
godaemon := &GoDaemon{
|
|
|
|
Logger: gologger.GetLogger("daemon"),
|
|
|
|
}
|
2023-12-10 17:56:48 +08:00
|
|
|
|
|
|
|
execName, _ := os.Executable()
|
|
|
|
if filepath.Ext(execName) != "" {
|
|
|
|
execName = strings.TrimSuffix(execName, filepath.Ext(execName))
|
|
|
|
}
|
|
|
|
godaemon.pidFile = execName + ".pid"
|
|
|
|
godaemon.StartFn = start
|
|
|
|
godaemon.StopFn = stop
|
|
|
|
return godaemon
|
|
|
|
}
|