golang 守護程序(daemon)例項(二)——載入任意程序
阿新 • • 發佈:2022-03-05
前期實現
-daemon
功能:為任意 Go 程式建立守護程序,使 Go 業務程序脫離終端執行;-forever
功能:建立監控重啟程序,使 Go 業務程序被殺死後能夠重啟;- 不影響業務程序邏輯;
- 實現 Linux 端執行。
見上一篇文章golang 守護程序(daemon)例項——後臺執行,重啟程序
更進一步思考
我們可以不侷限於 Go 程式碼層面的守護程序,而是實現對任意程序的守護程序建立,從而對所有 Linux 環境的程式都可以進行載入。
需求
-prog
功能:將任意待執行的程式以引數形式傳入;- 不影響業務程序邏輯,也就是把原先的
DoSomething
部分修改為啟動新程序; - 跨平臺,實現 Linux 端 / Windows 端執行。
實現
關鍵點
- 為實現相容不同作業系統,對作業系統型別進行了判斷
注意:在 Windows 環境下,雖然 daemon
和 forever
兩項功能都可以使用,但 Windows 沒有所謂的 daemon 託管機制,所以 daemon
功能只是把業務程序變成了後臺執行,cmd
掛掉程式依舊會退出。
switch runtime.GOOS { case "darwin": cmd = exec.Command(os.Getenv("SHELL"), "-c", args[0]) break case "linux": cmd = exec.Command(os.Getenv("SHELL"), "-c", args[0]) break case "windows": cmd = exec.Command("cmd", "/C", args[0]) break default: os.Exit(0) break }
- 在啟動業務程序時,要記得進行
cmd.Wait()
,否則業務程序尚未結束父程序就掛掉了,業務程序就無條件變成後臺程序了
cmd = SubProcess([]string{*program}, true)
cmd.Wait()
- 這個點存在一個Bug,但不影響程式使用。在完成
forever
功能時,當業務程序第一次被殺死後,for
迴圈中重啟業務程序,但-prog
引數被傳遞了2次,百思不得其解,希望有大神能幫忙解決一下
for { fmt.Printf("[*] Forever running in PID: %d PPID: %d\n", os.Getpid(), os.Getppid()) cmd = SubProcess(StripSlice(args, "-"+FOREVER), false) cmd.Wait() }
go-daemon-loader.go
直接上程式碼
package main
import (
"os"
"os/exec"
"fmt"
"flag"
"runtime"
)
const (
PROGRAM = "prog"
DAEMON = "daemon"
FOREVER = "forever"
)
func StripSlice(slice []string, element string) []string {
for i := 0; i < len(slice); {
if slice[i] == element && i != len(slice)-1 {
slice = append(slice[:i], slice[i+1:]...)
break
} else if slice[i] == element && i == len(slice)-1 {
slice = slice[:i]
break
} else {
i++
}
}
return slice
}
func SubProcess(args []string, shell bool) *exec.Cmd {
var cmd *exec.Cmd
if shell {
switch runtime.GOOS {
case "darwin":
cmd = exec.Command(os.Getenv("SHELL"), "-c", args[0])
break
case "linux":
cmd = exec.Command(os.Getenv("SHELL"), "-c", args[0])
break
case "windows":
cmd = exec.Command("cmd", "/C", args[0])
break
default:
os.Exit(1)
break
}
} else {
cmd = exec.Command(args[0], args[1:]...)
}
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Start()
if err != nil {
fmt.Fprintf(os.Stderr, "[-] Error: %s\n", err)
}
return cmd
}
func main(){
var cmd *exec.Cmd
program := flag.String(PROGRAM, "", "run program")
daemon := flag.Bool(DAEMON, false, "run in daemon")
forever := flag.Bool(FOREVER, false, "run forever")
flag.Parse()
fmt.Printf("[*] PID: %d PPID: %d ARG: %s PROG:\"%s\"\n", os.Getpid(), os.Getppid(), os.Args, *program)
if *program == "" {
flag.Usage()
os.Exit(1)
}
if *daemon {
fmt.Printf("[*] Daemon running in PID: %d PPID: %d\n", os.Getpid(), os.Getppid())
SubProcess(StripSlice(os.Args, "-"+DAEMON), false)
os.Exit(0)
} else if *forever {
args := os.Args
for {
fmt.Printf("[*] Forever running in PID: %d PPID: %d\n", os.Getpid(), os.Getppid())
cmd = SubProcess(StripSlice(args, "-"+FOREVER), false)
cmd.Wait()
}
os.Exit(0)
} else {
fmt.Printf("[*] Service running in PID: %d PPID: %d\n", os.Getpid(), os.Getppid())
cmd = SubProcess([]string{*program}, true)
cmd.Wait()
}
}
使用
編譯
- Linux 編譯(同 MacOS 編譯,未測試)
go build -ldflags "-s -w" ./go-daemon-loader.go
- Windows 編譯
go build -ldflags "-s -w" .\go-daemon-loader.go
執行
- Linux
./go-daemon-loader -prog "ping 127.0.0.1" -daemon -forever
- Windows
.\go-daemon-loader.exe -prog "ping 127.0.0.1 -t" -daemon -forever