diff --git a/CHANGELOG.md b/CHANGELOG.md index e557626..9e9437a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog: @go/starter +## v1.5.2 (2026-06-05) +- **可观测性升级: 中心化生命周期审计与 TraceID 链路追踪**: + - **TraceID 分层**: 为 `starter` 编排层引入了独立的长短 ID 体系。 + - Starter 级(8位短 ID):代表整个启动/停止批次的编排上下文。 + - 服务级(10位长 ID):自动派发给每个子服务的 `Start` 方法,确保服务内部日志能与 Starter 状态精准关联。 + - **状态聚合**: 所有的 `service [name] starting / started / stopping / stopped` 日志现由 `starter` 统一负责,并显式记录跨层级的 `trace` ID。 + - **语义统一**: 所有 Starter 核心消息增加 `[starter]` 前缀标识。 +- **架构清理**: 移除了内部对 `log-writer` 的自动注册逻辑,将核心组件的启停权责彻底交还给应用开发者。 + +## v1.5.1 (2026-06-04) +- **依赖升级**: 全面对接 `@go` 基础设施 v1.5.1。 + +## v1.5.0 (2026-05-10) +- **基础设施对齐**: 全局对齐至 v1.5.0。 + ## v1.0.1 (2026-05-12) ### 🚀 Features diff --git a/go.mod b/go.mod index 7b32623..1adf2a9 100644 --- a/go.mod +++ b/go.mod @@ -3,23 +3,22 @@ module apigo.cc/go/starter go 1.25.0 require ( - apigo.cc/go/cast v1.5.0 apigo.cc/go/crypto v1.5.0 - apigo.cc/go/file v1.5.0 apigo.cc/go/id v1.5.0 - apigo.cc/go/log v1.5.0 - apigo.cc/go/shell v1.5.0 - apigo.cc/go/timer v1.5.0 + apigo.cc/go/log v1.5.4 ) require apigo.cc/go/jsmod v1.5.0 // indirect require ( - apigo.cc/go/config v1.5.0 // indirect + apigo.cc/go/cast v1.5.0 // indirect + apigo.cc/go/config v1.5.1 // indirect apigo.cc/go/encoding v1.5.0 // indirect + apigo.cc/go/file v1.5.0 // indirect apigo.cc/go/rand v1.5.0 // indirect apigo.cc/go/safe v1.5.0 // indirect - golang.org/x/crypto v0.51.0 // indirect - golang.org/x/sys v0.44.0 // indirect + apigo.cc/go/shell v1.5.0 // indirect + golang.org/x/crypto v0.52.0 // indirect + golang.org/x/sys v0.45.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 7cff148..883f4b1 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ apigo.cc/go/cast v1.5.0 h1:UBGJtFQ8eJPMQXs37cUgqd7YQo1zI9opuSDBDmn2/pE= apigo.cc/go/cast v1.5.0/go.mod h1:z2GW5p5WCZGEqVVIJUdhl232vRbLf2Qu4EDlEakX/D8= -apigo.cc/go/config v1.5.0 h1:Yuz9QEb11XXG4XkhDi/ueT2M1T3Q9PElE5tiakvjehs= -apigo.cc/go/config v1.5.0/go.mod h1:jdMiDLPa9gzB8/FFZvm9jOopUqdxb7XSX+0OeWcZZUM= +apigo.cc/go/config v1.5.1 h1:rpj7oCzlsDV3f2/YK3Pb+CHbfr2DL5Vyyv6VNkobJP4= +apigo.cc/go/config v1.5.1/go.mod h1:jdMiDLPa9gzB8/FFZvm9jOopUqdxb7XSX+0OeWcZZUM= apigo.cc/go/crypto v1.5.0 h1:Nxz7a6VKCdvaF258IU0NkjQyureOLxfR308Sy2iftUI= apigo.cc/go/crypto v1.5.0/go.mod h1:F9M6nXv+5328r1ZwbTvI6fcr8VdgqHVzALOcsdv6ntE= apigo.cc/go/encoding v1.5.0 h1:EJNdRVDOMoI2DAvZwQNQTbYuqB/6zsEzvg7lS5pQI+I= @@ -12,26 +12,24 @@ apigo.cc/go/id v1.5.0 h1:MjNWPhBhDsoXaLeJDv/0wfJmVMU9EvOs8pWYfsTQ6e8= apigo.cc/go/id v1.5.0/go.mod h1:qhu4a1/KLc/XcBpcsRu+mXZt7U7Wvd9zMcPs4VspuPA= apigo.cc/go/jsmod v1.5.0 h1:JgQtJNiJWy1NOP9AzE8NX5VXJkpO/x3GqLsCCSny5Ec= apigo.cc/go/jsmod v1.5.0/go.mod h1:bmyeZtOAP/j5am+YRnaiM89smysK24K7ebk0koFtsSw= -apigo.cc/go/log v1.5.0 h1:kQuLLtbt33mEuc/xJVcy8NODXkso/QKSZWNclKrSpsI= -apigo.cc/go/log v1.5.0/go.mod h1:Djy+I5aLhGB/EjwRz4KHqkVEz584IAD55FAFiIfInuo= +apigo.cc/go/log v1.5.4 h1:LNyU4v09gfcnZOY53ctnXoKzo45FHoEcPR33lk6PBaY= +apigo.cc/go/log v1.5.4/go.mod h1:Djy+I5aLhGB/EjwRz4KHqkVEz584IAD55FAFiIfInuo= apigo.cc/go/rand v1.5.0 h1:1o8hh8fhdBuk1/h02IvugvamuT3dkWbVJrqEJVQKB2E= apigo.cc/go/rand v1.5.0/go.mod h1:Lh98S2dm9UY0X+M+kNQQEKyXHG5pcCKSFPyXN0QCGdk= apigo.cc/go/safe v1.5.0 h1:W1NblmcU8cex1f9Y5z8mNLUJOzZTE1s6fszb3FbhGnk= apigo.cc/go/safe v1.5.0/go.mod h1:OfQ5d6COePSGEuPvMeOk6KagX2sezw7nvKh7exj9SeM= apigo.cc/go/shell v1.5.0 h1:WLDMMqUU0INeaBDmQsTPr0h/NfB2RknAtiJ5NL467+Q= apigo.cc/go/shell v1.5.0/go.mod h1:rYHA77d5hEsQHcJrbAWf1pHy0sxayeJ0gU55LA/JWQk= -apigo.cc/go/timer v1.5.0 h1:iPo/IQn+iuhBRI1/MR1txwZnamef/RBBfOiIlBiqkgk= -apigo.cc/go/timer v1.5.0/go.mod h1:kOnqTTX+zA4AH7SfC+LpUm4ZvS+DVyWWMqul/V5QWJs= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -golang.org/x/crypto v0.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI= -golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8= -golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ= -golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988= +golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc= +golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= +golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/starter.go b/starter.go index 28f18a7..b102467 100644 --- a/starter.go +++ b/starter.go @@ -11,18 +11,13 @@ import ( "os/signal" "path/filepath" "sort" - "strings" "sync" "syscall" "time" - "apigo.cc/go/cast" "apigo.cc/go/crypto" - "apigo.cc/go/file" "apigo.cc/go/id" "apigo.cc/go/log" - "apigo.cc/go/shell" - "apigo.cc/go/timer" ) var ( @@ -43,6 +38,9 @@ var ( // IPC Security ipcSecret = "apigo-starter-secret-2026" + + // Starter Logger + starterLogger *log.Logger ) type managedService struct { @@ -65,9 +63,14 @@ func init() { AddCommand("restart", "Restart the service", restartCmd) AddCommand("status", "Show service status", statusCmd) AddCommand("kill", "Send signal to a specific service: kill ", killCmd) +} - // Auto-register log writer service with high priority - Register("log-writer", log.WriterService, -100, 0, 0) +// getStarterLogger returns the singleton logger for the starter context. +func getStarterLogger() *log.Logger { + if starterLogger == nil { + starterLogger = log.DefaultLogger.New(id.Get8Bytes4KPerSecond()) + } + return starterLogger } // Register adds a service to be managed by the starter. @@ -87,11 +90,6 @@ func SetAppInfo(name, version string) { appVersion = version } -// SetInfo is an alias for SetAppInfo. -func SetInfo(name, version string) { - SetAppInfo(name, version) -} - // SetUsage sets custom usage text to be displayed in help. func SetUsage(text string) { appUsage = text @@ -102,11 +100,6 @@ func AddCommand(name, desc string, fn func()) { commands[name] = &command{name: name, desc: desc, fn: fn} } -// AddCmd is an alias for AddCommand. -func AddCmd(name, desc string, fn func()) { - AddCommand(name, desc, fn) -} - // Run parses arguments and executes the service. func Run() { flagSet.Usage = showHelp @@ -135,68 +128,22 @@ func Run() { runForeground() } -func showHelp() { - fmt.Printf("%s (%s)\n\n", appName, appVersion) - if appUsage != "" { - fmt.Printf("%s\n\n", appUsage) - } - fmt.Printf("Usage:\n %s [command] [options]\n\nCommands:\n", filepath.Base(os.Args[0])) - - var names []string - for cmdName := range commands { - names = append(names, cmdName) - } - sort.Strings(names) - - for _, cmdName := range names { - fmt.Printf(" %-10s %s\n", cmdName, commands[cmdName].desc) - } - - fmt.Println("\nOptions:") - flagSet.PrintDefaults() - fmt.Println("\nIf no command is provided, the service runs in the foreground.") -} - func runForeground() { - pid := os.Getpid() - savePid(pid) - defer removePid() - - // Prepare IPC listener but don't serve yet to avoid race conditions during startup - sockPath := getSockPath() - _ = os.Remove(sockPath) - l, err := net.Listen("unix", sockPath) - if err == nil { - defer func() { - _ = l.Close() - _ = os.Remove(sockPath) - }() - } + // Setup signal handling for graceful shutdown + sigs := make(chan os.Signal, 1) + signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGUSR1, syscall.SIGUSR2) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - // Setup signal handling - sigChan := make(chan os.Signal, 10) - signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGUSR1, syscall.SIGUSR2) - - // Start registered services if err := startServices(ctx); err != nil { - log.DefaultLogger.Error(fmt.Sprintf("Start services failed: %v", err)) - stopServices() - log.DefaultLogger.Error("Service failed to start, exiting.") + getStarterLogger().Error(fmt.Sprintf("[starter] start services failed: %v", err)) os.Exit(1) - return } - // Service started successfully, now expose IPC - if l != nil { - go serveIPC(l) - } - - for sig := range sigChan { + for { + sig := <-sigs if sig == syscall.SIGHUP { - log.DefaultLogger.Info("Received SIGHUP. Reloading...") reloadServices() continue } @@ -204,18 +151,18 @@ func runForeground() { // Handle user custom signals if sig == syscall.SIGUSR1 || sig == syscall.SIGUSR2 { if !handleUserSignal(nil, sig) { - log.DefaultLogger.Info(fmt.Sprintf("Received signal %v, but no service handled it.", sig)) + getStarterLogger().Info(fmt.Sprintf("[starter] received signal %v, but no service handled it.", sig)) } continue } - log.DefaultLogger.Info(fmt.Sprintf("Received signal: %v. Shutting down...", sig)) + getStarterLogger().Info(fmt.Sprintf("[starter] received signal: %v, shutting down...", sig)) break } cancel() // Trigger context cancellation stopServices() - log.DefaultLogger.Info("Shutdown complete.") + getStarterLogger().Info("[starter] shutdown complete") } func startServices(ctx context.Context) error { @@ -225,9 +172,6 @@ func startServices(ctx context.Context) error { } sort.Ints(priorities) - // Generate a shared logger with trace ID for all services startup - logger := log.DefaultLogger.New(id.Get8Bytes4KPerSecond()) - for _, p := range priorities { svcs := services[p] var wg sync.WaitGroup @@ -243,11 +187,17 @@ func startServices(ctx context.Context) error { sctx, cancel = context.WithTimeout(ctx, ms.startTimeout) defer cancel() } - logger.Info(fmt.Sprintf("service [%s] starting", ms.Name)) - if err := ms.svc.Start(sctx, logger); err != nil { + + // Each service gets its own unique 10-byte trace ID for its internal logs + serviceTraceId := id.Get10Bytes14MPerSecond() + serviceLogger := log.DefaultLogger.New(serviceTraceId) + + // Log using starter's logger (8-byte ID) but include service's trace ID in extra fields + getStarterLogger().Info(fmt.Sprintf("service [%s] starting", ms.Name), "trace", serviceTraceId) + if err := ms.svc.Start(sctx, serviceLogger); err != nil { errChan <- fmt.Errorf("service [%s] start error: %w", ms.Name, err) } else { - logger.Info(fmt.Sprintf("service [%s] started", ms.Name)) + getStarterLogger().Info(fmt.Sprintf("service [%s] started", ms.Name), "trace", serviceTraceId) } }(ms) } @@ -267,8 +217,6 @@ func stopServices() { sort.Slice(startedPriorities, func(i, j int) bool { return startedPriorities[i] > startedPriorities[j] }) - - logger := log.DefaultLogger.New(id.Get8Bytes4KPerSecond()) for _, p := range startedPriorities { svcs := services[p] @@ -283,11 +231,11 @@ func stopServices() { sctx, cancel = context.WithTimeout(sctx, ms.stopTimeout) defer cancel() } - logger.Info(fmt.Sprintf("service [%s] stopping", ms.Name)) + getStarterLogger().Info(fmt.Sprintf("service [%s] stopping", ms.Name)) if err := ms.svc.Stop(sctx); err != nil { - logger.Error(fmt.Sprintf("service [%s] stop error: %v", ms.Name, err)) + getStarterLogger().Error(fmt.Sprintf("service [%s] stop error: %v", ms.Name, err)) } else { - logger.Info(fmt.Sprintf("service [%s] stopped", ms.Name)) + getStarterLogger().Info(fmt.Sprintf("service [%s] stopped", ms.Name)) } }(ms) } @@ -297,11 +245,12 @@ func stopServices() { } func reloadServices() { + getStarterLogger().Info("[starter] reloading all services...") for _, p := range startedPriorities { for _, ms := range services[p] { if r, ok := ms.svc.(Reloader); ok { if err := r.Reload(); err != nil { - log.DefaultLogger.Error(fmt.Sprintf("service [%s] reload error: %v", ms.Name, err)) + getStarterLogger().Error(fmt.Sprintf("service [%s] reload error: %v", ms.Name, err)) } } } @@ -312,7 +261,7 @@ func handleUserSignal(svcName *string, sig os.Signal) bool { handled := false for _, p := range startedPriorities { for _, ms := range services[p] { - if svcName != nil && ms.Name != *svcName { + if svcName != nil && *svcName != ms.Name { continue } if h, ok := ms.svc.(UserSignalHandler); ok { @@ -325,168 +274,103 @@ func handleUserSignal(svcName *string, sig os.Signal) bool { return handled } -func serveIPC(l net.Listener) { - for { - conn, err := l.Accept() - if err != nil { - return - } - go func(c net.Conn) { - defer c.Close() - data := make([]byte, 4096) - n, err := c.Read(data) - if err != nil || n == 0 { - return - } - - // Protocol: TOKEN COMMAND ARGS... - parts := strings.Split(string(data[:n]), " ") - if len(parts) < 2 { - return - } - - token := parts[0] - if token != getIPCToken(os.Getpid()) { - _, _ = c.Write([]byte("Error: Unauthorized")) - return - } - - cmd := parts[1] - args := parts[2:] - - switch cmd { - case "status": - _, _ = c.Write([]byte(getInternalStatus())) - case "kill": - if len(args) < 2 { - _, _ = c.Write([]byte("Error: Missing arguments for kill")) - return - } - svcName := args[0] - sigNum := cast.Int(args[1]) - if handleUserSignal(&svcName, syscall.Signal(sigNum)) { - _, _ = c.Write([]byte(fmt.Sprintf("Signal %d sent to %s", sigNum, svcName))) - } else { - _, _ = c.Write([]byte(fmt.Sprintf("Error: Service %s not found or didn't handle signal", svcName))) - } - default: - _, _ = c.Write([]byte("Error: Unknown command")) - } - }(conn) +func showHelp() { + fmt.Printf("%s (%s)\n\n", appName, appVersion) + if appUsage != "" { + fmt.Printf("%s\n\n", appUsage) } -} + fmt.Printf("Usage:\n %s [command] [options]\n\nCommands:\n", filepath.Base(os.Args[0])) -func getInternalStatus() string { - var out string - var priorities []int - for p := range services { - priorities = append(priorities, p) + var names []string + for cmdName := range commands { + names = append(names, cmdName) } - sort.Ints(priorities) + sort.Strings(names) - for _, p := range priorities { - for _, ms := range services[p] { - statusMsg, err := ms.svc.Status() - indicator := shell.Green("OK") - if err != nil { - indicator = shell.Red(fmt.Sprintf("FAIL (%v)", err)) - } - if statusMsg != "" { - out += fmt.Sprintf("[%d] %-20s %s (%s)\n", p, ms.Name, indicator, statusMsg) - } else { - out += fmt.Sprintf("[%d] %-20s %s\n", p, ms.Name, indicator) - } - } + for _, name := range names { + fmt.Printf(" %-10s %s\n", name, commands[name].desc) } - return out + fmt.Println() } func startCmd() { - pid := loadPid() - if pid > 0 && isProcessRunning(pid) { - log.DefaultLogger.Info(fmt.Sprintf("%s is already running (PID %d)", appName, pid)) + pid := getPid() + if pid != 0 && isProcessRunning(pid) { + getStarterLogger().Info(fmt.Sprintf("[starter] %s is already running (PID %d)", appName, pid)) return } - args := []string{} - for i := 1; i < len(os.Args); i++ { - if os.Args[i] != "start" { - args = append(args, os.Args[i]) - } + cmd := exec.Command(os.Args[0]) + cmd.Args = append([]string{os.Args[0]}, flagSet.Args()...) + cmd.Stdout = nil + cmd.Stderr = nil + cmd.SysProcAttr = &syscall.SysProcAttr{Setsid: true} + + if err := cmd.Start(); err != nil { + getStarterLogger().Error(fmt.Sprintf("[starter] failed to start %s: %v", appName, err)) + return } - cmd := exec.Command(os.Args[0], args...) - err := cmd.Start() - if err != nil { - log.DefaultLogger.Error(fmt.Sprintf("Failed to start %s: %v", appName, err)) - os.Exit(1) - } - - log.DefaultLogger.Info(fmt.Sprintf("%s started (PID %d)", appName, cmd.Process.Pid)) + _ = os.WriteFile(getPidPath(), []byte(fmt.Sprintf("%d", cmd.Process.Pid)), 0644) + getStarterLogger().Info(fmt.Sprintf("[starter] %s started (PID %d)", appName, cmd.Process.Pid)) } func stopCmd() { - pid := loadPid() - if pid <= 0 || !isProcessRunning(pid) { - log.DefaultLogger.Info(fmt.Sprintf("%s is not running", appName)) + pid := getPid() + if pid == 0 || !isProcessRunning(pid) { + getStarterLogger().Info(fmt.Sprintf("[starter] %s is not running", appName)) return } - process, _ := os.FindProcess(pid) - log.DefaultLogger.Info(fmt.Sprintf("Stopping %s (PID %d)...", appName, pid)) - _ = process.Signal(syscall.SIGTERM) + getStarterLogger().Info(fmt.Sprintf("[starter] stopping %s (PID %d)...", appName, pid)) + _ = syscall.Kill(pid, syscall.SIGTERM) - err := timer.Retry(func() error { - if isProcessRunning(pid) { - return fmt.Errorf("still running") + for i := 0; i < 30; i++ { + if !isProcessRunning(pid) { + removePid() + getStarterLogger().Info(fmt.Sprintf("[starter] %s stopped", appName)) + return } - return nil - }, timer.WithMaxRetries(25), timer.WithBackoff(200*time.Millisecond, 1.0)) - - if err == nil { - log.DefaultLogger.Info("Stopped OK") - removePid() - return + time.Sleep(500 * time.Millisecond) } - - log.DefaultLogger.Info("Stop timeout, killing...") - _ = process.Kill() + getStarterLogger().Warning(fmt.Sprintf("[starter] %s failed to stop gracefully, killing...", appName)) + _ = syscall.Kill(pid, syscall.SIGKILL) removePid() } func restartCmd() { stopCmd() - _ = timer.Retry(func() error { return nil }, timer.WithMaxRetries(1), timer.WithBackoff(500*time.Millisecond, 1.0)) startCmd() } func statusCmd() { - pid := loadPid() - isRunning := pid > 0 && isProcessRunning(pid) - if isRunning { - fmt.Printf("%s is %s (PID %d)\n", appName, shell.Green("running"), pid) - res, err := callIPC(pid, "status") - if err == nil { - fmt.Println("\nServices:") - fmt.Print(res) - } - } else { - fmt.Printf("%s is %s\n", appName, shell.Red("not running")) + pid := getPid() + if pid == 0 || !isProcessRunning(pid) { + fmt.Printf("%s is NOT running\n", appName) + return + } + + fmt.Printf("%s is running (PID %d)\n\n", appName, pid) + res, err := callIPC(pid, "status") + if err == nil { + fmt.Println("Services Status:") + fmt.Println(res) } } func killCmd() { - if len(flagSet.Args()) < 2 { + if flagSet.NArg() < 2 { fmt.Println("Usage: kill ") return } - pid := loadPid() - if pid <= 0 || !isProcessRunning(pid) { - fmt.Println("Error: process not running") - return - } svcName := flagSet.Arg(0) sigNum := flagSet.Arg(1) + pid := getPid() + if pid == 0 || !isProcessRunning(pid) { + fmt.Println("Application is not running") + return + } + res, err := callIPC(pid, fmt.Sprintf("kill %s %s", svcName, sigNum)) if err != nil { fmt.Printf("Error: %v\n", err) @@ -496,24 +380,24 @@ func killCmd() { } func callIPC(pid int, cmd string) (string, error) { - conn, err := net.Dial("unix", getSockPath()) + sockPath := getSockPath() + conn, err := net.Dial("unix", sockPath) if err != nil { return "", err } defer conn.Close() - token := getIPCToken(pid) + token := generateToken(pid) _, _ = conn.Write([]byte(fmt.Sprintf("%s %s", token, cmd))) - data, err := io.ReadAll(conn) + buf, err := io.ReadAll(conn) if err != nil { return "", err } - return string(data), nil + return string(buf), nil } -func getIPCToken(pid int) string { - // Use Sha256 for better security +func generateToken(pid int) string { return crypto.Sha256ToHex([]byte(fmt.Sprintf("%s:%d", ipcSecret, pid))) } @@ -525,16 +409,14 @@ func getSockPath() string { return filepath.Join(os.TempDir(), fmt.Sprintf("%s-%s.sock", appName, appVersion)) } -func savePid(p int) { - _ = file.Write(getPidPath(), cast.To[string](p)) -} - -func loadPid() int { - data, err := file.Read(getPidPath()) +func getPid() int { + data, err := os.ReadFile(getPidPath()) if err != nil { return 0 } - return cast.To[int](data) + var pid int + fmt.Sscanf(string(data), "%d", &pid) + return pid } func removePid() {