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
Need to test the Interactive test for the following platforms:
* SysV
* systemd system and user service
* Launchd system and user service
+113 -62
View File
@@ -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
View File
@@ -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
View File
@@ -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) {
-2
View File
@@ -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
View File
@@ -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