зеркало из https://github.com/mozilla/mig.git
215 строки
4.4 KiB
Go
215 строки
4.4 KiB
Go
package service /* import "mig.ninja/mig/service" */
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/kardianos/osext"
|
|
"golang.org/x/sys/windows/svc"
|
|
"golang.org/x/sys/windows/svc/eventlog"
|
|
"golang.org/x/sys/windows/svc/mgr"
|
|
)
|
|
|
|
func newService(c *Config) (*windowsService, error) {
|
|
return &windowsService{
|
|
name: c.Name,
|
|
displayName: c.DisplayName,
|
|
description: c.Description,
|
|
}, nil
|
|
}
|
|
|
|
type windowsService struct {
|
|
name, displayName, description string
|
|
onStart, onStop func() error
|
|
logger *eventlog.Log
|
|
}
|
|
|
|
const version = "Windows Service"
|
|
|
|
var interactive = false
|
|
|
|
func init() {
|
|
var err error
|
|
interactive, err = svc.IsAnInteractiveSession()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
func IsInteractive() bool {
|
|
return interactive
|
|
}
|
|
|
|
func (ws *windowsService) String() string {
|
|
return version
|
|
}
|
|
|
|
func (ws *windowsService) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (ssec bool, errno uint32) {
|
|
const cmdsAccepted = svc.AcceptStop | svc.AcceptShutdown
|
|
changes <- svc.Status{State: svc.StartPending}
|
|
|
|
if err := ws.onStart(); err != nil {
|
|
ws.Error(err.Error())
|
|
return true, 1
|
|
}
|
|
|
|
changes <- svc.Status{State: svc.Running, Accepts: cmdsAccepted}
|
|
loop:
|
|
for {
|
|
c := <-r
|
|
switch c.Cmd {
|
|
case svc.Interrogate:
|
|
changes <- c.CurrentStatus
|
|
case svc.Stop, svc.Shutdown:
|
|
changes <- svc.Status{State: svc.StopPending}
|
|
if err := ws.onStop(); err != nil {
|
|
ws.Error(err.Error())
|
|
changes <- svc.Status{State: svc.Running, Accepts: cmdsAccepted}
|
|
continue loop
|
|
}
|
|
break loop
|
|
default:
|
|
continue loop
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (ws *windowsService) Install() error {
|
|
exepath, err := osext.Executable()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
m, err := mgr.Connect()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer m.Disconnect()
|
|
s, err := m.OpenService(ws.name)
|
|
if err == nil {
|
|
s.Close()
|
|
return fmt.Errorf("service %s already exists", ws.name)
|
|
}
|
|
s, err = m.CreateService(ws.name, exepath, mgr.Config{
|
|
DisplayName: ws.displayName,
|
|
Description: ws.description,
|
|
StartType: mgr.StartAutomatic,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer s.Close()
|
|
err = eventlog.InstallAsEventCreate(ws.name, eventlog.Error|eventlog.Warning|eventlog.Info)
|
|
if err != nil {
|
|
s.Delete()
|
|
return fmt.Errorf("InstallAsEventCreate() failed: %s", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (ws *windowsService) Remove() error {
|
|
m, err := mgr.Connect()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer m.Disconnect()
|
|
s, err := m.OpenService(ws.name)
|
|
if err != nil {
|
|
// Try to remove the eventlog source as well here, just to ensure we are not
|
|
// in a situation where the eventlog object exists but the service does not
|
|
eventlog.Remove(ws.name)
|
|
return fmt.Errorf("service %s is not installed", ws.name)
|
|
}
|
|
err = s.Delete()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
s.Close()
|
|
cutoff := time.Now().Add(time.Second * 30)
|
|
for {
|
|
s2, err := m.OpenService(ws.name)
|
|
if err != nil {
|
|
break
|
|
}
|
|
s2.Close()
|
|
time.Sleep(time.Millisecond * 250)
|
|
if time.Now().After(cutoff) {
|
|
break
|
|
}
|
|
}
|
|
err = eventlog.Remove(ws.name)
|
|
if err != nil {
|
|
return fmt.Errorf("RemoveEventLogSource() failed: %s", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (ws *windowsService) Run(onStart, onStop func() error) error {
|
|
elog, err := eventlog.Open(ws.name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer elog.Close()
|
|
|
|
ws.logger = elog
|
|
|
|
ws.onStart = onStart
|
|
ws.onStop = onStop
|
|
return svc.Run(ws.name, ws)
|
|
}
|
|
|
|
func (ws *windowsService) Start() error {
|
|
m, err := mgr.Connect()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer m.Disconnect()
|
|
|
|
s, err := m.OpenService(ws.name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer s.Close()
|
|
return s.Start()
|
|
}
|
|
|
|
func (ws *windowsService) Stop() error {
|
|
m, err := mgr.Connect()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer m.Disconnect()
|
|
|
|
s, err := m.OpenService(ws.name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer s.Close()
|
|
_, err = s.Control(svc.Stop)
|
|
return err
|
|
}
|
|
|
|
func (s *windowsService) IntervalMode(interval int) error {
|
|
return fmt.Errorf("interval mode service only supported on darwin")
|
|
}
|
|
|
|
func (ws *windowsService) Error(format string, a ...interface{}) error {
|
|
if ws.logger == nil {
|
|
return nil
|
|
}
|
|
return ws.logger.Error(3, fmt.Sprintf(format, a...))
|
|
}
|
|
func (ws *windowsService) Warning(format string, a ...interface{}) error {
|
|
if ws.logger == nil {
|
|
return nil
|
|
}
|
|
return ws.logger.Warning(2, fmt.Sprintf(format, a...))
|
|
}
|
|
func (ws *windowsService) Info(format string, a ...interface{}) error {
|
|
if ws.logger == nil {
|
|
return nil
|
|
}
|
|
return ws.logger.Info(1, fmt.Sprintf(format, a...))
|
|
}
|