diff --git a/LICENSE b/LICENSE index ffcad4a..3c1a169 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2016 The Go Authors +Copyright (c) 2020 The Go Authors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index abb8dda..27dd650 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Go Daemon -A daemon package for use with Go (golang) services with no dependencies +A daemon package for use with Go (golang) services [![GoDoc](https://godoc.org/github.com/takama/daemon?status.svg)](https://godoc.org/github.com/takama/daemon) @@ -19,7 +19,7 @@ import ( ) func main() { - service, err := daemon.New("name", "description") + service, err := daemon.New("name", "description", daemon.SystemDaemon) if err != nil { log.Fatal("Error: ", err) } @@ -159,7 +159,7 @@ func init() { } func main() { - srv, err := daemon.New(name, description, dependencies...) + srv, err := daemon.New(name, description, daemon.SystemDaemon, dependencies...) if err != nil { errlog.Println("Error: ", err) os.Exit(1) @@ -183,13 +183,13 @@ 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| +| 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) @@ -215,7 +215,6 @@ See `examples/cron/cron_job.go` ## Contributors (unsorted) -- [Igor Dolzhikov](https://github.com/takama) - [Sheile](https://github.com/Sheile) - [Nguyen Trung Loi](https://github.com/loint) - [Donny Prasetyobudi](https://github.com/donnpebe) @@ -235,10 +234,12 @@ See `examples/cron/cron_job.go` - [AlgorathDev](https://github.com/AlgorathDev) - [Alexis Camilleri](https://github.com/krysennn) - [neverland4u](https://github.com/neverland4u) +- [Rustam](https://github.com/rusq) +- [King'ori Maina](https://github.com/itskingori) 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 +- 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 diff --git a/daemon.go b/daemon.go index 229d947..e5b0fa2 100644 --- a/daemon.go +++ b/daemon.go @@ -1,14 +1,16 @@ -// Copyright 2016 The Go Authors. All rights reserved. +// Copyright 2020 The Go Authors. All rights reserved. // Use of this source code is governed by // license that can be found in the LICENSE file. /* -Package daemon v0.12.0 for use with Go (golang) services. +Package daemon v1.0.0 for use with Go (golang) services. -Package daemon provides primitives for daemonization of golang services. -This package is not provide implementation of user daemon, -accordingly must have root rights to install/remove service. -In the current implementation is only supported Linux and Mac Os X daemon. +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: @@ -137,7 +139,7 @@ Example: } func main() { - srv, err := daemon.New(name, description, dependencies...) + srv, err := daemon.New(name, description, daemon.SystemDaemon, dependencies...) if err != nil { errlog.Println("Error: ", err) os.Exit(1) @@ -155,7 +157,11 @@ Go daemon */ package daemon -import "strings" +import ( + "errors" + "runtime" + "strings" +) // Status constants. const ( @@ -199,11 +205,58 @@ type Executable interface { Run() } +// Kind is type of the daemon +type Kind string + +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" +) + // New - Create a new daemon // // name: name of the service // // description: any explanation, what is the service, its purpose -func New(name, description string, dependencies ...string) (Daemon, error) { - return newDaemon(strings.Join(strings.Fields(name), "_"), description, dependencies) +// +// kind: what kind of daemon to create +func New(name, description string, kind Kind, dependencies ...string) (Daemon, error) { + switch runtime.GOOS { + case "darwin": + if kind == SystemDaemon { + return nil, errors.New("Invalid daemon kind specified") + } + case "freebsd": + if kind != SystemDaemon { + return nil, errors.New("Invalid daemon kind specified") + } + case "linux": + if kind != SystemDaemon { + return nil, errors.New("Invalid daemon kind specified") + } + case "windows": + if kind != SystemDaemon { + return nil, errors.New("Invalid daemon kind specified") + } + } + + return newDaemon(strings.Join(strings.Fields(name), "_"), description, kind, dependencies) } diff --git a/daemon_darwin.go b/daemon_darwin.go index 7fa2b4e..0c99343 100644 --- a/daemon_darwin.go +++ b/daemon_darwin.go @@ -1,4 +1,4 @@ -// Copyright 2016 The Go Authors. All rights reserved. +// Copyright 2020 The Go Authors. All rights reserved. // Use of this source code is governed by // license that can be found in the LICENSE file. @@ -8,6 +8,7 @@ package daemon import ( "os" "os/exec" + "os/user" "path/filepath" "regexp" "text/template" @@ -17,17 +18,30 @@ import ( type darwinRecord struct { name string description string + kind Kind dependencies []string } -func newDaemon(name, description string, dependencies []string) (Daemon, error) { +func newDaemon(name, description string, kind Kind, dependencies []string) (Daemon, error) { - return &darwinRecord{name, description, dependencies}, nil + return &darwinRecord{name, description, kind, dependencies}, nil } // Standard service path for system daemons func (darwin *darwinRecord) servicePath() string { - return "/Library/LaunchDaemons/" + darwin.name + ".plist" + var path string + + switch darwin.kind { + case UserAgent: + usr, _ := user.Current() + path = usr.HomeDir + "/Library/LaunchAgents/" + darwin.name + ".plist" + case GlobalAgent: + path = "/Library/LaunchAgents/" + darwin.name + ".plist" + case GlobalDaemon: + path = "/Library/LaunchDaemons/" + darwin.name + ".plist" + } + + return path } // Is a service installed @@ -66,7 +80,8 @@ func (darwin *darwinRecord) checkRunning() (string, bool) { func (darwin *darwinRecord) Install(args ...string) (string, error) { installAction := "Install " + darwin.description + ":" - if ok, err := checkPrivileges(); !ok { + ok, err := checkPrivileges() + if !ok && darwin.kind != UserAgent { return installAction + failed, err } @@ -109,7 +124,8 @@ func (darwin *darwinRecord) Install(args ...string) (string, error) { func (darwin *darwinRecord) Remove() (string, error) { removeAction := "Removing " + darwin.description + ":" - if ok, err := checkPrivileges(); !ok { + ok, err := checkPrivileges() + if !ok && darwin.kind != UserAgent { return removeAction + failed, err } @@ -128,7 +144,8 @@ func (darwin *darwinRecord) Remove() (string, error) { func (darwin *darwinRecord) Start() (string, error) { startAction := "Starting " + darwin.description + ":" - if ok, err := checkPrivileges(); !ok { + ok, err := checkPrivileges() + if !ok && darwin.kind != UserAgent { return startAction + failed, err } @@ -151,7 +168,8 @@ func (darwin *darwinRecord) Start() (string, error) { func (darwin *darwinRecord) Stop() (string, error) { stopAction := "Stopping " + darwin.description + ":" - if ok, err := checkPrivileges(); !ok { + ok, err := checkPrivileges() + if !ok && darwin.kind != UserAgent { return stopAction + failed, err } @@ -173,7 +191,8 @@ func (darwin *darwinRecord) Stop() (string, error) { // Status - Get service status func (darwin *darwinRecord) Status() (string, error) { - if ok, err := checkPrivileges(); !ok { + ok, err := checkPrivileges() + if !ok && darwin.kind != UserAgent { return "", err } diff --git a/daemon_freebsd.go b/daemon_freebsd.go index c39e046..2d84d5f 100644 --- a/daemon_freebsd.go +++ b/daemon_freebsd.go @@ -1,3 +1,7 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by +// license that can be found in the LICENSE file. + package daemon import ( @@ -15,6 +19,7 @@ import ( type bsdRecord struct { name string description string + kind Kind dependencies []string } @@ -66,8 +71,8 @@ func (bsd *bsdRecord) getCmd(cmd string) string { } // Get the daemon properly -func newDaemon(name, description string, dependencies []string) (Daemon, error) { - return &bsdRecord{name, description, dependencies}, nil +func newDaemon(name, description string, kind Kind, dependencies []string) (Daemon, error) { + return &bsdRecord{name, description, kind, dependencies}, nil } func execPath() (name string, err error) { diff --git a/daemon_linux.go b/daemon_linux.go index 633f20c..524c7d8 100644 --- a/daemon_linux.go +++ b/daemon_linux.go @@ -1,4 +1,4 @@ -// Copyright 2016 The Go Authors. All rights reserved. +// Copyright 2020 The Go Authors. All rights reserved. // Use of this source code is governed by // license that can be found in the LICENSE file. @@ -10,15 +10,15 @@ import ( ) // Get the daemon properly -func newDaemon(name, description string, dependencies []string) (Daemon, error) { +func newDaemon(name, description string, kind Kind, dependencies []string) (Daemon, error) { // newer subsystem must be checked first if _, err := os.Stat("/run/systemd/system"); err == nil { - return &systemDRecord{name, description, dependencies}, nil + return &systemDRecord{name, description, kind, dependencies}, nil } if _, err := os.Stat("/sbin/initctl"); err == nil { - return &upstartRecord{name, description, dependencies}, nil + return &upstartRecord{name, description, kind, dependencies}, nil } - return &systemVRecord{name, description, dependencies}, nil + return &systemVRecord{name, description, kind, dependencies}, nil } // Get executable path diff --git a/daemon_linux_systemd.go b/daemon_linux_systemd.go index f7a8ebc..d6e6cca 100644 --- a/daemon_linux_systemd.go +++ b/daemon_linux_systemd.go @@ -1,4 +1,4 @@ -// Copyright 2016 The Go Authors. All rights reserved. +// Copyright 2020 The Go Authors. All rights reserved. // Use of this source code is governed by // license that can be found in the LICENSE file. @@ -16,6 +16,7 @@ import ( type systemDRecord struct { name string description string + kind Kind dependencies []string } diff --git a/daemon_linux_systemv.go b/daemon_linux_systemv.go index 0c1cb17..588f6db 100644 --- a/daemon_linux_systemv.go +++ b/daemon_linux_systemv.go @@ -1,4 +1,4 @@ -// Copyright 2016 The Go Authors. All rights reserved. +// Copyright 2020 The Go Authors. All rights reserved. // Use of this source code is governed by // license that can be found in the LICENSE file. @@ -16,6 +16,7 @@ import ( type systemVRecord struct { name string description string + kind Kind dependencies []string } diff --git a/daemon_linux_upstart.go b/daemon_linux_upstart.go index ab870b7..16216c7 100644 --- a/daemon_linux_upstart.go +++ b/daemon_linux_upstart.go @@ -1,4 +1,4 @@ -// Copyright 2016 The Go Authors. All rights reserved. +// Copyright 2020 The Go Authors. All rights reserved. // Use of this source code is governed by // license that can be found in the LICENSE file. @@ -16,6 +16,7 @@ import ( type upstartRecord struct { name string description string + kind Kind dependencies []string } diff --git a/daemon_windows.go b/daemon_windows.go index bb37ba0..047b635 100644 --- a/daemon_windows.go +++ b/daemon_windows.go @@ -1,4 +1,4 @@ -// Copyright 2016 The Go Authors. All rights reserved. +// Copyright 2020 The Go Authors. All rights reserved. // Use of this source code is governed by // license that can be found in the LICENSE file. @@ -24,12 +24,13 @@ import ( type windowsRecord struct { name string description string + kind Kind dependencies []string } -func newDaemon(name, description string, dependencies []string) (Daemon, error) { +func newDaemon(name, description string, kind Kind, dependencies []string) (Daemon, error) { - return &windowsRecord{name, description, dependencies}, nil + return &windowsRecord{name, description, kind, dependencies}, nil } // Install the service @@ -278,7 +279,7 @@ type serviceHandler struct { executable Executable } -func (sh *serviceHandler) Execute(args []string, r <-chan svc.ChangeRequest, changes chan <- svc.Status) (ssec bool, errno uint32) { +func (sh *serviceHandler) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (ssec bool, errno uint32) { const cmdsAccepted = svc.AcceptStop | svc.AcceptShutdown | svc.AcceptPauseAndContinue changes <- svc.Status{State: svc.StartPending} @@ -289,7 +290,7 @@ func (sh *serviceHandler) Execute(args []string, r <-chan svc.ChangeRequest, cha sh.executable.Start() changes <- svc.Status{State: svc.Running, Accepts: cmdsAccepted} - loop: +loop: for { select { case <-tick: @@ -351,4 +352,4 @@ func (linux *windowsRecord) GetTemplate() string { // SetTemplate - sets service config template func (linux *windowsRecord) SetTemplate(tplStr string) error { return errors.New(fmt.Sprintf("templating is not supported for windows")) -} \ No newline at end of file +} diff --git a/examples/cron/cron_job.go b/examples/cron/cron_job.go index 7102297..fdbc241 100644 --- a/examples/cron/cron_job.go +++ b/examples/cron/cron_job.go @@ -80,7 +80,7 @@ func init() { } func main() { - srv, err := daemon.New(name, description) + srv, err := daemon.New(name, description, daemon.SystemDaemon) if err != nil { errlog.Println("Error: ", err) os.Exit(1) diff --git a/examples/myservice.go b/examples/myservice.go index bf0f5da..a9cce43 100644 --- a/examples/myservice.go +++ b/examples/myservice.go @@ -120,7 +120,7 @@ func init() { } func main() { - srv, err := daemon.New(name, description, dependencies...) + srv, err := daemon.New(name, description, daemon.SystemDaemon, dependencies...) if err != nil { errlog.Println("Error: ", err) os.Exit(1) diff --git a/go.mod b/go.mod index 7115eba..30e34f0 100644 --- a/go.mod +++ b/go.mod @@ -4,5 +4,5 @@ go 1.14 require ( github.com/robfig/cron v1.2.0 - golang.org/x/sys v0.0.0-20200413165638-669c56c373c4 + golang.org/x/sys v0.0.0-20200722175500-76b94024e4b6 ) diff --git a/go.sum b/go.sum index 282b469..c6f16c2 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,4 @@ github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ= github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= -golang.org/x/sys v0.0.0-20200413165638-669c56c373c4 h1:opSr2sbRXk5X5/givKrrKj9HXxFpW2sdCiP8MJSKLQY= -golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200722175500-76b94024e4b6 h1:X9xIZ1YU8bLZA3l6gqDUHSFiD0GFI9S548h6C8nDtOY= +golang.org/x/sys v0.0.0-20200722175500-76b94024e4b6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/helper.go b/helper.go index 35981fb..e4bd4c1 100644 --- a/helper.go +++ b/helper.go @@ -1,4 +1,4 @@ -// Copyright 2016 The Go Authors. All rights reserved. +// Copyright 2020 The Go Authors. All rights reserved. // Use of this source code is governed by // license that can be found in the LICENSE file. diff --git a/helper_legacy.go b/helper_legacy.go index bf5d9f1..f52cc98 100644 --- a/helper_legacy.go +++ b/helper_legacy.go @@ -1,4 +1,4 @@ -// Copyright 2016 The Go Authors. All rights reserved. +// Copyright 2020 The Go Authors. All rights reserved. // Use of this source code is governed by // license that can be found in the LICENSE file. diff --git a/helper_windows.go b/helper_windows.go index 12816bc..2c176a3 100644 --- a/helper_windows.go +++ b/helper_windows.go @@ -1,4 +1,4 @@ -// Copyright 2016 The Go Authors. All rights reserved. +// Copyright 2020 The Go Authors. All rights reserved. // Use of this source code is governed by // license that can be found in the LICENSE file.