service: move windows and darwin to system registry.
Also add API to handle the system registry.
This commit is contained in:
@@ -10,7 +10,6 @@ terminal or from a service manager.
|
||||
|
||||
## TODO
|
||||
Need to test the Interactive test for the following platforms:
|
||||
* SysV
|
||||
* systemd system and user service
|
||||
* Launchd system and user service
|
||||
|
||||
|
||||
+113
-62
@@ -12,54 +12,53 @@
|
||||
// terminal or from a service manager.
|
||||
//
|
||||
// Examples in the example/ folder.
|
||||
/*
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/kardianos/service"
|
||||
)
|
||||
|
||||
var logger service.Logger
|
||||
|
||||
type program struct{}
|
||||
|
||||
func (p *program) Start(s service.Service) error {
|
||||
// Start should not block. Do the actual work async.
|
||||
go p.run()
|
||||
return nil
|
||||
}
|
||||
func (p *program) run() {
|
||||
// Do work here
|
||||
}
|
||||
func (p *program) Stop(s service.Service) error {
|
||||
// Stop should not block. Return with a few seconds.
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
svcConfig := &service.Config{
|
||||
Name: "GoServiceTest",
|
||||
DisplayName: "Go Service Test",
|
||||
Description: "This is a test Go service.",
|
||||
}
|
||||
|
||||
prg := &program{}
|
||||
s, err := service.New(prg, svcConfig)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
logger, err = s.Logger(nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = s.Run()
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
}
|
||||
}
|
||||
*/
|
||||
//
|
||||
// package main
|
||||
//
|
||||
// import (
|
||||
// "log"
|
||||
//
|
||||
// "github.com/kardianos/service"
|
||||
// )
|
||||
//
|
||||
// var logger service.Logger
|
||||
//
|
||||
// type program struct{}
|
||||
//
|
||||
// func (p *program) Start(s service.Service) error {
|
||||
// // Start should not block. Do the actual work async.
|
||||
// go p.run()
|
||||
// return nil
|
||||
// }
|
||||
// func (p *program) run() {
|
||||
// // Do work here
|
||||
// }
|
||||
// func (p *program) Stop(s service.Service) error {
|
||||
// // Stop should not block. Return with a few seconds.
|
||||
// return nil
|
||||
// }
|
||||
//
|
||||
// func main() {
|
||||
// svcConfig := &service.Config{
|
||||
// Name: "GoServiceTest",
|
||||
// DisplayName: "Go Service Test",
|
||||
// Description: "This is a test Go service.",
|
||||
// }
|
||||
//
|
||||
// prg := &program{}
|
||||
// s, err := service.New(prg, svcConfig)
|
||||
// if err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
// logger, err = s.Logger(nil)
|
||||
// if err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
// err = s.Run()
|
||||
// if err != nil {
|
||||
// logger.Error(err)
|
||||
// }
|
||||
// }
|
||||
package service // import "github.com/kardianos/service"
|
||||
|
||||
import (
|
||||
@@ -85,14 +84,27 @@ type Config struct {
|
||||
Option KeyValue
|
||||
}
|
||||
|
||||
var errNameFieldRequired = errors.New("Config.Name field is required.")
|
||||
var (
|
||||
system System
|
||||
systemRegistry []System
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrNameFieldRequired is returned when Conifg.Name is empty.
|
||||
ErrNameFieldRequired = errors.New("Config.Name field is required.")
|
||||
// ErrNoServiceSystemDetected is returned when no system was detected.
|
||||
ErrNoServiceSystemDetected = errors.New("No service system detected.")
|
||||
)
|
||||
|
||||
// New creates a new service based on a service interface and configuration.
|
||||
func New(i Interface, c *Config) (Service, error) {
|
||||
if len(c.Name) == 0 {
|
||||
return nil, errNameFieldRequired
|
||||
return nil, ErrNameFieldRequired
|
||||
}
|
||||
return newService(i, c)
|
||||
if system == nil {
|
||||
return nil, ErrNoServiceSystemDetected
|
||||
}
|
||||
return system.New(i, c)
|
||||
}
|
||||
|
||||
// KeyValue provides a list of platform specific options. See platform docs for
|
||||
@@ -143,29 +155,66 @@ func (kv KeyValue) float64(name string, defaultValue float64) float64 {
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
// Platform returns a description of the OS and service platform.
|
||||
// TODO: Do these really need to be package level?
|
||||
|
||||
// Platform returns a description of the system service.
|
||||
func Platform() string {
|
||||
if system == nil {
|
||||
return ""
|
||||
}
|
||||
return system.String()
|
||||
}
|
||||
|
||||
// Interactive returns false if running under the OS service manager
|
||||
// and true otherwise.
|
||||
func Interactive() bool {
|
||||
if system == nil {
|
||||
return true
|
||||
}
|
||||
return system.Interactive()
|
||||
}
|
||||
|
||||
// runningSystem represents the system and system's service being used.
|
||||
type runningSystem interface {
|
||||
// String returns a description of the OS and service platform.
|
||||
String() string
|
||||
|
||||
// Interactive returns false if running under the OS service manager
|
||||
// and true otherwise.
|
||||
Interactive() bool
|
||||
func newSystem() System {
|
||||
for _, choice := range systemRegistry {
|
||||
if choice.Detect() == false {
|
||||
continue
|
||||
}
|
||||
return choice
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Be sure to implement each platform.
|
||||
var _ runningSystem = system
|
||||
// TODO: Choose system could return the choosen system.
|
||||
|
||||
// ChooseSystem chooses a system from the given system services.
|
||||
// SystemServices are considered in the order they are suggested.
|
||||
// Calling this may change what Interactive and Platform return.
|
||||
func ChooseSystem(a ...System) {
|
||||
systemRegistry = a
|
||||
system = newSystem()
|
||||
}
|
||||
|
||||
// AvailableSystems returns the list of system services considered
|
||||
// when choosing the system service.
|
||||
func AvailableSystems() []System {
|
||||
return systemRegistry
|
||||
}
|
||||
|
||||
// System represents the service manager that is available.
|
||||
type System interface {
|
||||
// String returns a description of the system.
|
||||
String() string
|
||||
|
||||
// Detect returns true if the system is available to use.
|
||||
Detect() bool
|
||||
|
||||
// Interactive returns false if running under the system service manager
|
||||
// and true otherwise.
|
||||
Interactive() bool
|
||||
|
||||
// New creates a new service for this system.
|
||||
New(i Interface, c *Config) (Service, error)
|
||||
}
|
||||
|
||||
// Interface represents the service interface for a program. Start runs before
|
||||
// the hosting process is granted control and Stop runs when control is returned.
|
||||
@@ -192,6 +241,8 @@ type Interface interface {
|
||||
Stop(s Service) error
|
||||
}
|
||||
|
||||
// TODO: Add Configure to Service interface.
|
||||
|
||||
// Service represents a service that can be run or controlled.
|
||||
type Service interface {
|
||||
// Run should be called shortly after the program entry point.
|
||||
|
||||
+16
-13
@@ -22,15 +22,27 @@ const version = "Darwin Launchd"
|
||||
|
||||
type darwinSystem struct{}
|
||||
|
||||
func (ls darwinSystem) String() string {
|
||||
func (darwinSystem) String() string {
|
||||
return version
|
||||
}
|
||||
|
||||
func (ls darwinSystem) Interactive() bool {
|
||||
func (darwinSystem) Detect() bool {
|
||||
return true
|
||||
}
|
||||
func (darwinSystem) Interactive() bool {
|
||||
return interactive
|
||||
}
|
||||
func (darwinSystem) New(i Interface, c *Config) (Service, error) {
|
||||
s := &darwinLaunchdService{
|
||||
i: i,
|
||||
Config: c,
|
||||
}
|
||||
|
||||
var system = darwinSystem{}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
ChooseSystem(darwinSystem{})
|
||||
}
|
||||
|
||||
var interactive = false
|
||||
|
||||
@@ -47,15 +59,6 @@ func isInteractive() (bool, error) {
|
||||
return os.Getppid() != 1, nil
|
||||
}
|
||||
|
||||
func newService(i Interface, c *Config) (*darwinLaunchdService, error) {
|
||||
s := &darwinLaunchdService{
|
||||
i: i,
|
||||
Config: c,
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
type darwinLaunchdService struct {
|
||||
i Interface
|
||||
*Config
|
||||
|
||||
+25
-59
@@ -5,18 +5,15 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type newServiceFunc func(i Interface, c *Config) (Service, error)
|
||||
|
||||
type linuxSystem struct {
|
||||
interactive bool
|
||||
selectedName string
|
||||
selectedNew newServiceFunc
|
||||
selectedNew func(i Interface, c *Config) (Service, error)
|
||||
}
|
||||
|
||||
func (ls linuxSystem) String() string {
|
||||
@@ -27,35 +24,28 @@ func (ls linuxSystem) Interactive() bool {
|
||||
return ls.interactive
|
||||
}
|
||||
|
||||
type systemChoice interface {
|
||||
Name() string
|
||||
Detect() bool
|
||||
Interactive() bool
|
||||
New(i Interface, c *Config) (Service, error)
|
||||
}
|
||||
|
||||
type linuxSystemChoice struct {
|
||||
type linuxSystemService struct {
|
||||
name string
|
||||
detect func() bool
|
||||
interactive func() bool
|
||||
new func(i Interface, c *Config) (Service, error)
|
||||
}
|
||||
|
||||
func (sc linuxSystemChoice) Name() string {
|
||||
func (sc linuxSystemService) String() string {
|
||||
return sc.name
|
||||
}
|
||||
func (sc linuxSystemChoice) Detect() bool {
|
||||
func (sc linuxSystemService) Detect() bool {
|
||||
return sc.detect()
|
||||
}
|
||||
func (sc linuxSystemChoice) Interactive() bool {
|
||||
func (sc linuxSystemService) Interactive() bool {
|
||||
return sc.interactive()
|
||||
}
|
||||
func (sc linuxSystemChoice) New(i Interface, c *Config) (Service, error) {
|
||||
func (sc linuxSystemService) New(i Interface, c *Config) (Service, error) {
|
||||
return sc.new(i, c)
|
||||
}
|
||||
|
||||
var systemRegistry = []systemChoice{
|
||||
linuxSystemChoice{
|
||||
func init() {
|
||||
ChooseSystem(linuxSystemService{
|
||||
name: "systemd",
|
||||
detect: isSystemd,
|
||||
interactive: func() bool {
|
||||
@@ -64,49 +54,25 @@ var systemRegistry = []systemChoice{
|
||||
},
|
||||
new: newSystemdService,
|
||||
},
|
||||
linuxSystemChoice{
|
||||
name: "Upstart",
|
||||
detect: isUpstart,
|
||||
interactive: func() bool {
|
||||
is, _ := isInteractive()
|
||||
return is
|
||||
linuxSystemService{
|
||||
name: "Upstart",
|
||||
detect: isUpstart,
|
||||
interactive: func() bool {
|
||||
is, _ := isInteractive()
|
||||
return is
|
||||
},
|
||||
new: newUpstartService,
|
||||
},
|
||||
new: newUpstartService,
|
||||
},
|
||||
linuxSystemChoice{
|
||||
name: "System-V",
|
||||
detect: func() bool { return true },
|
||||
interactive: func() bool {
|
||||
is, _ := isInteractive()
|
||||
return is
|
||||
linuxSystemService{
|
||||
name: "System-V",
|
||||
detect: func() bool { return true },
|
||||
interactive: func() bool {
|
||||
is, _ := isInteractive()
|
||||
return is
|
||||
},
|
||||
new: newSystemVService,
|
||||
},
|
||||
new: newSystemVService,
|
||||
},
|
||||
}
|
||||
|
||||
func newLinuxSystem() linuxSystem {
|
||||
for _, choice := range systemRegistry {
|
||||
if choice.Detect() == false {
|
||||
continue
|
||||
}
|
||||
return linuxSystem{
|
||||
interactive: choice.Interactive(),
|
||||
selectedName: choice.Name(),
|
||||
selectedNew: choice.New,
|
||||
}
|
||||
}
|
||||
return linuxSystem{}
|
||||
}
|
||||
|
||||
var system = newLinuxSystem()
|
||||
|
||||
var errNoServiceSystemDetected = errors.New("No service system detected.")
|
||||
|
||||
func newService(i Interface, c *Config) (Service, error) {
|
||||
if system.selectedNew == nil {
|
||||
return nil, errNoServiceSystemDetected
|
||||
}
|
||||
return system.selectedNew(i, c)
|
||||
)
|
||||
}
|
||||
|
||||
func isInteractive() (bool, error) {
|
||||
|
||||
@@ -98,8 +98,6 @@ func (s *systemd) Install() error {
|
||||
return err
|
||||
}
|
||||
return exec.Command("systemctl", "daemon-reload").Run()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *systemd) Uninstall() error {
|
||||
|
||||
+14
-10
@@ -13,10 +13,10 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/kardianos/osext"
|
||||
"code.google.com/p/winsvc/eventlog"
|
||||
"code.google.com/p/winsvc/mgr"
|
||||
"code.google.com/p/winsvc/svc"
|
||||
"github.com/kardianos/osext"
|
||||
)
|
||||
|
||||
const version = "Windows Service"
|
||||
@@ -40,11 +40,23 @@ type windowsSystem struct{}
|
||||
func (windowsSystem) String() string {
|
||||
return version
|
||||
}
|
||||
func (windowsSystem) Detect() bool {
|
||||
return true
|
||||
}
|
||||
func (windowsSystem) Interactive() bool {
|
||||
return interactive
|
||||
}
|
||||
func (windowsSystem) New(i Interface, c *Config) (Service, error) {
|
||||
ws := &windowsService{
|
||||
i: i,
|
||||
Config: c,
|
||||
}
|
||||
return ws, nil
|
||||
}
|
||||
|
||||
var system = windowsSystem{}
|
||||
func init() {
|
||||
ChooseSystem(windowsSystem{})
|
||||
}
|
||||
|
||||
func (l WindowsLogger) send(err error) error {
|
||||
if err == nil {
|
||||
@@ -103,14 +115,6 @@ func init() {
|
||||
}
|
||||
}
|
||||
|
||||
func newService(i Interface, c *Config) (*windowsService, error) {
|
||||
ws := &windowsService{
|
||||
i: i,
|
||||
Config: c,
|
||||
}
|
||||
return ws, nil
|
||||
}
|
||||
|
||||
func (ws *windowsService) String() string {
|
||||
if len(ws.DisplayName) > 0 {
|
||||
return ws.DisplayName
|
||||
|
||||
Reference in New Issue
Block a user