service: move windows and darwin to system registry.

Also add API to handle the system registry.
This commit is contained in:
Daniel Theophanes
2015-03-11 12:42:30 -07:00
parent c880387dea
commit 3a7d893dde
6 changed files with 168 additions and 147 deletions
-1
View File
@@ -10,7 +10,6 @@ terminal or from a service manager.
## TODO ## TODO
Need to test the Interactive test for the following platforms: Need to test the Interactive test for the following platforms:
* SysV
* systemd system and user service * systemd system and user service
* Launchd system and user service * Launchd system and user service
+113 -62
View File
@@ -12,54 +12,53 @@
// terminal or from a service manager. // terminal or from a service manager.
// //
// Examples in the example/ folder. // Examples in the example/ folder.
/* //
package main // package main
//
import ( // import (
"log" // "log"
//
"github.com/kardianos/service" // "github.com/kardianos/service"
) // )
//
var logger service.Logger // var logger service.Logger
//
type program struct{} // type program struct{}
//
func (p *program) Start(s service.Service) error { // func (p *program) Start(s service.Service) error {
// Start should not block. Do the actual work async. // // Start should not block. Do the actual work async.
go p.run() // go p.run()
return nil // return nil
} // }
func (p *program) run() { // func (p *program) run() {
// Do work here // // Do work here
} // }
func (p *program) Stop(s service.Service) error { // func (p *program) Stop(s service.Service) error {
// Stop should not block. Return with a few seconds. // // Stop should not block. Return with a few seconds.
return nil // return nil
} // }
//
func main() { // func main() {
svcConfig := &service.Config{ // svcConfig := &service.Config{
Name: "GoServiceTest", // Name: "GoServiceTest",
DisplayName: "Go Service Test", // DisplayName: "Go Service Test",
Description: "This is a test Go service.", // Description: "This is a test Go service.",
} // }
//
prg := &program{} // prg := &program{}
s, err := service.New(prg, svcConfig) // s, err := service.New(prg, svcConfig)
if err != nil { // if err != nil {
log.Fatal(err) // log.Fatal(err)
} // }
logger, err = s.Logger(nil) // logger, err = s.Logger(nil)
if err != nil { // if err != nil {
log.Fatal(err) // log.Fatal(err)
} // }
err = s.Run() // err = s.Run()
if err != nil { // if err != nil {
logger.Error(err) // logger.Error(err)
} // }
} // }
*/
package service // import "github.com/kardianos/service" package service // import "github.com/kardianos/service"
import ( import (
@@ -85,14 +84,27 @@ type Config struct {
Option KeyValue 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. // New creates a new service based on a service interface and configuration.
func New(i Interface, c *Config) (Service, error) { func New(i Interface, c *Config) (Service, error) {
if len(c.Name) == 0 { 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 // 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 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 { func Platform() string {
if system == nil {
return ""
}
return system.String() return system.String()
} }
// Interactive returns false if running under the OS service manager // Interactive returns false if running under the OS service manager
// and true otherwise. // and true otherwise.
func Interactive() bool { func Interactive() bool {
if system == nil {
return true
}
return system.Interactive() return system.Interactive()
} }
// runningSystem represents the system and system's service being used. func newSystem() System {
type runningSystem interface { for _, choice := range systemRegistry {
// String returns a description of the OS and service platform. if choice.Detect() == false {
String() string continue
}
// Interactive returns false if running under the OS service manager return choice
// and true otherwise. }
Interactive() bool return nil
} }
// Be sure to implement each platform. // TODO: Choose system could return the choosen system.
var _ runningSystem = 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 // Interface represents the service interface for a program. Start runs before
// the hosting process is granted control and Stop runs when control is returned. // the hosting process is granted control and Stop runs when control is returned.
@@ -192,6 +241,8 @@ type Interface interface {
Stop(s Service) error Stop(s Service) error
} }
// TODO: Add Configure to Service interface.
// Service represents a service that can be run or controlled. // Service represents a service that can be run or controlled.
type Service interface { type Service interface {
// Run should be called shortly after the program entry point. // Run should be called shortly after the program entry point.
+16 -13
View File
@@ -22,15 +22,27 @@ const version = "Darwin Launchd"
type darwinSystem struct{} type darwinSystem struct{}
func (ls darwinSystem) String() string { func (darwinSystem) String() string {
return version return version
} }
func (darwinSystem) Detect() bool {
func (ls darwinSystem) Interactive() bool { return true
}
func (darwinSystem) Interactive() bool {
return interactive 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 var interactive = false
@@ -47,15 +59,6 @@ func isInteractive() (bool, error) {
return os.Getppid() != 1, nil 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 { type darwinLaunchdService struct {
i Interface i Interface
*Config *Config
+25 -59
View File
@@ -5,18 +5,15 @@
package service package service
import ( import (
"errors"
"fmt" "fmt"
"os" "os"
"strings" "strings"
) )
type newServiceFunc func(i Interface, c *Config) (Service, error)
type linuxSystem struct { type linuxSystem struct {
interactive bool interactive bool
selectedName string selectedName string
selectedNew newServiceFunc selectedNew func(i Interface, c *Config) (Service, error)
} }
func (ls linuxSystem) String() string { func (ls linuxSystem) String() string {
@@ -27,35 +24,28 @@ func (ls linuxSystem) Interactive() bool {
return ls.interactive return ls.interactive
} }
type systemChoice interface { type linuxSystemService struct {
Name() string
Detect() bool
Interactive() bool
New(i Interface, c *Config) (Service, error)
}
type linuxSystemChoice struct {
name string name string
detect func() bool detect func() bool
interactive func() bool interactive func() bool
new func(i Interface, c *Config) (Service, error) new func(i Interface, c *Config) (Service, error)
} }
func (sc linuxSystemChoice) Name() string { func (sc linuxSystemService) String() string {
return sc.name return sc.name
} }
func (sc linuxSystemChoice) Detect() bool { func (sc linuxSystemService) Detect() bool {
return sc.detect() return sc.detect()
} }
func (sc linuxSystemChoice) Interactive() bool { func (sc linuxSystemService) Interactive() bool {
return sc.interactive() 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) return sc.new(i, c)
} }
var systemRegistry = []systemChoice{ func init() {
linuxSystemChoice{ ChooseSystem(linuxSystemService{
name: "systemd", name: "systemd",
detect: isSystemd, detect: isSystemd,
interactive: func() bool { interactive: func() bool {
@@ -64,49 +54,25 @@ var systemRegistry = []systemChoice{
}, },
new: newSystemdService, new: newSystemdService,
}, },
linuxSystemChoice{ linuxSystemService{
name: "Upstart", name: "Upstart",
detect: isUpstart, detect: isUpstart,
interactive: func() bool { interactive: func() bool {
is, _ := isInteractive() is, _ := isInteractive()
return is return is
},
new: newUpstartService,
}, },
new: newUpstartService, linuxSystemService{
}, name: "System-V",
linuxSystemChoice{ detect: func() bool { return true },
name: "System-V", interactive: func() bool {
detect: func() bool { return true }, is, _ := isInteractive()
interactive: func() bool { return is
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) { func isInteractive() (bool, error) {
-2
View File
@@ -98,8 +98,6 @@ func (s *systemd) Install() error {
return err return err
} }
return exec.Command("systemctl", "daemon-reload").Run() return exec.Command("systemctl", "daemon-reload").Run()
return nil
} }
func (s *systemd) Uninstall() error { func (s *systemd) Uninstall() error {
+14 -10
View File
@@ -13,10 +13,10 @@ import (
"sync" "sync"
"time" "time"
"github.com/kardianos/osext"
"code.google.com/p/winsvc/eventlog" "code.google.com/p/winsvc/eventlog"
"code.google.com/p/winsvc/mgr" "code.google.com/p/winsvc/mgr"
"code.google.com/p/winsvc/svc" "code.google.com/p/winsvc/svc"
"github.com/kardianos/osext"
) )
const version = "Windows Service" const version = "Windows Service"
@@ -40,11 +40,23 @@ type windowsSystem struct{}
func (windowsSystem) String() string { func (windowsSystem) String() string {
return version return version
} }
func (windowsSystem) Detect() bool {
return true
}
func (windowsSystem) Interactive() bool { func (windowsSystem) Interactive() bool {
return interactive 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 { func (l WindowsLogger) send(err error) error {
if err == nil { 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 { func (ws *windowsService) String() string {
if len(ws.DisplayName) > 0 { if len(ws.DisplayName) > 0 {
return ws.DisplayName return ws.DisplayName