diff --git a/README.md b/README.md index 6731a67..7da6597 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/service.go b/service.go index 26299bd..0ee223d 100644 --- a/service.go +++ b/service.go @@ -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. diff --git a/service_darwin.go b/service_darwin.go index 202c7a7..492dcc1 100644 --- a/service_darwin.go +++ b/service_darwin.go @@ -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 diff --git a/service_linux.go b/service_linux.go index 7ab4b7d..1a832b7 100644 --- a/service_linux.go +++ b/service_linux.go @@ -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) { diff --git a/service_systemd_linux.go b/service_systemd_linux.go index 56a7fad..c647871 100644 --- a/service_systemd_linux.go +++ b/service_systemd_linux.go @@ -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 { diff --git a/service_windows.go b/service_windows.go index d93a917..6597c51 100644 --- a/service_windows.go +++ b/service_windows.go @@ -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