agent/util/serviceutil/serviceutil_windows.go (104 lines of code) (raw):
//go:build windows
// +build windows
package serviceutil
import (
"strconv"
"time"
"github.com/aliyun/aliyun_assist_client/agent/util/process"
"github.com/aliyun/aliyun_assist_client/thirdparty/sirupsen/logrus"
"golang.org/x/sys/windows/registry"
"golang.org/x/sys/windows/svc"
"golang.org/x/sys/windows/svc/mgr"
)
// RestartService restarts Agent service, the Agent service process itself
// should not call this function
func RestartAgentService(logger logrus.FieldLogger) {
s, err := getService()
if err != nil {
logger.WithError(err).Error("failed to get service handle")
goto RESTARTBYCOMMAND
}
if err = StopWait(s); err != nil {
logger.WithError(err).Error("failed to stop service by service handle")
goto RESTARTBYCOMMAND
}
if err = s.Start(); err != nil {
logger.WithError(err).Error("failed to start service by service handle")
goto RESTARTBYCOMMAND
}
return
RESTARTBYCOMMAND:
logger.Info("try to restart service by command")
if err := restartServiceByCommand(); err != nil {
logger.WithError(err).Error("failed to restart service by command")
}
}
// StopAgentService stops Agent service
func StopAgentService(logger logrus.FieldLogger) {
s, err := getService()
if err != nil {
logger.WithError(err).Error("failed to get service handle")
goto STOPBYCOMMAND
}
if err = StopWait(s); err != nil {
logger.WithError(err).Error("failed to stop service by service handle")
goto STOPBYCOMMAND
}
return
STOPBYCOMMAND:
logger.Info("try to stop service by command")
if err := stopServiceByCommand(); err != nil {
logger.WithError(err).Error("failed to stop service by command")
}
}
func stopServiceByCommand() error {
processer := process.ProcessCmd{}
return processer.SyncRunSimple("net", []string{"stop", "AliyunService"}, 10)
}
func restartServiceByCommand() error {
processer := process.ProcessCmd{}
processer.SyncRunSimple("net", []string{"stop", "AliyunService"}, 10)
return processer.SyncRunSimple("net", []string{"start", "AliyunService"}, 10)
}
func getService() (*mgr.Service, error) {
m, err := mgr.Connect()
if err != nil {
return nil, err
}
defer m.Disconnect()
return m.OpenService("AliyunService")
}
func StopWait(s *mgr.Service) error {
// First stop the service. Then wait for the service to
// actually stop before starting it.
status, err := s.Control(svc.Stop)
if err != nil {
return err
}
timeDuration := time.Millisecond * 50
timeout := time.After(getStopTimeout() + (timeDuration * 2))
tick := time.NewTicker(timeDuration)
defer tick.Stop()
for status.State != svc.Stopped {
select {
case <-tick.C:
status, err = s.Query()
if err != nil {
return err
}
case <-timeout:
break
}
}
return nil
}
// getStopTimeout fetches the time before windows will kill the service.
func getStopTimeout() time.Duration {
// For default and paths see https://support.microsoft.com/en-us/kb/146092
defaultTimeout := time.Millisecond * 20000
key, err := registry.OpenKey(registry.LOCAL_MACHINE, `SYSTEM\CurrentControlSet\Control`, registry.READ)
if err != nil {
return defaultTimeout
}
defer key.Close()
sv, _, err := key.GetStringValue("WaitToKillServiceTimeout")
if err != nil {
return defaultTimeout
}
v, err := strconv.Atoi(sv)
if err != nil {
return defaultTimeout
}
return time.Millisecond * time.Duration(v)
}