README
¶
Go Daemon
A daemon package for use with Go (golang) services
Examples
Simplest example (just install self as daemon)
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)
}
Real example
// 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)
}
Service config file
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 |
Example template(for linux systemv)
[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
Cron example
See examples/cron/cron_job.go
Contributors (unsorted)
- 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!
License
Documentation
¶
Overview ¶
Package daemon v1.0.0 for use with Go (golang) services.
Package daemon provides primitives for daemonization of golang services. In the current implementation the only supported operating systems are macOS, FreeBSD, Linux and Windows. Also to note, for global daemons one must have root rights to install or remove the service. The only exception is macOS where there is an implementation of a user daemon that can installed or removed by the current user.
Example:
// 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 interrupted 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) }
Go daemon ¶
Package daemon linux version
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ( // ErrUnsupportedSystem appears if try to use service on system which is not supported by this release ErrUnsupportedSystem = errors.New("Unsupported system") // ErrRootPrivileges appears if run installation or deleting the service without root privileges ErrRootPrivileges = errors.New("You must have root user privileges. Possibly using 'sudo' command should help") // ErrAlreadyInstalled appears if service already installed on the system ErrAlreadyInstalled = errors.New("Service has already been installed") // ErrNotInstalled appears if try to delete service which was not been installed ErrNotInstalled = errors.New("Service is not installed") // ErrAlreadyRunning appears if try to start already running service ErrAlreadyRunning = errors.New("Service is already running") // ErrAlreadyStopped appears if try to stop already stopped service ErrAlreadyStopped = errors.New("Service has already been stopped") )
Functions ¶
Types ¶
type Daemon ¶
type Daemon interface { // GetTemplate - gets service config template GetTemplate() string // SetTemplate - sets service config template SetTemplate(string) error // Install the service into the system Install(args ...string) (string, error) // Remove the service and all corresponding files from the system Remove() (string, error) // Start the service Start() (string, error) // Stop the service Stop() (string, error) // Status - check the service status Status() (string, error) // Run - run executable service Run(e Executable) (string, error) }
Daemon interface has a standard set of methods/commands
type Executable ¶
type Executable interface { // Start - non-blocking start service Start() // Stop - non-blocking stop service Stop() // Run - blocking run service Run() }
Executable interface defines controlling methods of executable service
type Kind ¶
type Kind string
Kind is type of the daemon
const ( // UserAgent is a user daemon that runs as the currently logged in user and // stores its property list in the user’s individual LaunchAgents directory. // In other words, per-user agents provided by the user. Valid for macOS only. UserAgent Kind = "UserAgent" // GlobalAgent is a user daemon that runs as the currently logged in user and // stores its property list in the users' global LaunchAgents directory. In // other words, per-user agents provided by the administrator. Valid for macOS // only. GlobalAgent Kind = "GlobalAgent" // GlobalDaemon is a system daemon that runs as the root user and stores its // property list in the global LaunchDaemons directory. In other words, // system-wide daemons provided by the administrator. Valid for macOS only. GlobalDaemon Kind = "GlobalDaemon" // SystemDaemon is a system daemon that runs as the root user. In other words, // system-wide daemons provided by the administrator. Valid for FreeBSD, Linux // and Windows only. SystemDaemon Kind = "SystemDaemon" )