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
|
## 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
@@ -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
@@ -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
@@ -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) {
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user