You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
354 lines
7.3 KiB
Go
354 lines
7.3 KiB
Go
3 years ago
|
package wincmd
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"golang.org/x/sys/windows"
|
||
|
"golang.org/x/sys/windows/svc"
|
||
|
"golang.org/x/sys/windows/svc/eventlog"
|
||
|
"golang.org/x/sys/windows/svc/mgr"
|
||
|
"time"
|
||
|
)
|
||
|
|
||
|
type SvcStatus svc.State
|
||
|
|
||
|
const (
|
||
|
Stopped = SvcStatus(svc.Stopped)
|
||
|
StartPending = SvcStatus(svc.StartPending)
|
||
|
StopPending = SvcStatus(svc.StopPending)
|
||
|
Running = SvcStatus(svc.Running)
|
||
|
ContinuePending = SvcStatus(svc.ContinuePending)
|
||
|
PausePending = SvcStatus(svc.PausePending)
|
||
|
Paused = SvcStatus(svc.Paused)
|
||
|
|
||
|
StartManual = windows.SERVICE_DEMAND_START // the service must be started manually
|
||
|
StartAutomatic = windows.SERVICE_AUTO_START // the service will start by itself whenever the computer reboots
|
||
|
StartDisabled = windows.SERVICE_DISABLED // the service cannot be started
|
||
|
|
||
|
// The severity of the error, and action taken,
|
||
|
// if this service fails to start.
|
||
|
ErrorCritical = windows.SERVICE_ERROR_CRITICAL
|
||
|
ErrorIgnore = windows.SERVICE_ERROR_IGNORE
|
||
|
ErrorNormal = windows.SERVICE_ERROR_NORMAL
|
||
|
ErrorSevere = windows.SERVICE_ERROR_SEVERE
|
||
|
)
|
||
|
|
||
|
type WinSvcExecute struct {
|
||
|
Run func()
|
||
|
Stop func()
|
||
|
Interrupt func()
|
||
|
Pause func()
|
||
|
Continue func()
|
||
|
OtherMethod func(svc.Cmd)
|
||
|
Name string
|
||
|
Accepted []svc.Accepted
|
||
|
}
|
||
|
|
||
|
type WinSvcInput struct {
|
||
|
Name string
|
||
|
DisplayName string
|
||
|
ExecPath string
|
||
|
DelayedAutoStart bool
|
||
|
Description string
|
||
|
StartType uint32
|
||
|
Args []string
|
||
|
}
|
||
|
|
||
|
type WinSvc struct {
|
||
|
*mgr.Service
|
||
|
}
|
||
|
|
||
|
func IsServiceExists(name string) (bool, error) {
|
||
|
if !Isas() {
|
||
|
return false, errors.New("permission deny")
|
||
|
}
|
||
|
winmgr, err := mgr.Connect()
|
||
|
if err != nil {
|
||
|
return false, err
|
||
|
}
|
||
|
defer winmgr.Disconnect()
|
||
|
lists, err := winmgr.ListServices()
|
||
|
if err != nil {
|
||
|
return false, err
|
||
|
}
|
||
|
for _, v := range lists {
|
||
|
if name == v {
|
||
|
return true, nil
|
||
|
}
|
||
|
}
|
||
|
return false, nil
|
||
|
}
|
||
|
|
||
|
func CreateService(mysvc WinSvcInput) (*WinSvc, error) {
|
||
|
if !Isas() {
|
||
|
return nil, errors.New("permission deny")
|
||
|
}
|
||
|
if exists, err := IsServiceExists(mysvc.Name); err != nil {
|
||
|
return nil, err
|
||
|
} else if exists {
|
||
|
return nil, errors.New("service already exists")
|
||
|
}
|
||
|
winmgr, err := mgr.Connect()
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
defer winmgr.Disconnect()
|
||
|
mycfg := mgr.Config{
|
||
|
DisplayName: mysvc.DisplayName,
|
||
|
StartType: mysvc.StartType,
|
||
|
DelayedAutoStart: mysvc.DelayedAutoStart,
|
||
|
Description: mysvc.Description,
|
||
|
}
|
||
|
gsvc, err := winmgr.CreateService(mysvc.Name, mysvc.ExecPath, mycfg, mysvc.Args...)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
err = eventlog.InstallAsEventCreate(mysvc.Name, eventlog.Error|eventlog.Warning|eventlog.Info)
|
||
|
if err != nil {
|
||
|
gsvc.Delete()
|
||
|
return nil, fmt.Errorf("winsvc.InstallService: InstallAsEventCreate failed, err = %v", err)
|
||
|
}
|
||
|
var result WinSvc
|
||
|
result.Service = gsvc
|
||
|
return &result, nil
|
||
|
}
|
||
|
|
||
|
func OpenService(name string) (*WinSvc, error) {
|
||
|
if !Isas() {
|
||
|
return nil, errors.New("permission deny")
|
||
|
}
|
||
|
if exists, err := IsServiceExists(name); err != nil {
|
||
|
return nil, err
|
||
|
} else if !exists {
|
||
|
return nil, errors.New("service not exists")
|
||
|
}
|
||
|
winmgr, err := mgr.Connect()
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
defer winmgr.Disconnect()
|
||
|
gsvc, err := winmgr.OpenService(name)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
var result WinSvc
|
||
|
result.Service = gsvc
|
||
|
return &result, nil
|
||
|
}
|
||
|
|
||
|
func DeleteService(name string) error {
|
||
|
mysvc, err := OpenService(name)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
err = mysvc.Service.Delete()
|
||
|
if err != nil {
|
||
|
mysvc.Close()
|
||
|
return err
|
||
|
}
|
||
|
mysvc.Close()
|
||
|
err = eventlog.Remove(name)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
var count int
|
||
|
for {
|
||
|
if ok, err := IsServiceExists(name); err != nil {
|
||
|
return err
|
||
|
} else if !ok {
|
||
|
return nil
|
||
|
}
|
||
|
time.Sleep(time.Millisecond * 300)
|
||
|
count++
|
||
|
if count > 100 {
|
||
|
return errors.New("timeout")
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func StopService(name string) error {
|
||
|
mysvc, err := OpenService(name)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
defer mysvc.Close()
|
||
|
_, err = mysvc.Service.Control(svc.Stop)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
var count int
|
||
|
for {
|
||
|
status, err := mysvc.Service.Query()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
if status.State == svc.Stopped {
|
||
|
return nil
|
||
|
}
|
||
|
time.Sleep(time.Millisecond * 100)
|
||
|
count++
|
||
|
if count > 100 {
|
||
|
return errors.New("timeout")
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func StartService(name string) error {
|
||
|
mysvc, err := OpenService(name)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
defer mysvc.Close()
|
||
|
err = mysvc.Service.Start()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
var count int
|
||
|
for {
|
||
|
status, err := mysvc.Service.Query()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
if status.State == svc.Running {
|
||
|
return nil
|
||
|
}
|
||
|
time.Sleep(time.Millisecond * 100)
|
||
|
count++
|
||
|
if count > 100 {
|
||
|
return errors.New("timeout")
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func ServiceStatus(name string) (SvcStatus, error) {
|
||
|
mysvc, err := OpenService(name)
|
||
|
if err != nil {
|
||
|
return Stopped, err
|
||
|
}
|
||
|
defer mysvc.Close()
|
||
|
status, err := mysvc.Service.Query()
|
||
|
return SvcStatus(status.State), err
|
||
|
}
|
||
|
|
||
|
func InService() (bool, error) {
|
||
|
if !Isas() {
|
||
|
return false, nil
|
||
|
}
|
||
|
return svc.IsWindowsService()
|
||
|
}
|
||
|
|
||
|
func (w *WinSvc) Stop() error {
|
||
|
return StopService(w.Name)
|
||
|
}
|
||
|
|
||
|
func (w *WinSvc) Delete() error {
|
||
|
if err := w.Close(); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
return DeleteService(w.Name)
|
||
|
}
|
||
|
|
||
|
func (w *WinSvc) StartService() error {
|
||
|
err := w.Service.Start()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
var count int
|
||
|
for {
|
||
|
sts, err := w.Query()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
if SvcStatus(sts.State) == Running {
|
||
|
return nil
|
||
|
}
|
||
|
time.Sleep(time.Millisecond * 100)
|
||
|
count++
|
||
|
if count > 100 {
|
||
|
return errors.New("timeout")
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func InServiceBool() bool {
|
||
|
ok, _ := svc.IsWindowsService()
|
||
|
return ok
|
||
|
}
|
||
|
|
||
|
func (w *WinSvcExecute) Execute(args []string, r <-chan svc.ChangeRequest, s chan<- svc.Status) (svcSpecificEC bool, exitCode uint32) {
|
||
|
var sva svc.Accepted
|
||
|
alreadyStoped := make(chan int)
|
||
|
for _, v := range w.Accepted {
|
||
|
sva = sva | v
|
||
|
}
|
||
|
s <- svc.Status{State: svc.StartPending}
|
||
|
go func() {
|
||
|
s <- svc.Status{State: svc.Running, Accepts: sva}
|
||
|
w.Run()
|
||
|
alreadyStoped <- 1
|
||
|
}()
|
||
|
|
||
|
for {
|
||
|
select {
|
||
|
case <-alreadyStoped:
|
||
|
return
|
||
|
case c := <-r:
|
||
|
switch c.Cmd {
|
||
|
case svc.Interrogate:
|
||
|
s <- c.CurrentStatus
|
||
|
w.Interrupt()
|
||
|
s <- c.CurrentStatus
|
||
|
case svc.Stop, svc.Shutdown:
|
||
|
s <- svc.Status{State: svc.StopPending}
|
||
|
w.Stop()
|
||
|
s <- svc.Status{State: svc.Stopped}
|
||
|
return
|
||
|
case svc.Pause:
|
||
|
s <- svc.Status{State: svc.PausePending, Accepts: sva}
|
||
|
if w.Pause != nil {
|
||
|
w.Pause()
|
||
|
}
|
||
|
s <- svc.Status{State: svc.Paused, Accepts: sva}
|
||
|
case svc.Continue:
|
||
|
s <- svc.Status{State: svc.ContinuePending, Accepts: sva}
|
||
|
if w.Continue != nil {
|
||
|
w.Continue()
|
||
|
}
|
||
|
s <- svc.Status{State: svc.Running, Accepts: sva}
|
||
|
default:
|
||
|
if w.OtherMethod != nil {
|
||
|
w.OtherMethod(c.Cmd)
|
||
|
}
|
||
|
s <- svc.Status{State: svc.Running, Accepts: sva}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func NewWinSvcExecute(name string, run, stop func()) *WinSvcExecute {
|
||
|
var res WinSvcExecute
|
||
|
res.Run = run
|
||
|
res.Stop = stop
|
||
|
res.Interrupt = func() {
|
||
|
time.Sleep(time.Millisecond)
|
||
|
}
|
||
|
res.Accepted = []svc.Accepted{svc.AcceptStop, svc.AcceptShutdown, svc.AcceptPauseAndContinue}
|
||
|
return &res
|
||
|
}
|
||
|
|
||
|
func (w *WinSvcExecute) StartService() error {
|
||
|
return svc.Run(w.Name, w)
|
||
|
}
|
||
|
|
||
|
func (w *WinSvcExecute) InService() (bool, error) {
|
||
|
if !Isas() {
|
||
|
return false, nil
|
||
|
}
|
||
|
return svc.IsWindowsService()
|
||
|
}
|
||
|
|
||
|
func (w *WinSvcExecute) InServiceBool() bool {
|
||
|
ok, _ := svc.IsWindowsService()
|
||
|
return ok
|
||
|
}
|