service: update api.
This commit is contained in:
Binary file not shown.
+28
-13
@@ -10,30 +10,31 @@ import (
|
||||
|
||||
var logger service.Logger
|
||||
|
||||
// Program structures.
|
||||
// Define Start and Stop methods.
|
||||
type program struct {
|
||||
exit chan struct{}
|
||||
}
|
||||
|
||||
func (p *program) Start(s service.Service) error {
|
||||
if s.Interactive() {
|
||||
if service.Local.Interactive() {
|
||||
logger.Info("Running in terminal.")
|
||||
} else {
|
||||
logger.Info("Running under service manager.")
|
||||
}
|
||||
p.exit = make(chan struct{})
|
||||
|
||||
// Start should not block. Do the actual work async.
|
||||
go p.run()
|
||||
return nil
|
||||
}
|
||||
func (p *program) run() error {
|
||||
logger.Infof("I'm running %v.", service.LocalSystem())
|
||||
logger.Infof("I'm running %v.", service.Local)
|
||||
ticker := time.NewTicker(2 * time.Second)
|
||||
for {
|
||||
select {
|
||||
case tm := <-ticker.C:
|
||||
err := logger.Infof("Still running at %v...", tm)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
logger.Infof("Still running at %v...", tm)
|
||||
case <-p.exit:
|
||||
ticker.Stop()
|
||||
return nil
|
||||
@@ -42,14 +43,18 @@ func (p *program) run() error {
|
||||
return nil
|
||||
}
|
||||
func (p *program) Stop(s service.Service) error {
|
||||
err := logger.Info("I'm Stopping!")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// Any work in Stop should be quick, usually a few seconds at most.
|
||||
logger.Info("I'm Stopping!")
|
||||
close(p.exit)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Service setup.
|
||||
// Define service config.
|
||||
// Create the service.
|
||||
// Setup the logger.
|
||||
// Handle service controls (optional).
|
||||
// Run the service.
|
||||
func main() {
|
||||
svcConfig := &service.Config{
|
||||
Name: "GoServiceTest",
|
||||
@@ -60,16 +65,26 @@ func main() {
|
||||
prg := &program{}
|
||||
s, err := service.New(prg, svcConfig)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
log.Fatal(err)
|
||||
}
|
||||
logger, err = s.Logger()
|
||||
errs := make(chan error, 5)
|
||||
logger, err = s.Logger(errs)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
log.Fatal(err)
|
||||
}
|
||||
go func() {
|
||||
for {
|
||||
err := <-errs
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
if len(os.Args) > 1 {
|
||||
err := service.Control(s, os.Args[1])
|
||||
if err != nil {
|
||||
log.Printf("Valid actions: %q\n", service.ControlAction)
|
||||
log.Fatal(err)
|
||||
}
|
||||
return
|
||||
|
||||
+11
-11
@@ -87,12 +87,14 @@ func (kv KeyValue) float64(name string, defaultValue float64) float64 {
|
||||
type System 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
|
||||
}
|
||||
|
||||
// LocalSystem get's the local system information.
|
||||
func LocalSystem() System {
|
||||
return system
|
||||
}
|
||||
var Local System = system
|
||||
|
||||
// Interface represents the service interface for a program. Start runs before
|
||||
// the hosting process is granted control and Stop runs when control is returned.
|
||||
@@ -143,17 +145,15 @@ type Service interface {
|
||||
// greater rights. Will return an error if the service is not present.
|
||||
Remove() error
|
||||
|
||||
// Interactive returns false if running under the OS service manager
|
||||
// and true otherwise.
|
||||
Interactive() bool
|
||||
|
||||
// Opens and returns a system logger. If the user program is running
|
||||
// interactively rather then as a service, the returned logger will write to
|
||||
// os.Stderr.
|
||||
Logger() (Logger, error)
|
||||
// os.Stderr. If errs is non-nil errors will be sent on errs as well as
|
||||
// returned from Logger's functions.
|
||||
Logger(errs chan<- error) (Logger, error)
|
||||
|
||||
// SystemLogger opens and returns a system logger.
|
||||
SystemLogger() (Logger, error)
|
||||
// SystemLogger opens and returns a system logger. If errs is non-nil errors
|
||||
// will be sent on errs as well as returned from Logger's functions.
|
||||
SystemLogger(errs chan<- error) (Logger, error)
|
||||
|
||||
// String displays the name of the service. The display name if present,
|
||||
// otherwise the name.
|
||||
|
||||
+20
-14
@@ -22,8 +22,22 @@ func (ls darwinSystem) String() string {
|
||||
return version
|
||||
}
|
||||
|
||||
func (ls darwinSystem) Interactive() bool {
|
||||
return interactive
|
||||
}
|
||||
|
||||
var system = darwinSystem{}
|
||||
|
||||
var interactive = false
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
interactive, err = isInteractive()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func isInteractive() (bool, error) {
|
||||
// TODO: The PPID of Launchd is 1. The PPid of a service process should match launchd's PID.
|
||||
return os.Getppid() != 1, nil
|
||||
@@ -35,17 +49,12 @@ func newService(i Interface, c *Config) (*darwinLaunchdService, error) {
|
||||
Config: c,
|
||||
}
|
||||
|
||||
var err error
|
||||
s.interactive, err = isInteractive()
|
||||
|
||||
return s, err
|
||||
return s, nil
|
||||
}
|
||||
|
||||
type darwinLaunchdService struct {
|
||||
i Interface
|
||||
*Config
|
||||
|
||||
interactive bool
|
||||
}
|
||||
|
||||
func (s *darwinLaunchdService) String() string {
|
||||
@@ -66,9 +75,6 @@ func (s *darwinLaunchdService) getServiceFilePath() (string, error) {
|
||||
return "/Library/LaunchDaemons/" + s.Name + ".plist", nil
|
||||
}
|
||||
|
||||
func (s *darwinLaunchdService) Interactive() bool {
|
||||
return s.interactive
|
||||
}
|
||||
func (s *darwinLaunchdService) Install() error {
|
||||
confPath, err := s.getServiceFilePath()
|
||||
if err != nil {
|
||||
@@ -166,14 +172,14 @@ func (s *darwinLaunchdService) Run() error {
|
||||
return s.i.Stop(s)
|
||||
}
|
||||
|
||||
func (s *darwinLaunchdService) Logger() (Logger, error) {
|
||||
if s.interactive {
|
||||
func (s *darwinLaunchdService) Logger(errs chan<- error) (Logger, error) {
|
||||
if interactive {
|
||||
return ConsoleLogger, nil
|
||||
}
|
||||
return s.SystemLogger()
|
||||
return s.SystemLogger(errs)
|
||||
}
|
||||
func (s *darwinLaunchdService) SystemLogger() (Logger, error) {
|
||||
return newSysLogger(s.Name)
|
||||
func (s *darwinLaunchdService) SystemLogger(errs chan<- error) (Logger, error) {
|
||||
return newSysLogger(s.Name, errs)
|
||||
}
|
||||
|
||||
var launchdConfig = `<?xml version='1.0' encoding='UTF-8'?>
|
||||
|
||||
+18
-8
@@ -56,6 +56,10 @@ func (ls linuxSystem) String() string {
|
||||
return fmt.Sprintf("Linux %s", flavor.String())
|
||||
}
|
||||
|
||||
func (ls linuxSystem) Interactive() bool {
|
||||
return interactive
|
||||
}
|
||||
|
||||
var system = linuxSystem{}
|
||||
|
||||
func newService(i Interface, c *Config) (Service, error) {
|
||||
@@ -117,15 +121,21 @@ func (f initFlavor) GetTemplate() *template.Template {
|
||||
return template.Must(template.New(f.String() + "Script").Parse(templ))
|
||||
}
|
||||
|
||||
var interactive = false
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
interactive, err = isInteractive()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func isInteractive() (bool, error) {
|
||||
// TODO: Is this true for user services?
|
||||
return os.Getppid() != 1, nil
|
||||
}
|
||||
|
||||
func (s *linuxService) Interactive() bool {
|
||||
return s.interactive
|
||||
}
|
||||
|
||||
func (s *linuxService) Install() error {
|
||||
confPath := flavor.ConfigPath(s.Name)
|
||||
_, err := os.Stat(confPath)
|
||||
@@ -192,14 +202,14 @@ func (s *linuxService) Remove() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *linuxService) Logger() (Logger, error) {
|
||||
func (s *linuxService) Logger(errs chan<- error) (Logger, error) {
|
||||
if s.interactive {
|
||||
return ConsoleLogger, nil
|
||||
}
|
||||
return s.SystemLogger()
|
||||
return s.SystemLogger(errs)
|
||||
}
|
||||
func (s *linuxService) SystemLogger() (Logger, error) {
|
||||
return newSysLogger(s.Name)
|
||||
func (s *linuxService) SystemLogger(errs chan<- error) (Logger, error) {
|
||||
return newSysLogger(s.Name, errs)
|
||||
}
|
||||
|
||||
func (s *linuxService) Run() (err error) {
|
||||
|
||||
+16
-8
@@ -9,33 +9,41 @@ import (
|
||||
"log/syslog"
|
||||
)
|
||||
|
||||
func newSysLogger(name string) (Logger, error) {
|
||||
func newSysLogger(name string, errs chan<- error) (Logger, error) {
|
||||
w, err := syslog.New(syslog.LOG_INFO, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return sysLogger{w}, nil
|
||||
return sysLogger{w, errs}, nil
|
||||
}
|
||||
|
||||
type sysLogger struct {
|
||||
*syslog.Writer
|
||||
errs chan<- error
|
||||
}
|
||||
|
||||
func (s sysLogger) send(err error) error {
|
||||
if err != nil && s.errs != nil {
|
||||
s.errs <- err
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (s sysLogger) Error(v ...interface{}) error {
|
||||
return s.Writer.Err(fmt.Sprint(v...))
|
||||
return s.send(s.Writer.Err(fmt.Sprint(v...)))
|
||||
}
|
||||
func (s sysLogger) Warning(v ...interface{}) error {
|
||||
return s.Writer.Warning(fmt.Sprint(v...))
|
||||
return s.send(s.Writer.Warning(fmt.Sprint(v...)))
|
||||
}
|
||||
func (s sysLogger) Info(v ...interface{}) error {
|
||||
return s.Writer.Info(fmt.Sprint(v...))
|
||||
return s.send(s.Writer.Info(fmt.Sprint(v...)))
|
||||
}
|
||||
func (s sysLogger) Errorf(format string, a ...interface{}) error {
|
||||
return s.Writer.Err(fmt.Sprintf(format, a...))
|
||||
return s.send(s.Writer.Err(fmt.Sprintf(format, a...)))
|
||||
}
|
||||
func (s sysLogger) Warningf(format string, a ...interface{}) error {
|
||||
return s.Writer.Warning(fmt.Sprintf(format, a...))
|
||||
return s.send(s.Writer.Warning(fmt.Sprintf(format, a...)))
|
||||
}
|
||||
func (s sysLogger) Infof(format string, a ...interface{}) error {
|
||||
return s.Writer.Info(fmt.Sprintf(format, a...))
|
||||
return s.send(s.Writer.Info(fmt.Sprintf(format, a...)))
|
||||
}
|
||||
|
||||
+41
-30
@@ -17,13 +17,12 @@ const version = "Windows Service"
|
||||
type windowsService struct {
|
||||
i Interface
|
||||
*Config
|
||||
|
||||
interactive bool
|
||||
}
|
||||
|
||||
// WindowsLogger allows using windows specific logging methods.
|
||||
type WindowsLogger struct {
|
||||
ev *eventlog.Log
|
||||
ev *eventlog.Log
|
||||
errs chan<- error
|
||||
}
|
||||
|
||||
type windowsSystem struct{}
|
||||
@@ -31,49 +30,67 @@ type windowsSystem struct{}
|
||||
func (windowsSystem) String() string {
|
||||
return version
|
||||
}
|
||||
func (windowsSystem) Interactive() bool {
|
||||
return interactive
|
||||
}
|
||||
|
||||
var system = windowsSystem{}
|
||||
|
||||
func (l WindowsLogger) send(err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
if l.errs != nil {
|
||||
l.errs <- err
|
||||
}
|
||||
return err
|
||||
}
|
||||
func (l WindowsLogger) Error(v ...interface{}) error {
|
||||
return l.ev.Error(3, fmt.Sprint(v...))
|
||||
return l.send(l.ev.Error(3, fmt.Sprint(v...)))
|
||||
}
|
||||
func (l WindowsLogger) Warning(v ...interface{}) error {
|
||||
return l.ev.Warning(2, fmt.Sprint(v...))
|
||||
return l.send(l.ev.Warning(2, fmt.Sprint(v...)))
|
||||
}
|
||||
func (l WindowsLogger) Info(v ...interface{}) error {
|
||||
return l.ev.Info(1, fmt.Sprint(v...))
|
||||
return l.send(l.ev.Info(1, fmt.Sprint(v...)))
|
||||
}
|
||||
func (l WindowsLogger) Errorf(format string, a ...interface{}) error {
|
||||
return l.ev.Error(3, fmt.Sprintf(format, a...))
|
||||
return l.send(l.ev.Error(3, fmt.Sprintf(format, a...)))
|
||||
}
|
||||
func (l WindowsLogger) Warningf(format string, a ...interface{}) error {
|
||||
return l.ev.Warning(2, fmt.Sprintf(format, a...))
|
||||
return l.send(l.ev.Warning(2, fmt.Sprintf(format, a...)))
|
||||
}
|
||||
func (l WindowsLogger) Infof(format string, a ...interface{}) error {
|
||||
return l.ev.Info(1, fmt.Sprintf(format, a...))
|
||||
return l.send(l.ev.Info(1, fmt.Sprintf(format, a...)))
|
||||
}
|
||||
|
||||
func (l WindowsLogger) NError(eventId uint32, v ...interface{}) error {
|
||||
return l.ev.Error(eventId, fmt.Sprint(v...))
|
||||
return l.send(l.ev.Error(eventId, fmt.Sprint(v...)))
|
||||
}
|
||||
func (l WindowsLogger) NWarning(eventId uint32, v ...interface{}) error {
|
||||
return l.ev.Warning(eventId, fmt.Sprint(v...))
|
||||
return l.send(l.ev.Warning(eventId, fmt.Sprint(v...)))
|
||||
}
|
||||
func (l WindowsLogger) NInfo(eventId uint32, v ...interface{}) error {
|
||||
return l.ev.Info(eventId, fmt.Sprint(v...))
|
||||
return l.send(l.ev.Info(eventId, fmt.Sprint(v...)))
|
||||
}
|
||||
func (l WindowsLogger) NErrorf(eventId uint32, format string, a ...interface{}) error {
|
||||
return l.ev.Error(eventId, fmt.Sprintf(format, a...))
|
||||
return l.send(l.ev.Error(eventId, fmt.Sprintf(format, a...)))
|
||||
}
|
||||
func (l WindowsLogger) NWarningf(eventId uint32, format string, a ...interface{}) error {
|
||||
return l.ev.Warning(eventId, fmt.Sprintf(format, a...))
|
||||
return l.send(l.ev.Warning(eventId, fmt.Sprintf(format, a...)))
|
||||
}
|
||||
func (l WindowsLogger) NInfof(eventId uint32, format string, a ...interface{}) error {
|
||||
return l.ev.Info(eventId, fmt.Sprintf(format, a...))
|
||||
return l.send(l.ev.Info(eventId, fmt.Sprintf(format, a...)))
|
||||
}
|
||||
|
||||
func isInteractive() (bool, error) {
|
||||
return svc.IsAnInteractiveSession()
|
||||
var interactive = false
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
interactive, err = svc.IsAnInteractiveSession()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func newService(i Interface, c *Config) (*windowsService, error) {
|
||||
@@ -81,9 +98,7 @@ func newService(i Interface, c *Config) (*windowsService, error) {
|
||||
i: i,
|
||||
Config: c,
|
||||
}
|
||||
var err error
|
||||
ws.interactive, err = isInteractive()
|
||||
return ws, err
|
||||
return ws, nil
|
||||
}
|
||||
|
||||
func (ws *windowsService) String() string {
|
||||
@@ -126,10 +141,6 @@ loop:
|
||||
return false, 0
|
||||
}
|
||||
|
||||
func (ws *windowsService) Interactive() bool {
|
||||
return ws.interactive
|
||||
}
|
||||
|
||||
func (ws *windowsService) Install() error {
|
||||
exepath, err := osext.Executable()
|
||||
if err != nil {
|
||||
@@ -187,7 +198,7 @@ func (ws *windowsService) Remove() error {
|
||||
}
|
||||
|
||||
func (ws *windowsService) Run() error {
|
||||
if !ws.interactive {
|
||||
if !interactive {
|
||||
return svc.Run(ws.Name, ws)
|
||||
}
|
||||
err := ws.i.Start(ws)
|
||||
@@ -243,16 +254,16 @@ func (ws *windowsService) Restart() error {
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
return ws.Start()
|
||||
}
|
||||
func (ws *windowsService) Logger() (Logger, error) {
|
||||
if ws.interactive {
|
||||
func (ws *windowsService) Logger(errs chan<- error) (Logger, error) {
|
||||
if interactive {
|
||||
return ConsoleLogger, nil
|
||||
}
|
||||
return ws.SystemLogger()
|
||||
return ws.SystemLogger(errs)
|
||||
}
|
||||
func (ws *windowsService) SystemLogger() (Logger, error) {
|
||||
func (ws *windowsService) SystemLogger(errs chan<- error) (Logger, error) {
|
||||
el, err := eventlog.Open(ws.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return WindowsLogger{el}, nil
|
||||
return WindowsLogger{el, errs}, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user