v2.0.0
This commit is contained in:
commit
e455af7f1f
33 changed files with 8471 additions and 0 deletions
307
agent/services_windows.go
Normal file
307
agent/services_windows.go
Normal file
|
|
@ -0,0 +1,307 @@
|
|||
/*
|
||||
Copyright 2022 AmidaWare LLC.
|
||||
|
||||
Licensed under the Tactical RMM License Version 1.0 (the “License”).
|
||||
You may only use the Licensed Software in accordance with the License.
|
||||
A copy of the License is available at:
|
||||
|
||||
https://license.tacticalrmm.com
|
||||
|
||||
*/
|
||||
|
||||
package agent
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
rmm "github.com/amidaware/rmmagent/shared"
|
||||
trmm "github.com/wh1te909/trmm-shared"
|
||||
"golang.org/x/sys/windows/svc"
|
||||
"golang.org/x/sys/windows/svc/mgr"
|
||||
)
|
||||
|
||||
func GetServiceStatus(name string) (string, error) {
|
||||
conn, err := mgr.Connect()
|
||||
if err != nil {
|
||||
return "n/a", err
|
||||
}
|
||||
defer conn.Disconnect()
|
||||
|
||||
srv, err := conn.OpenService(name)
|
||||
if err != nil {
|
||||
return "n/a", err
|
||||
}
|
||||
defer srv.Close()
|
||||
|
||||
q, err := srv.Query()
|
||||
if err != nil {
|
||||
return "n/a", err
|
||||
}
|
||||
|
||||
return serviceStatusText(uint32(q.State)), nil
|
||||
}
|
||||
|
||||
func (a *Agent) ControlService(name, action string) rmm.WinSvcResp {
|
||||
conn, err := mgr.Connect()
|
||||
if err != nil {
|
||||
return rmm.WinSvcResp{Success: false, ErrorMsg: err.Error()}
|
||||
}
|
||||
defer conn.Disconnect()
|
||||
|
||||
srv, err := conn.OpenService(name)
|
||||
if err != nil {
|
||||
return rmm.WinSvcResp{Success: false, ErrorMsg: err.Error()}
|
||||
}
|
||||
defer srv.Close()
|
||||
|
||||
var status svc.Status
|
||||
switch action {
|
||||
|
||||
case "stop":
|
||||
status, err = srv.Control(svc.Stop)
|
||||
if err != nil {
|
||||
return rmm.WinSvcResp{Success: false, ErrorMsg: err.Error()}
|
||||
}
|
||||
timeout := time.Now().Add(30 * time.Second)
|
||||
for status.State != svc.Stopped {
|
||||
if timeout.Before(time.Now()) {
|
||||
return rmm.WinSvcResp{Success: false, ErrorMsg: "Timed out waiting for service to stop"}
|
||||
}
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
status, err = srv.Query()
|
||||
if err != nil {
|
||||
return rmm.WinSvcResp{Success: false, ErrorMsg: err.Error()}
|
||||
}
|
||||
}
|
||||
return rmm.WinSvcResp{Success: true, ErrorMsg: ""}
|
||||
|
||||
case "start":
|
||||
err := srv.Start()
|
||||
if err != nil {
|
||||
return rmm.WinSvcResp{Success: false, ErrorMsg: err.Error()}
|
||||
}
|
||||
return rmm.WinSvcResp{Success: true, ErrorMsg: ""}
|
||||
}
|
||||
|
||||
return rmm.WinSvcResp{Success: false, ErrorMsg: "Something went wrong"}
|
||||
}
|
||||
|
||||
func (a *Agent) EditService(name, startupType string) rmm.WinSvcResp {
|
||||
conn, err := mgr.Connect()
|
||||
if err != nil {
|
||||
return rmm.WinSvcResp{Success: false, ErrorMsg: err.Error()}
|
||||
}
|
||||
defer conn.Disconnect()
|
||||
|
||||
srv, err := conn.OpenService(name)
|
||||
if err != nil {
|
||||
return rmm.WinSvcResp{Success: false, ErrorMsg: err.Error()}
|
||||
}
|
||||
defer srv.Close()
|
||||
|
||||
conf, err := srv.Config()
|
||||
if err != nil {
|
||||
return rmm.WinSvcResp{Success: false, ErrorMsg: err.Error()}
|
||||
}
|
||||
|
||||
var startType uint32
|
||||
switch startupType {
|
||||
case "auto":
|
||||
startType = 2
|
||||
case "autodelay":
|
||||
startType = 2
|
||||
case "manual":
|
||||
startType = 3
|
||||
case "disabled":
|
||||
startType = 4
|
||||
default:
|
||||
return rmm.WinSvcResp{Success: false, ErrorMsg: "Unknown startup type provided"}
|
||||
}
|
||||
|
||||
conf.StartType = startType
|
||||
if startupType == "autodelay" {
|
||||
conf.DelayedAutoStart = true
|
||||
} else if startupType == "auto" {
|
||||
conf.DelayedAutoStart = false
|
||||
}
|
||||
|
||||
err = srv.UpdateConfig(conf)
|
||||
if err != nil {
|
||||
return rmm.WinSvcResp{Success: false, ErrorMsg: err.Error()}
|
||||
}
|
||||
return rmm.WinSvcResp{Success: true, ErrorMsg: ""}
|
||||
}
|
||||
|
||||
func (a *Agent) GetServiceDetail(name string) trmm.WindowsService {
|
||||
ret := trmm.WindowsService{}
|
||||
|
||||
conn, err := mgr.Connect()
|
||||
if err != nil {
|
||||
a.Logger.Errorln(err)
|
||||
return ret
|
||||
}
|
||||
defer conn.Disconnect()
|
||||
|
||||
srv, err := conn.OpenService(name)
|
||||
if err != nil {
|
||||
a.Logger.Errorln(err)
|
||||
return ret
|
||||
}
|
||||
defer srv.Close()
|
||||
|
||||
q, err := srv.Query()
|
||||
if err != nil {
|
||||
a.Logger.Errorln(err)
|
||||
return ret
|
||||
}
|
||||
|
||||
conf, err := srv.Config()
|
||||
if err != nil {
|
||||
a.Logger.Errorln(err)
|
||||
return ret
|
||||
}
|
||||
|
||||
ret.BinPath = CleanString(conf.BinaryPathName)
|
||||
ret.Description = CleanString(conf.Description)
|
||||
ret.DisplayName = CleanString(conf.DisplayName)
|
||||
ret.Name = name
|
||||
ret.PID = q.ProcessId
|
||||
ret.StartType = serviceStartType(uint32(conf.StartType))
|
||||
ret.Status = serviceStatusText(uint32(q.State))
|
||||
ret.Username = CleanString(conf.ServiceStartName)
|
||||
ret.DelayedAutoStart = conf.DelayedAutoStart
|
||||
return ret
|
||||
}
|
||||
|
||||
// GetServices returns a list of windows services
|
||||
func (a *Agent) GetServices() []trmm.WindowsService {
|
||||
ret := make([]trmm.WindowsService, 0)
|
||||
|
||||
conn, err := mgr.Connect()
|
||||
if err != nil {
|
||||
a.Logger.Debugln(err)
|
||||
return ret
|
||||
}
|
||||
defer conn.Disconnect()
|
||||
|
||||
svcs, err := conn.ListServices()
|
||||
|
||||
if err != nil {
|
||||
a.Logger.Debugln(err)
|
||||
return ret
|
||||
}
|
||||
|
||||
for _, s := range svcs {
|
||||
srv, err := conn.OpenService(s)
|
||||
if err != nil {
|
||||
if err.Error() != "Access is denied." {
|
||||
a.Logger.Debugln("Open Service", s, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
defer srv.Close()
|
||||
|
||||
q, err := srv.Query()
|
||||
if err != nil {
|
||||
a.Logger.Debugln(err)
|
||||
continue
|
||||
}
|
||||
|
||||
conf, err := srv.Config()
|
||||
if err != nil {
|
||||
a.Logger.Debugln(err)
|
||||
continue
|
||||
}
|
||||
|
||||
ret = append(ret, trmm.WindowsService{
|
||||
Name: s,
|
||||
Status: serviceStatusText(uint32(q.State)),
|
||||
DisplayName: CleanString(conf.DisplayName),
|
||||
BinPath: CleanString(conf.BinaryPathName),
|
||||
Description: CleanString(conf.Description),
|
||||
Username: CleanString(conf.ServiceStartName),
|
||||
PID: q.ProcessId,
|
||||
StartType: serviceStartType(uint32(conf.StartType)),
|
||||
DelayedAutoStart: conf.DelayedAutoStart,
|
||||
})
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// WaitForService will wait for a service to be in X state for X retries
|
||||
func WaitForService(name string, status string, retries int) {
|
||||
attempts := 0
|
||||
for {
|
||||
stat, err := GetServiceStatus(name)
|
||||
if err != nil {
|
||||
attempts++
|
||||
time.Sleep(5 * time.Second)
|
||||
} else {
|
||||
if stat != status {
|
||||
attempts++
|
||||
time.Sleep(5 * time.Second)
|
||||
} else {
|
||||
attempts = 0
|
||||
}
|
||||
}
|
||||
if attempts == 0 || attempts >= retries {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func serviceExists(name string) bool {
|
||||
conn, err := mgr.Connect()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer conn.Disconnect()
|
||||
|
||||
srv, err := conn.OpenService(name)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer srv.Close()
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// https://docs.microsoft.com/en-us/dotnet/api/system.serviceprocess.servicecontrollerstatus?view=dotnet-plat-ext-3.1
|
||||
func serviceStatusText(num uint32) string {
|
||||
switch num {
|
||||
case 1:
|
||||
return "stopped"
|
||||
case 2:
|
||||
return "start_pending"
|
||||
case 3:
|
||||
return "stop_pending"
|
||||
case 4:
|
||||
return "running"
|
||||
case 5:
|
||||
return "continue_pending"
|
||||
case 6:
|
||||
return "pause_pending"
|
||||
case 7:
|
||||
return "paused"
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
// https://docs.microsoft.com/en-us/dotnet/api/system.serviceprocess.servicestartmode?view=dotnet-plat-ext-3.1
|
||||
func serviceStartType(num uint32) string {
|
||||
switch num {
|
||||
case 0:
|
||||
return "Boot"
|
||||
case 1:
|
||||
return "System"
|
||||
case 2:
|
||||
return "Automatic"
|
||||
case 3:
|
||||
return "Manual"
|
||||
case 4:
|
||||
return "Disabled"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue