service: update api.

This commit is contained in:
Daniel Theophanes
2015-01-13 21:40:07 -08:00
parent 071b50c909
commit bc48b26bdb
7 changed files with 134 additions and 84 deletions
BIN
View File
Binary file not shown.
+28 -13
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
}