A daemon package for use with Go (golang) services with no dependencies
Note: examples are not updated in line with code at this time.
The purpose of this fork is to provide flag and command pass through on services. This fork now provides that for linux distros and darwin (only tested on systemd). There is an issue stating that the takama devs not keen on breaking backward compatibility to implement part of this. However, I'm also not keen on maintaining a fork for such a minor deviation so this will hopefully be a temporary measure whilst I explore alternative approaches.
Further note: I'm not sure command pass-through is even possible on Windows services - I seem to remember having issues with this previously but possibly worked on win 10 (needs checking), either way it was not obvious how to pass this through on windows service init with current code
package main
import (
"fmt"
"log"
"github.com/takama/daemon"
)
func main() {
service, err := daemon.New("name", "description")
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, 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)
}
See examples/cron/cron_job.go
- Igor Dolzhikov
- 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
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!