~robertgzr/goul

5fe20022905af521f3a3e81372319ae5070c1563 — Robert Günzler 9 months ago master
initialize repo

Signed-off-by: Robert Günzler <r@gnzler.io>
4 files changed, 59 insertions(+), 0 deletions(-)

A README.md
A daemonize/daemonize.go
A daemonize/go.mod
A go.work
A  => README.md +3 -0
@@ 1,3 @@
# goul

_**go**lang **u**tility **l**ibrary_

A  => daemonize/daemonize.go +50 -0
@@ 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
}

A  => daemonize/go.mod +3 -0
@@ 1,3 @@
module dev.gzr.im/goul/daemonize

go 1.20

A  => go.work +3 -0
@@ 1,3 @@
go 1.20

use ./daemonize