// RAINBOND, Application Management Platform // Copyright (C) 2014-2017 Goodrain Co., Ltd. // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. For any non-GPL usage of Rainbond, // one or multiple Commercial Licenses authorized by Goodrain Co., Ltd. // must be obtained first. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see . package windows import ( "fmt" "time" "unsafe" "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" ) //RegisterService register a service func RegisterService(serviceName, execPath, displayName string, depends, args []string) error { m, err := mgr.Connect() if err != nil { return err } defer m.Disconnect() c := mgr.Config{ ServiceType: windows.SERVICE_WIN32_OWN_PROCESS, StartType: mgr.StartAutomatic, ErrorControl: mgr.ErrorNormal, Dependencies: depends, DisplayName: displayName, } s, err := m.CreateService(serviceName, execPath, c, args...) if err != nil { return err } defer s.Close() // See http://stackoverflow.com/questions/35151052/how-do-i-configure-failure-actions-of-a-windows-service-written-in-go const ( scActionNone = 0 scActionRestart = 1 scActionReboot = 2 scActionRunCommand = 3 serviceConfigFailureActions = 2 ) type serviceFailureActions struct { ResetPeriod uint32 RebootMsg *uint16 Command *uint16 ActionsCount uint32 Actions uintptr } type scAction struct { Type uint32 Delay uint32 } t := []scAction{ {Type: scActionRestart, Delay: uint32(60 * time.Second / time.Millisecond)}, {Type: scActionRestart, Delay: uint32(60 * time.Second / time.Millisecond)}, {Type: scActionNone}, } lpInfo := serviceFailureActions{ResetPeriod: uint32(24 * time.Hour / time.Second), ActionsCount: uint32(3), Actions: uintptr(unsafe.Pointer(&t[0]))} err = windows.ChangeServiceConfig2(s.Handle, serviceConfigFailureActions, (*byte)(unsafe.Pointer(&lpInfo))) if err != nil { return err } return eventlog.Install(serviceName, execPath, false, eventlog.Info|eventlog.Warning|eventlog.Error) } //UnRegisterService unres func UnRegisterService(serviceName string) error { m, err := mgr.Connect() if err != nil { return err } defer m.Disconnect() s, err := m.OpenService(serviceName) if err != nil { return err } defer s.Close() eventlog.Remove(serviceName) err = s.Delete() if err != nil { return err } return nil } //StartService start a windows service func StartService(serviceName string) error { m, err := mgr.Connect() if err != nil { return err } defer m.Disconnect() s, err := m.OpenService(serviceName) if err != nil { return fmt.Errorf("could not open service: %v", err) } defer s.Close() err = s.Start() if err != nil { return fmt.Errorf("could not start service: %v", err) } return nil } //StopService stop a windows service func StopService(serviceName string) error { return controlService(serviceName, svc.Stop, svc.Stopped) } //RestartService restart a windows service func RestartService(serviceName string) error { if err := controlService(serviceName, svc.Stop, svc.Stopped); err != nil { return err } return StartService(serviceName) } func controlService(name string, c svc.Cmd, to svc.State) error { m, err := mgr.Connect() if err != nil { return err } defer m.Disconnect() s, err := m.OpenService(name) if err != nil { return fmt.Errorf("could not access service: %v", err) } defer s.Close() status, err := s.Control(c) if err != nil { return fmt.Errorf("could not send control=%d: %v", c, err) } timeout := time.Now().Add(10 * time.Second) for status.State != to { if timeout.Before(time.Now()) { return fmt.Errorf("timeout waiting for service to go to state=%d", to) } time.Sleep(300 * time.Millisecond) status, err = s.Query() if err != nil { return fmt.Errorf("could not retrieve service status: %v", err) } } return nil }