package commands import ( "context" "errors" "fmt" "os" "os/signal" "sync" "syscall" "time" "git.nevets.tech/Keys/CertManager/client" "git.nevets.tech/Keys/CertManager/internal" "git.nevets.tech/Keys/CertManager/server" ) var ( ctx context.Context cancel context.CancelFunc wg sync.WaitGroup ) func RunDaemonCmd() error { err := internal.CreateOrUpdatePIDFile("/var/run/certman.pid") if err != nil { if errors.Is(err, internal.ErrorPIDInUse) { return fmt.Errorf("daemon process is already running") } return fmt.Errorf("error creating pidfile: %v", err) } ctx, cancel = context.WithCancel(context.Background()) // Check if main config exists if _, err := os.Stat("/etc/certman/certman.conf"); os.IsNotExist(err) { return fmt.Errorf("main config file not found, please run 'certman --install', then properly configure /etc/certman/certman.conf") } else if err != nil { return fmt.Errorf("error opening /etc/certman/certman.conf: %v", err) } err = internal.LoadConfig() if err != nil { return fmt.Errorf("error loading configuration: %v", err) } // Setup SIGINT and SIGTERM listeners sigChannel := make(chan os.Signal, 1) signal.Notify(sigChannel, syscall.SIGINT, syscall.SIGTERM) defer signal.Stop(sigChannel) reloadSigChan := make(chan os.Signal, 1) signal.Notify(reloadSigChan, syscall.SIGHUP) defer signal.Stop(reloadSigChan) tickSigChan := make(chan os.Signal, 1) signal.Notify(tickSigChan, syscall.SIGUSR1) defer signal.Stop(tickSigChan) tickRate := internal.Config().GetInt("App.tick_rate") ticker := time.NewTicker(time.Duration(tickRate) * time.Hour) defer ticker.Stop() wg.Add(1) if internal.Config().GetString("App.mode") == "server" { fmt.Println("Starting CertManager in server mode...") // Server Task loop go func() { server.Init() defer wg.Done() for { select { case <-ctx.Done(): server.Stop() return case <-reloadSigChan: server.Reload() case <-ticker.C: server.Tick() case <-tickSigChan: server.Tick() } } }() } else if internal.Config().GetString("App.mode") == "client" { fmt.Println("Starting CertManager in client mode...") // Client Task loop go func() { client.Init() defer wg.Done() for { select { case <-ctx.Done(): client.Stop() return case <-reloadSigChan: client.Reload() case <-ticker.C: client.Tick() case <-tickSigChan: client.Tick() } } }() } else { return fmt.Errorf("invalid operating mode \"" + internal.Config().GetString("App.mode") + "\"") } // Cleanup on stop sig := <-sigChannel fmt.Printf("Program terminated with %v\n", sig.String()) stop() wg.Wait() return nil } func stop() { cancel() internal.ClearPIDFile() } func StopDaemonCmd() error { proc, err := internal.DaemonProcess() if err != nil { return fmt.Errorf("error getting daemon process: %v", err) } err = proc.Signal(syscall.SIGTERM) if err != nil { return fmt.Errorf("error sending SIGTERM to daemon PID: %v", err) } return nil } func ReloadDaemonCmd() error { proc, err := internal.DaemonProcess() if err != nil { return fmt.Errorf("error getting daemon process: %v", err) } err = proc.Signal(syscall.SIGHUP) if err != nil { return fmt.Errorf("error sending SIGHUP to daemon PID: %v", err) } return nil } func TickDaemonCmd() error { proc, err := internal.DaemonProcess() if err != nil { return fmt.Errorf("error getting daemon process: %v", err) } err = proc.Signal(syscall.SIGUSR1) if err != nil { return fmt.Errorf("error sending SIGUSR1 to daemon PID: %v", err) } return nil } func DaemonStatusCmd() error { fmt.Println("Not implemented :/") return nil }