A daemon package for use with Go (golang) services
package main
import (
"fmt"
"log"
"github.com/takama/daemon"
)
func main() {
service, err := daemon.New("name", "description", daemon.SystemDaemon)
if err != nil {
log.Fatal("Error: ", err)
}
status, err := service.Install()
if err != nil {
log.Fatal(status, "\nError: ", err)
}
fmt.Println(status)
}
// Example of a daemon with echo service
package main
import (
"fmt"
"log"
"net"
"os"
"os/signal"
"syscall"
"github.com/takama/daemon"
)
const (
// name of the service
name = "myservice"
description = "My Echo Service"
// port which daemon should be listen
port = ":9977"
)
// dependencies that are NOT required by the service, but might be used
var dependencies = []string{"dummy.service"}
var stdlog, errlog *log.Logger
// Service has embedded daemon
type Service struct {
daemon.Daemon
}
// Manage by daemon commands or run the daemon
func (service *Service) Manage() (string, error) {
usage := "Usage: myservice install | remove | start | stop | status"
// if received any kind of command, do it
if len(os.Args) > 1 {
command := os.Args[1]
switch command {
case "install":
return service.Install()
case "remove":
return service.Remove()
case "start":
return service.Start()
case "stop":
return service.Stop()
case "status":
return service.Status()
default:
return usage, nil
}
}
// Do something, call your goroutines, etc
// Set up channel on which to send signal notifications.
// We must use a buffered channel or risk missing the signal
// if we're not ready to receive when the signal is sent.
interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, os.Interrupt, os.Kill, syscall.SIGTERM)
// Set up listener for defined host and port
listener, err := net.Listen("tcp", port)
if err != nil {
return "Possibly was a problem with the port binding", err
}
// set up channel on which to send accepted connections
listen := make(chan net.Conn, 100)
go acceptConnection(listener, listen)
// loop work cycle with accept connections or interrupt
// by system signal
for {
select {
case conn := <-listen:
go handleClient(conn)
case killSignal := <-interrupt:
stdlog.Println("Got signal:", killSignal)
stdlog.Println("Stoping listening on ", listener.Addr())
listener.Close()
if killSignal == os.Interrupt {
return "Daemon was interruped by system signal", nil
}
return "Daemon was killed", nil
}
}
// never happen, but need to complete code
return usage, nil
}
// Accept a client connection and collect it in a channel
func acceptConnection(listener net.Listener, listen chan<- net.Conn) {
for {
conn, err := listener.Accept()
if err != nil {
continue
}
listen <- conn
}
}
func handleClient(client net.Conn) {
for {
buf := make([]byte, 4096)
numbytes, err := client.Read(buf)
if numbytes == 0 || err != nil {
return
}
client.Write(buf[:numbytes])
}
}
func init() {
stdlog = log.New(os.Stdout, "", log.Ldate|log.Ltime)
errlog = log.New(os.Stderr, "", log.Ldate|log.Ltime)
}
func main() {
srv, err := daemon.New(name, description, daemon.SystemDaemon, dependencies...)
if err != nil {
errlog.Println("Error: ", err)
os.Exit(1)
}
service := &Service{srv}
status, err := service.Manage()
if err != nil {
errlog.Println(status, "\nError: ", err)
os.Exit(1)
}
fmt.Println(status)
}
Optionally, service config file can be retrieved or updated by calling
GetTemplate() string
and SetTemplate(string)
methods(except MS
Windows). Template will be a default Go Template("text/template"
).
If SetTemplate
is not called, default template content will be used
while creating service.
Variable | Description |
---|---|
Description | Description for service |
Dependencies | Service dependencies |
Name | Service name |
Path | Path of service executable |
Args | Arguments for service executable |
[Unit]
Description={{.Description}}
Requires={{.Dependencies}}
After={{.Dependencies}}
[Service]
PIDFile=/var/run/{{.Name}}.pid
ExecStartPre=/bin/rm -f /var/run/{{.Name}}.pid
ExecStart={{.Path}} {{.Args}}
Restart=on-failure
[Install]
WantedBy=multi-user.target
See examples/cron/cron_job.go
If you want the system daemon to run under a normal user and not root, you can call SetUser(username string)
to
change the username the daemon will run as.
If username isn't set specifically the default username will be set to root
or Administrator
depending on the OS.
If you are installing a service on Windows you will also need to set the user's password as well, using the
SetPassword(password string)
function. On Windows, you will also need to grant the user permissions to
"Log on as a Service" permissions. This is done by opening the Local Security Policy
tool, then navigating to
Local Policies
> User Rights Assignment
> Log on as a service
and add the username to this list.
If the above permissions aren't granted, the service will install correctly, but will fail to start, as the user is unable to launch the daemon.
For SetUser
the username needs to be in the format of DOMAIN\username
. If you are using a local system account,
this can be written in shorthand notation, for example: .\username
- Sheile
- Nguyen Trung Loi
- Donny Prasetyobudi
- Mark Berner
- Fatih Kaya
- Jannick Fahlbusch
- TobyZXJ
- Pichu Chen
- Eric Halpern
- Yota
- Erkan Durmus
- maxxant
- 1for
- okamura
- 0X8C - Demired
- Maximus
- AlgorathDev
- Alexis Camilleri
- neverland4u
- Rustam
- King'ori Maina
All the contributors are welcome. If you would like to be the contributor please accept some rules.
- The pull requests will be accepted only in
develop
branch - All modifications or additions should be tested
- Sorry, We will not accept code with any dependency, only standard library
Thank you for your understanding!