@@ 1,50 @@
+package daemonize
+
+import (
+ "fmt"
+ "os"
+ "os/exec"
+ "syscall"
+)
+
+func Daemonize() error {
+ err := syscall.FcntlFlock(os.Stdout.Fd(), syscall.F_SETLKW, &syscall.Flock_t{
+ Type: syscall.F_WRLCK, Whence: 0, Start: 0, Len: 0,
+ })
+ if err != nil {
+ return fmt.Errorf("Failed to lock stdout: %w", err)
+ }
+
+ if os.Getppid() != 1 {
+ // parent; spawns child as daemon
+ binary, err := exec.LookPath(os.Args[0])
+ if err != nil {
+ return fmt.Errorf("Failed to lookup binary: %w", err)
+ }
+ _, err = os.StartProcess(binary, os.Args, &os.ProcAttr{
+ Dir: "", Env: nil,
+ Files: []*os.File{os.Stdin, os.Stdout, os.Stderr}, Sys: nil,
+ })
+ if err != nil {
+ return fmt.Errorf("Failed to start process: %w", err)
+ }
+ os.Exit(0)
+ } else {
+ // start new session
+ _, err := syscall.Setsid()
+ if err != nil {
+ return fmt.Errorf("Failed to create new session: %w", err)
+ }
+ file, err := os.OpenFile("/dev/null", os.O_RDWR, 0)
+ if err != nil {
+ return fmt.Errorf("Failed to open /dev/null: %w", err)
+ }
+ // detach from terminal
+ syscall.Dup2(int(file.Fd()), int(os.Stdin.Fd()))
+ syscall.Dup2(int(file.Fd()), int(os.Stdout.Fd()))
+ syscall.Dup2(int(file.Fd()), int(os.Stderr.Fd()))
+ file.Close()
+ }
+
+ return nil
+}