Files
service/service_windows.go
T
2012-08-22 15:51:49 -07:00

169 lines
3.7 KiB
Go

package service
import (
"syscall"
"unicode/utf16"
"fmt"
"unsafe"
"code.google.com/p/winsvc/svc"
"code.google.com/p/winsvc/mgr"
"code.google.com/p/winsvc/eventlog"
)
func newService(name, displayName, description string) (*windowsService, error) {
return &windowsService{
name: name,
displayName: displayName,
description: description,
}, nil
}
type windowsService struct {
name, displayName, description string
onStart, onStop func() error
logger *eventlog.Log
}
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.LogError(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.LogError(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 := getExePath()
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 {
return fmt.Errorf("service %s is not installed", ws.name)
}
defer s.Close()
err = s.Delete()
if err != nil {
return err
}
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) LogError(format string, a ...interface{}) error {
if ws.logger == nil {
return nil
}
return ws.logger.Error(3, fmt.Sprintf(format, a...))
}
func (ws *windowsService) LogWarning(format string, a ...interface{}) error {
if ws.logger == nil {
return nil
}
return ws.logger.Warning(2, fmt.Sprintf(format, a...))
}
func (ws *windowsService) LogInfo(format string, a ...interface{}) error {
if ws.logger == nil {
return nil
}
return ws.logger.Info(1, fmt.Sprintf(format, a...))
}
func getExePath() (exePath string, err error) {
return getModuleFileName()
}
var (
kernel = syscall.MustLoadDLL("kernel32.dll")
// kernel32.dll
getModuleFileNameProc = kernel.MustFindProc("GetModuleFileNameW")
)
func getModuleFileName() (string, error) {
var n uint32
b := make([]uint16, syscall.MAX_PATH)
size := uint32(len(b))
r0, _, e1 := getModuleFileNameProc.Call(0, uintptr(unsafe.Pointer(&b[0])), uintptr(size))
n = uint32(r0)
if n == 0 {
return "", e1
}
return string(utf16.Decode(b[0:n])), nil
}