📜 ⬆️ ⬇️

Golang daemon

About a year ago I needed to write a linux daemon that implements a small network service. At that time I was actively learning Go and I really liked this language, so after weighing all the pros and cons I decided to implement the task on it. In addition, Go was already stable and had version 1.0.1.

About what pitfalls I had to face, read under the cut, but at once I will make a reservation: I will describe only the subtleties of the demon implementation on Go. If you have little idea what a “demon” is or how the process is demonized, you should first read about it by searching in Google or on the “linux daemon” or browsing the list of links at the end of the article.

But back to the demons. At first I decided to act classically:

The lack of a clean fork in the standard syscall package did not stop me and did not even arouse any suspicion. I just did something like this (simplified):
ret, _, err := syscall.Syscall(syscall.SYS_FORK, 0, 0, 0) if err != 0 { os.Exit(2) } if ret > 0 { //   os.Exit(0) } 

By implementing all the points, running the daemon and admiring the output of the commands ps -eafw and lsof -p , , .

, Go os/signal. , . fork, . . , code.google.com/p/go/issues/detail?id=227, . : Go fork, .. , , (goroutines), , , .

. , fork . os/signal, , :
func init() { signal_enable(0) // first call - initialize go loop() }

, , loop() . main(). loop() . , fork, loop(). , fork. init() fork, .

os/signal , init() Init() fork. .

- , : - - . , - , . , StartProcess . , , fork exec . , , , - . , . , _GO_DAEMON=1 .

:
const ( envVarName = "_GO_DAEMON" envVarValue = "1" ) func Reborn(umask uint32, workDir string) (err error) { if !WasReborn() { var path string if path, err = filepath.Abs(os.Args[0]); err != nil { return } cmd := exec.Command(path, os.Args[1:]...) envVar := fmt.Sprintf("%s=%s", envVarName, envVarValue) cmd.Env = append(os.Environ(), envVar) if err = cmd.Start(); err != nil { return } os.Exit(0) } syscall.Umask(int(umask)) if len(workDir) == 0 { if err = os.Chdir(workDir); err != nil { return } } _, err = syscall.Setsid() return } func WasReborn() bool { return os.Getenv(envVarName) == envVarValue }
. os/exec - StartProcess .

, , , , Reborn(), . - WasReborn(). (, ), Reborn(), Reborn() ( panic()), - /dev/null.

, , github: go-daemon . pid- . Go. - .

:
- Wikipedia
linux
golang.org
     lsof -p ,  ,         . 

, Go os/signal. , . fork, . . , code.google.com/p/go/issues/detail?id=227, . : Go fork, .. , , (goroutines), , , .

. , fork . os/signal, , :
func init() { signal_enable(0) // first call - initialize go loop() }

, , loop() . main(). loop() . , fork, loop(). , fork. init() fork, .

os/signal , init() Init() fork. .

- , : - - . , - , . , StartProcess . , , fork exec . , , , - . , . , _GO_DAEMON=1 .

:
const ( envVarName = "_GO_DAEMON" envVarValue = "1" ) func Reborn(umask uint32, workDir string) (err error) { if !WasReborn() { var path string if path, err = filepath.Abs(os.Args[0]); err != nil { return } cmd := exec.Command(path, os.Args[1:]...) envVar := fmt.Sprintf("%s=%s", envVarName, envVarValue) cmd.Env = append(os.Environ(), envVar) if err = cmd.Start(); err != nil { return } os.Exit(0) } syscall.Umask(int(umask)) if len(workDir) == 0 { if err = os.Chdir(workDir); err != nil { return } } _, err = syscall.Setsid() return } func WasReborn() bool { return os.Getenv(envVarName) == envVarValue }
. os/exec - StartProcess .

, , , , Reborn(), . - WasReborn(). (, ), Reborn(), Reborn() ( panic()), - /dev/null.

, , github: go-daemon . pid- . Go. - .

:
- Wikipedia
linux
golang.org
lsof -p , , .

, Go os/signal. , . fork, . . , code.google.com/p/go/issues/detail?id=227, . : Go fork, .. , , (goroutines), , , .

. , fork . os/signal, , :
func init() { signal_enable(0) // first call - initialize go loop() }

, , loop() . main(). loop() . , fork, loop(). , fork. init() fork, .

os/signal , init() Init() fork. .

- , : - - . , - , . , StartProcess . , , fork exec . , , , - . , . , _GO_DAEMON=1 .

:
const ( envVarName = "_GO_DAEMON" envVarValue = "1" ) func Reborn(umask uint32, workDir string) (err error) { if !WasReborn() { var path string if path, err = filepath.Abs(os.Args[0]); err != nil { return } cmd := exec.Command(path, os.Args[1:]...) envVar := fmt.Sprintf("%s=%s", envVarName, envVarValue) cmd.Env = append(os.Environ(), envVar) if err = cmd.Start(); err != nil { return } os.Exit(0) } syscall.Umask(int(umask)) if len(workDir) == 0 { if err = os.Chdir(workDir); err != nil { return } } _, err = syscall.Setsid() return } func WasReborn() bool { return os.Getenv(envVarName) == envVarValue }
. os/exec - StartProcess .

, , , , Reborn(), . - WasReborn(). (, ), Reborn(), Reborn() ( panic()), - /dev/null.

, , github: go-daemon . pid- . Go. - .

:
- Wikipedia
linux
golang.org
     lsof -p ,  ,         . 

, Go os/signal. , . fork, . . , code.google.com/p/go/issues/detail?id=227, . : Go fork, .. , , (goroutines), , , .

. , fork . os/signal, , :
func init() { signal_enable(0) // first call - initialize go loop() }

, , loop() . main(). loop() . , fork, loop(). , fork. init() fork, .

os/signal , init() Init() fork. .

- , : - - . , - , . , StartProcess . , , fork exec . , , , - . , . , _GO_DAEMON=1 .

:
const ( envVarName = "_GO_DAEMON" envVarValue = "1" ) func Reborn(umask uint32, workDir string) (err error) { if !WasReborn() { var path string if path, err = filepath.Abs(os.Args[0]); err != nil { return } cmd := exec.Command(path, os.Args[1:]...) envVar := fmt.Sprintf("%s=%s", envVarName, envVarValue) cmd.Env = append(os.Environ(), envVar) if err = cmd.Start(); err != nil { return } os.Exit(0) } syscall.Umask(int(umask)) if len(workDir) == 0 { if err = os.Chdir(workDir); err != nil { return } } _, err = syscall.Setsid() return } func WasReborn() bool { return os.Getenv(envVarName) == envVarValue }
. os/exec - StartProcess .

, , , , Reborn(), . - WasReborn(). (, ), Reborn(), Reborn() ( panic()), - /dev/null.

, , github: go-daemon . pid- . Go. - .

:
- Wikipedia
linux
golang.org
lsof -p , , .

, Go os/signal. , . fork, . . , code.google.com/p/go/issues/detail?id=227, . : Go fork, .. , , (goroutines), , , .

. , fork . os/signal, , :
func init() { signal_enable(0) // first call - initialize go loop() }

, , loop() . main(). loop() . , fork, loop(). , fork. init() fork, .

os/signal , init() Init() fork. .

- , : - - . , - , . , StartProcess . , , fork exec . , , , - . , . , _GO_DAEMON=1 .

:
const ( envVarName = "_GO_DAEMON" envVarValue = "1" ) func Reborn(umask uint32, workDir string) (err error) { if !WasReborn() { var path string if path, err = filepath.Abs(os.Args[0]); err != nil { return } cmd := exec.Command(path, os.Args[1:]...) envVar := fmt.Sprintf("%s=%s", envVarName, envVarValue) cmd.Env = append(os.Environ(), envVar) if err = cmd.Start(); err != nil { return } os.Exit(0) } syscall.Umask(int(umask)) if len(workDir) == 0 { if err = os.Chdir(workDir); err != nil { return } } _, err = syscall.Setsid() return } func WasReborn() bool { return os.Getenv(envVarName) == envVarValue }
. os/exec - StartProcess .

, , , , Reborn(), . - WasReborn(). (, ), Reborn(), Reborn() ( panic()), - /dev/null.

, , github: go-daemon . pid- . Go. - .

:
- Wikipedia
linux
golang.org

')

Source: https://habr.com/ru/post/187668/


All Articles