Hi, I’m @Luo Zhu

This article was first published on luo Zhu’s official website

This article synchronizes in the public account “luo Zhu early teahouse”, reprint please contact the author.

Creation is not easy, form a habit, quality three even!

Write a library for the system daemon in Go.

Currently only UNIx-based operating systems are supported (Windows is not supported). But the library has only been tested on Linux and OSX, so if you have the ability to test the library on other platforms, please let me know, thanks.

Please feel free to send me bug reports and fixes. Many thanks to all contributors.

In a multitasking computer operating system, a daemon (/ ː ːmən/ or /ˈdeɪmən/) is a computer program that executes in the background. Such programs are initialized as processes. The name of a daemon program usually ends with the letter “D” : syslogd, for example, is the daemon that manages system logs. — Wikipedia

features

  • Go coroutine secure daemon;
  • Built-in use of PID file;
  • Easy processing of system signals;
  • Daemon control.

The installation

go get github.com/sevlyar/go-daemon
Copy the code

You can use gopkg.in:

go get gopkg.in/sevlyar/go-daemon.v0
Copy the code

If you want to use the library in a production project, use Vendoring. Because I couldn’t guarantee backward compatibility until I released V1.0.

The sample

Based on using

package main

import (
	"fmt"
	"html"
	"log"
	"net/http"

	"github.com/sevlyar/go-daemon"
)

// To terminate the daemon, use:
// kill 'cat sample.pid'.
func main(a) {
	cntxt := &daemon.Context{
		PidFileName: "sample.pid",
		PidFilePerm: 0644,
		LogFileName: "sample.log",
		LogFilePerm: 0640,
		WorkDir:     ". /",
		Umask:       027,
		Args:        []string{"[go-daemon sample]"},
	}

	d, err := cntxt.Reborn()
	iferr ! =nil {
		log.Fatal("Unable to run: ", err)
	}
	ifd ! =nil {
		return
	}
	defer cntxt.Release()

	log.Print("- - - - - - - - - - - - - - -")
	log.Print("daemon started")

	serveHTTP()
}

func serveHTTP(a) {
	http.HandleFunc("/", httpHandler)
	http.ListenAndServe("127.0.0.1:8080".nil)}func httpHandler(w http.ResponseWriter, r *http.Request) {
	log.Printf("request from %s: %s %q", r.RemoteAddr, r.Method, r.URL)
	fmt.Fprintf(w, "go-daemon: %q", html.EscapeString(r.URL.Path))
}
Copy the code

The signal processing

package main

import (
	"flag"
	"github.com/sevlyar/go-daemon"
	"log"
	"os"
	"syscall"
	"time"
)

var (
	signal = flag.String("s"."".'Send signal to the daemon: quit - graceful shutdown stop - fast shutdown reload - reloading the configuration file'))func main(a) {
	flag.Parse()
	daemon.AddCommand(daemon.StringFlag(signal, "quit"), syscall.SIGQUIT, termHandler)
	daemon.AddCommand(daemon.StringFlag(signal, "stop"), syscall.SIGTERM, termHandler)
	daemon.AddCommand(daemon.StringFlag(signal, "reload"), syscall.SIGHUP, reloadHandler)

	cntxt := &daemon.Context{
		PidFileName: "sample.pid",
		PidFilePerm: 0644,
		LogFileName: "sample.log",
		LogFilePerm: 0640,
		WorkDir:     ". /",
		Umask:       027,
		Args:        []string{"[go-daemon sample]"}},if len(daemon.ActiveFlags()) > 0 {
		d, err := cntxt.Search()
		iferr ! =nil {
			log.Fatalf("Unable send signal to the daemon: %s", err.Error())
		}
		daemon.SendCommands(d)
		return
	}

	d, err := cntxt.Reborn()
	iferr ! =nil {
		log.Fatalln(err)
	}
	ifd ! =nil {
		return
	}
	defer cntxt.Release()

	log.Println("- - - - - - - - - - - - - - -")
	log.Println("daemon started")

	go worker()

	err = daemon.ServeSignals()
	iferr ! =nil {
		log.Printf("Error: %s", err.Error())
	}

	log.Println("daemon terminated")}var (
	stop = make(chan struct{})
	done = make(chan struct{}))func worker(a) {
LOOP:
	for {
		time.Sleep(time.Second) // this is work to be done by worker.
		select {
		case <-stop:
			break LOOP
		default:
		}
	}
	done <- struct{}{}
}

func termHandler(sig os.Signal) error {
	log.Println("terminating...")
	stop <- struct{} {}if sig == syscall.SIGQUIT {
		<-done
	}
	return daemon.ErrStop
}

func reloadHandler(sig os.Signal) error {
	log.Println("configuration reloaded")
	return nil
}
Copy the code

The working principle of

We cannot use the fork system call at run time for Golang because the child process does not inherit threads and coroutines in this case. The library uses a simple trick:

It runs its own copy with a tag — a predefined environment variable. The availability of this variable to the process implies execution in a copy of the child process. So, if the tag is not set — the library will perform the parent library’s actions and run its own copy with the tag, and if the tag is set — the library will perform the child library’s actions.

func main(a) {
	Pre()

	context := new(Context)
	child, _ := context.Reborn()

	ifchild ! =nil {
		PostParent()
	} else {
		defer context.Release()
		PostChild()
	}
}
Copy the code