mirror of
https://github.com/safedep/pmg.git
synced 2026-08-03 07:24:09 +02:00
50 lines
1.5 KiB
Go
50 lines
1.5 KiB
Go
//go:build !windows
|
|||
|
|
|
||
|
|
package proxyserver
|
||
|
|
|
||
|
|
import (
|
||
|
|
"fmt"
|
||
|
|
"os"
|
||
|
|
"os/exec"
|
||
|
|
"syscall"
|
||
|
|
"time"
|
||
|
|
)
|
||
|
|
|
||
|
|
// Daemonize re-execs the current binary with args (which must run the proxy in
|
||
|
|
// foreground mode), detached into its own session (Setsid), with child stdio
|
||
|
|
// redirected to cfg.LogPath. It waits up to cfg.ReadyTimeout for the child to
|
||
|
|
// write the state file and returns the running state. The caller owns the log
|
||
|
|
// path (its parent directory must exist); Daemonize fails if it cannot be
|
||
|
|
// opened.
|
||
|
|
func Daemonize(cfg ProxyDaemonConfig, statePath, exe string, args []string) (State, error) {
|
||
|
|
logFile, err := os.OpenFile(cfg.LogPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o600)
|
||
|
|
if err != nil {
|
||
|
|
return State{}, fmt.Errorf("open daemon log %s: %w", cfg.LogPath, err)
|
||
|
|
}
|
||
|
|
|
||
|
|
defer func() { _ = logFile.Close() }()
|
||
|
|
|
||
|
|
cmd := exec.Command(exe, args...)
|
||
|
|
cmd.SysProcAttr = &syscall.SysProcAttr{Setsid: true}
|
||
|
|
cmd.Stdin = nil
|
||
|
|
cmd.Stdout = logFile
|
||
|
|
cmd.Stderr = logFile
|
||
|
|
|
||
|
|
if err := cmd.Start(); err != nil {
|
||
|
|
return State{}, fmt.Errorf("start daemon: %w", err)
|
||
|
|
}
|
||
|
|
if err := cmd.Process.Release(); err != nil {
|
||
|
|
return State{}, fmt.Errorf("release daemon process: %w", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
deadline := time.Now().Add(cfg.ReadyTimeout)
|
||
|
|
for time.Now().Before(deadline) {
|
||
|
|
if state, rerr := readState(statePath); rerr == nil && state.IsRunning() {
|
||
|
|
return state, nil
|
||
|
|
}
|
||
|
|
time.Sleep(200 * time.Millisecond)
|
||
|
|
}
|
||
|
|
|
||
|
|
return State{}, fmt.Errorf("daemon did not become ready within %s; see %s", cfg.ReadyTimeout, cfg.LogPath)
|
||
|
|
}
|