testing and generate
This commit is contained in:
parent
9743d7ee22
commit
3841aee4b2
24 changed files with 1087 additions and 610 deletions
|
|
@ -3,12 +3,17 @@ https://github.com/amidaware/tacticalrmm
|
||||||
|
|
||||||
#### building the agent - linux
|
#### building the agent - linux
|
||||||
```
|
```
|
||||||
|
go install github.com/josephspurrier/goversioninfo/cmd/goversioninfo
|
||||||
|
go generate
|
||||||
env CGO_ENABLED=0 GOOS=<GOOS> GOARCH=<GOARCH> go build -ldflags "-s -w -X 'main.version=v2.0.4'"
|
env CGO_ENABLED=0 GOOS=<GOOS> GOARCH=<GOARCH> go build -ldflags "-s -w -X 'main.version=v2.0.4'"
|
||||||
|
example: env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "-s -w -X 'main.version=v2.0.4' -o build/output/rmmagent"
|
||||||
```
|
```
|
||||||
|
|
||||||
#### building the agent - windows
|
#### building the agent - windows
|
||||||
```
|
```
|
||||||
$env:CGO_ENABLED="0";$env:GOOS="windows";$env:GOARCH="amd64"; go build -ldflags "-s -w -X 'main.version=v2.0.4'"
|
go install github.com/josephspurrier/goversioninfo/cmd/goversioninfo
|
||||||
|
go generate
|
||||||
|
$env:CGO_ENABLED="0";$env:GOOS="windows";$env:GOARCH="amd64"; go build -ldflags "-s -w -X 'main.version=v2.0.4' -o build/output/tacticalrmm.exe"
|
||||||
```
|
```
|
||||||
|
|
||||||
### tests
|
### tests
|
||||||
|
|
|
||||||
|
|
@ -836,15 +836,15 @@ func (a *Agent) InstallService() error {
|
||||||
// skip on first call of inno setup if this is a new install
|
// skip on first call of inno setup if this is a new install
|
||||||
_, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\TacticalRMM`, registry.ALL_ACCESS)
|
_, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\TacticalRMM`, registry.ALL_ACCESS)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
s, err := service.New(a, a.ServiceConfig)
|
svc, err := service.New(a, a.ServiceConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return service.Control(s, "install")
|
return service.Control(svc, "install")
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO add to stub
|
// TODO add to stub
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,32 @@ import (
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
func TestInstall(t *testing.T) {
|
func TestInstall(t *testing.T) {
|
||||||
|
testTable := []struct {
|
||||||
|
name string
|
||||||
|
expectedError error
|
||||||
|
version string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Install",
|
||||||
|
expectedError: nil,
|
||||||
|
version: "2.0.4",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Install Error",
|
||||||
|
expectedError: nil,
|
||||||
|
version: "bad ver",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range testTable {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
version = "2.0.4"
|
version = "2.0.4"
|
||||||
log = logrus.New()
|
log = logrus.New()
|
||||||
|
|
|
||||||
|
|
@ -36,8 +36,6 @@ func PatchMgmnt(enable bool) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
func GetUpdates() (PackageList, error) {
|
func GetUpdates() (PackageList, error) {
|
||||||
wuaupdates, err := wua.WUAUpdates("IsInstalled=1 or IsInstalled=0 and Type='Software' and IsHidden=0")
|
wuaupdates, err := wua.WUAUpdates("IsInstalled=1 or IsInstalled=0 and Type='Software' and IsHidden=0")
|
||||||
packages := []Package{}
|
packages := []Package{}
|
||||||
|
|
|
||||||
|
|
@ -297,3 +297,19 @@ func EditService(name, startupType string) WinSvcResp {
|
||||||
|
|
||||||
return WinSvcResp{Success: true, ErrorMsg: ""}
|
return WinSvcResp{Success: true, ErrorMsg: ""}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ServiceExists(name string) (bool, error) {
|
||||||
|
conn, err := mgr.Connect()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer conn.Disconnect()
|
||||||
|
srv, err := conn.OpenService(name)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer srv.Close()
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/amidaware/rmmagent/agent/tactical/shared"
|
||||||
"github.com/amidaware/rmmagent/agent/utils"
|
"github.com/amidaware/rmmagent/agent/utils"
|
||||||
ps "github.com/elastic/go-sysinfo"
|
ps "github.com/elastic/go-sysinfo"
|
||||||
gocmd "github.com/go-cmd/cmd"
|
gocmd "github.com/go-cmd/cmd"
|
||||||
|
|
@ -144,7 +145,7 @@ func RunPythonCode(code string, timeout int, args []string) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//a.Logger.Debugln(cmdArgs)
|
//a.Logger.Debugln(cmdArgs)
|
||||||
cmd := exec.CommandContext(ctx, GetPythonBin(), cmdArgs...)
|
cmd := exec.CommandContext(ctx, shared.GetPythonBin(), cmdArgs...)
|
||||||
cmd.Stdout = &outb
|
cmd.Stdout = &outb
|
||||||
cmd.Stderr = &errb
|
cmd.Stderr = &errb
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,12 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/amidaware/rmmagent/agent/tactical/shared"
|
||||||
"github.com/amidaware/rmmagent/agent/utils"
|
"github.com/amidaware/rmmagent/agent/utils"
|
||||||
ps "github.com/elastic/go-sysinfo"
|
ps "github.com/elastic/go-sysinfo"
|
||||||
"github.com/go-ole/go-ole"
|
"github.com/go-ole/go-ole"
|
||||||
|
|
@ -25,11 +25,6 @@ import (
|
||||||
"golang.org/x/sys/windows/registry"
|
"golang.org/x/sys/windows/registry"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
ProgFilesName = "TacticalAgent"
|
|
||||||
winExeName = "tacticalrmm.exe"
|
|
||||||
)
|
|
||||||
|
|
||||||
func RunScript(code string, shell string, args []string, timeout int) (stdout, stderr string, exitcode int, e error) {
|
func RunScript(code string, shell string, args []string, timeout int) (stdout, stderr string, exitcode int, e error) {
|
||||||
content := []byte(code)
|
content := []byte(code)
|
||||||
dir := filepath.Join(os.TempDir(), "trmm")
|
dir := filepath.Join(os.TempDir(), "trmm")
|
||||||
|
|
@ -74,7 +69,7 @@ func RunScript(code string, shell string, args []string, timeout int) (stdout, s
|
||||||
exe = "Powershell"
|
exe = "Powershell"
|
||||||
cmdArgs = []string{"-NonInteractive", "-NoProfile", "-ExecutionPolicy", "Bypass", tmpfn.Name()}
|
cmdArgs = []string{"-NonInteractive", "-NoProfile", "-ExecutionPolicy", "Bypass", tmpfn.Name()}
|
||||||
case "python":
|
case "python":
|
||||||
exe = GetPythonBin()
|
exe = shared.GetPythonBin()
|
||||||
cmdArgs = []string{tmpfn.Name()}
|
cmdArgs = []string{tmpfn.Name()}
|
||||||
case "cmd":
|
case "cmd":
|
||||||
exe = tmpfn.Name()
|
exe = tmpfn.Name()
|
||||||
|
|
@ -250,28 +245,6 @@ func CMDShell(shell string, cmdArgs []string, command string, timeout int, detac
|
||||||
nil
|
nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetProgramDirectory() string {
|
|
||||||
pd := filepath.Join(os.Getenv("ProgramFiles"), ProgFilesName)
|
|
||||||
return pd
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetProgramBin() string {
|
|
||||||
exe := filepath.Join(GetProgramDirectory(), winExeName)
|
|
||||||
return exe
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetPythonBin() string {
|
|
||||||
var pybin string
|
|
||||||
switch runtime.GOARCH {
|
|
||||||
case "amd64":
|
|
||||||
pybin = filepath.Join(GetProgramDirectory(), "py38-x64", "python.exe")
|
|
||||||
case "386":
|
|
||||||
pybin = filepath.Join(GetProgramDirectory(), "py38-x32", "python.exe")
|
|
||||||
}
|
|
||||||
|
|
||||||
return pybin
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoggedOnUser returns the first logged on user it finds
|
// LoggedOnUser returns the first logged on user it finds
|
||||||
func LoggedOnUser() string {
|
func LoggedOnUser() string {
|
||||||
pyCode := `
|
pyCode := `
|
||||||
|
|
@ -471,7 +444,7 @@ func OsString() string {
|
||||||
return osFullName
|
return osFullName
|
||||||
}
|
}
|
||||||
|
|
||||||
func AddDefenderExlusions() error {
|
func AddDefenderExclusions() error {
|
||||||
code := `
|
code := `
|
||||||
Add-MpPreference -ExclusionPath 'C:\Program Files\TacticalAgent\*'
|
Add-MpPreference -ExclusionPath 'C:\Program Files\TacticalAgent\*'
|
||||||
Add-MpPreference -ExclusionPath 'C:\Windows\Temp\winagent-v*.exe'
|
Add-MpPreference -ExclusionPath 'C:\Windows\Temp\winagent-v*.exe'
|
||||||
|
|
@ -507,4 +480,4 @@ func KillProc(pid int32) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"github.com/amidaware/rmmagent/agent/services"
|
"github.com/amidaware/rmmagent/agent/services"
|
||||||
"github.com/amidaware/rmmagent/agent/system"
|
"github.com/amidaware/rmmagent/agent/system"
|
||||||
"github.com/amidaware/rmmagent/agent/tactical/api"
|
"github.com/amidaware/rmmagent/agent/tactical/api"
|
||||||
|
"github.com/amidaware/rmmagent/agent/tactical/shared"
|
||||||
"github.com/amidaware/rmmagent/agent/utils"
|
"github.com/amidaware/rmmagent/agent/utils"
|
||||||
ps "github.com/elastic/go-sysinfo"
|
ps "github.com/elastic/go-sysinfo"
|
||||||
"github.com/go-ping/ping"
|
"github.com/go-ping/ping"
|
||||||
|
|
@ -25,7 +26,7 @@ func CheckRunner(agentID string) error {
|
||||||
for {
|
for {
|
||||||
interval, err := GetCheckInterval(agentID)
|
interval, err := GetCheckInterval(agentID)
|
||||||
if err == nil && !ChecksRunning() {
|
if err == nil && !ChecksRunning() {
|
||||||
_, err = system.CMD(system.GetProgramBin(), []string{"-m", "checkrunner"}, 600, false)
|
_, err = system.CMD(shared.GetProgramBin(), []string{"-m", "checkrunner"}, 600, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -66,7 +67,7 @@ Out:
|
||||||
if p.PID == 0 {
|
if p.PID == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if p.Exe != system.GetProgramBin() {
|
if p.Exe != shared.GetProgramBin() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
39
agent/tactical/install/install_unix.go
Normal file
39
agent/tactical/install/install_unix.go
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
//go:build !windows
|
||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package install
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CheckExistingAndRemove(silent bool) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateAgentConfig(baseurl, agentid, apiurl, token, agentpk, cert, proxy, meshdir string) {
|
||||||
|
viper.SetConfigType("json")
|
||||||
|
viper.Set("baseurl", baseurl)
|
||||||
|
viper.Set("agentid", agentid)
|
||||||
|
viper.Set("apiurl", apiurl)
|
||||||
|
viper.Set("token", token)
|
||||||
|
viper.Set("agentpk", agentpk)
|
||||||
|
viper.Set("cert", cert)
|
||||||
|
viper.Set("proxy", proxy)
|
||||||
|
viper.Set("meshdir", meshdir)
|
||||||
|
viper.SetConfigPermissions(0660)
|
||||||
|
configLocation := "/etc/tacticalagent"
|
||||||
|
|
||||||
|
err := viper.SafeWriteConfigAs(configLocation)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("createAgentConfig", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func DisableSleepHibernate() {}
|
||||||
|
|
||||||
|
func EnablePing() {}
|
||||||
|
|
||||||
|
func EnableRDP() {}
|
||||||
349
agent/tactical/install/install_windows.go
Normal file
349
agent/tactical/install/install_windows.go
Normal file
|
|
@ -0,0 +1,349 @@
|
||||||
|
package install
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"math/rand"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/amidaware/rmmagent/agent/patching"
|
||||||
|
"github.com/amidaware/rmmagent/agent/services"
|
||||||
|
"github.com/amidaware/rmmagent/agent/system"
|
||||||
|
"github.com/amidaware/rmmagent/agent/tactical"
|
||||||
|
"github.com/amidaware/rmmagent/agent/tactical/mesh"
|
||||||
|
"github.com/amidaware/rmmagent/agent/tactical/service"
|
||||||
|
"github.com/amidaware/rmmagent/agent/tactical/shared"
|
||||||
|
"github.com/amidaware/rmmagent/agent/utils"
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
"github.com/gonutz/w32/v2"
|
||||||
|
ksvc "github.com/kardianos/service"
|
||||||
|
"github.com/shirou/gopsutil/host"
|
||||||
|
"golang.org/x/sys/windows/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
const winSvcName = "tacticalrmm"
|
||||||
|
|
||||||
|
func Install(i *Installer) error {
|
||||||
|
CheckExistingAndRemove(i.Silent)
|
||||||
|
i.Headers = map[string]string{
|
||||||
|
"content-type": "application/json",
|
||||||
|
"Authorization": fmt.Sprintf("Token %s", i.Token),
|
||||||
|
}
|
||||||
|
|
||||||
|
AgentID := GenerateAgentID()
|
||||||
|
u, err := url.Parse(i.RMM)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if u.Scheme != "https" && u.Scheme != "http" {
|
||||||
|
return errors.New("Invalid URL (must contain https or http)")
|
||||||
|
}
|
||||||
|
|
||||||
|
// will match either ipv4 , or ipv4:port
|
||||||
|
var ipPort = regexp.MustCompile(`[0-9]+(?:\.[0-9]+){3}(:[0-9]+)?`)
|
||||||
|
|
||||||
|
// if ipv4:port, strip the port to get ip for salt master
|
||||||
|
if ipPort.MatchString(u.Host) && strings.Contains(u.Host, ":") {
|
||||||
|
i.SaltMaster = strings.Split(u.Host, ":")[0]
|
||||||
|
} else if strings.Contains(u.Host, ":") {
|
||||||
|
i.SaltMaster = strings.Split(u.Host, ":")[0]
|
||||||
|
} else {
|
||||||
|
i.SaltMaster = u.Host
|
||||||
|
}
|
||||||
|
|
||||||
|
terr := utils.TestTCP(fmt.Sprintf("%s:4222", i.SaltMaster))
|
||||||
|
if terr != nil {
|
||||||
|
return fmt.Errorf("ERROR: Either port 4222 TCP is not open on your RMM, or nats.service is not running.\n\n%s", terr.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
baseURL := u.Scheme + "://" + u.Host
|
||||||
|
iClient := resty.New()
|
||||||
|
iClient.SetCloseConnection(true)
|
||||||
|
iClient.SetTimeout(15 * time.Second)
|
||||||
|
iClient.SetHeaders(i.Headers)
|
||||||
|
|
||||||
|
// set proxy if applicable
|
||||||
|
if len(i.Proxy) > 0 {
|
||||||
|
iClient.SetProxy(i.Proxy)
|
||||||
|
}
|
||||||
|
|
||||||
|
creds, cerr := iClient.R().Get(fmt.Sprintf("%s/api/v3/installer/", baseURL))
|
||||||
|
if cerr != nil {
|
||||||
|
return cerr
|
||||||
|
}
|
||||||
|
|
||||||
|
if creds.StatusCode() == 401 {
|
||||||
|
return errors.New("Installer token has expired. Please generate a new one.")
|
||||||
|
}
|
||||||
|
|
||||||
|
verPayload := map[string]string{"version": i.Version}
|
||||||
|
iVersion, ierr := iClient.R().SetBody(verPayload).Post(fmt.Sprintf("%s/api/v3/installer/", baseURL))
|
||||||
|
if ierr != nil {
|
||||||
|
return ierr
|
||||||
|
}
|
||||||
|
|
||||||
|
if iVersion.StatusCode() != 200 {
|
||||||
|
return errors.New(DjangoStringResp(iVersion.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
rClient := resty.New()
|
||||||
|
rClient.SetCloseConnection(true)
|
||||||
|
rClient.SetTimeout(i.Timeout * time.Second)
|
||||||
|
// set rest knox headers
|
||||||
|
rClient.SetHeaders(i.Headers)
|
||||||
|
|
||||||
|
// set local cert if applicable
|
||||||
|
if len(i.Cert) > 0 {
|
||||||
|
if !utils.FileExists(i.Cert) {
|
||||||
|
return fmt.Errorf("%s does not exist", i.Cert)
|
||||||
|
}
|
||||||
|
|
||||||
|
rClient.SetRootCertificate(i.Cert)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(i.Proxy) > 0 {
|
||||||
|
rClient.SetProxy(i.Proxy)
|
||||||
|
}
|
||||||
|
|
||||||
|
var arch string
|
||||||
|
switch runtime.GOARCH {
|
||||||
|
case "x86_64":
|
||||||
|
arch = "64"
|
||||||
|
case "x86":
|
||||||
|
arch = "32"
|
||||||
|
}
|
||||||
|
|
||||||
|
var installerMeshSystemBin string
|
||||||
|
if len(i.MeshDir) > 0 {
|
||||||
|
installerMeshSystemBin = filepath.Join(i.MeshDir, "MeshAgent.exe")
|
||||||
|
} else {
|
||||||
|
installerMeshSystemBin = mesh.GetMeshBinLocation()
|
||||||
|
}
|
||||||
|
|
||||||
|
var meshNodeID string
|
||||||
|
|
||||||
|
if runtime.GOOS == "windows" && !i.NoMesh {
|
||||||
|
meshPath := filepath.Join(shared.GetProgramDirectory(), "meshagent.exe")
|
||||||
|
if i.LocalMesh == "" {
|
||||||
|
payload := map[string]string{"arch": arch, "plat": runtime.GOOS}
|
||||||
|
r, err := rClient.R().SetBody(payload).SetOutput(meshPath).Post(fmt.Sprintf("%s/api/v3/meshexe/", baseURL))
|
||||||
|
if err != nil {
|
||||||
|
}
|
||||||
|
if r.StatusCode() != 200 {
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err := copyFile(i.LocalMesh, meshPath)
|
||||||
|
if err != nil {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
meshNodeID, err = mesh.InstallMesh(meshPath, installerMeshSystemBin, i.Proxy)
|
||||||
|
if err != nil {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(i.MeshNodeID) > 0 {
|
||||||
|
meshNodeID = i.MeshNodeID
|
||||||
|
}
|
||||||
|
|
||||||
|
// add agent
|
||||||
|
host, _ := host.Info()
|
||||||
|
agentPayload := map[string]interface{}{
|
||||||
|
"agent_id": AgentID,
|
||||||
|
"hostname": host.Hostname,
|
||||||
|
"site": i.SiteID,
|
||||||
|
"monitoring_type": i.AgentType,
|
||||||
|
"mesh_node_id": meshNodeID,
|
||||||
|
"description": i.Description,
|
||||||
|
"goarch": runtime.GOARCH,
|
||||||
|
"plat": runtime.GOOS,
|
||||||
|
}
|
||||||
|
|
||||||
|
r, err := rClient.R().SetBody(agentPayload).SetResult(&NewAgentResp{}).Post(fmt.Sprintf("%s/api/v3/newagent/", baseURL))
|
||||||
|
if err != nil {
|
||||||
|
}
|
||||||
|
if r.StatusCode() != 200 {
|
||||||
|
}
|
||||||
|
|
||||||
|
agentPK := r.Result().(*NewAgentResp).AgentPK
|
||||||
|
agentToken := r.Result().(*NewAgentResp).Token
|
||||||
|
CreateAgentConfig(baseURL, AgentID, i.SaltMaster, agentToken, strconv.Itoa(agentPK), i.Cert, i.Proxy, i.MeshDir)
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
|
||||||
|
time.Sleep(3 * time.Second)
|
||||||
|
|
||||||
|
// check in once
|
||||||
|
service.DoNatsCheckIn(i.Version)
|
||||||
|
service.SendSoftware()
|
||||||
|
utils.CreateTRMMTempDir()
|
||||||
|
patching.PatchMgmnt(true)
|
||||||
|
|
||||||
|
svcConf := &ksvc.Config{
|
||||||
|
Executable: shared.GetProgramBin(),
|
||||||
|
Name: winSvcName,
|
||||||
|
DisplayName: "TacticalRMM Agent Service",
|
||||||
|
Arguments: []string{"-m", "svc"},
|
||||||
|
Description: "TacticalRMM Agent Service",
|
||||||
|
Option: ksvc.KeyValue{
|
||||||
|
"StartType": "automatic",
|
||||||
|
"OnFailure": "restart",
|
||||||
|
"OnFailureDelayDuration": "5s",
|
||||||
|
"OnFailureResetPeriod": 10,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err = service.InstallService(winSvcName, service.IService{}, svcConf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
out := services.ControlService(winSvcName, "start")
|
||||||
|
if !out.Success {
|
||||||
|
return errors.New(out.ErrorMsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
system.AddDefenderExclusions()
|
||||||
|
if i.Power {
|
||||||
|
system.DisableSleepHibernate()
|
||||||
|
}
|
||||||
|
|
||||||
|
if i.Ping {
|
||||||
|
system.EnablePing()
|
||||||
|
}
|
||||||
|
|
||||||
|
if i.RDP {
|
||||||
|
system.EnableRDP()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyFile(src, dst string) error {
|
||||||
|
in, err := os.Open(src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer in.Close()
|
||||||
|
|
||||||
|
out, err := os.Create(dst)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer out.Close()
|
||||||
|
|
||||||
|
_, err = io.Copy(out, in)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateAgentID creates and returns a unique agent id
|
||||||
|
func GenerateAgentID() string {
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
letters := []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
||||||
|
b := make([]rune, 40)
|
||||||
|
for i := range b {
|
||||||
|
b[i] = letters[rand.Intn(len(letters))]
|
||||||
|
}
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DjangoStringResp removes double quotes from django rest api resp
|
||||||
|
func DjangoStringResp(resp string) string {
|
||||||
|
return strings.Trim(resp, `"`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateAgentConfig(baseurl, agentid, apiurl, token, agentpk, cert, proxy, meshdir string) error {
|
||||||
|
k, _, err := registry.CreateKey(registry.LOCAL_MACHINE, `SOFTWARE\TacticalRMM`, registry.ALL_ACCESS)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer k.Close()
|
||||||
|
err = k.SetStringValue("BaseURL", baseurl)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = k.SetStringValue("AgentID", agentid)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = k.SetStringValue("ApiURL", apiurl)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = k.SetStringValue("Token", token)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = k.SetStringValue("AgentPK", agentpk)
|
||||||
|
if err != nil {
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(cert) > 0 {
|
||||||
|
err = k.SetStringValue("Cert", cert)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(proxy) > 0 {
|
||||||
|
err = k.SetStringValue("Proxy", proxy)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(meshdir) > 0 {
|
||||||
|
err = k.SetStringValue("MeshDir", meshdir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CheckExistingAndRemove(silent bool) {
|
||||||
|
hasReg := false
|
||||||
|
_, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\TacticalRMM`, registry.ALL_ACCESS)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
hasReg = true
|
||||||
|
}
|
||||||
|
if hasReg {
|
||||||
|
tacUninst := filepath.Join(shared.GetProgramDirectory(), tactical.GetUninstallExe())
|
||||||
|
tacUninstArgs := [2]string{tacUninst, "/VERYSILENT"}
|
||||||
|
|
||||||
|
window := w32.GetForegroundWindow()
|
||||||
|
if !silent && window != 0 {
|
||||||
|
var handle w32.HWND
|
||||||
|
msg := "Existing installation found\nClick OK to remove, then re-run the installer.\nClick Cancel to abort."
|
||||||
|
action := w32.MessageBox(handle, msg, "Tactical RMM", w32.MB_OKCANCEL|w32.MB_ICONWARNING)
|
||||||
|
if action == w32.IDOK {
|
||||||
|
tactical.AgentUninstall("foo")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Println("Existing installation found and must be removed before attempting to reinstall.")
|
||||||
|
fmt.Println("Run the following command to uninstall, and then re-run this installer.")
|
||||||
|
fmt.Printf(`"%s" %s `, tacUninstArgs[0], tacUninstArgs[1])
|
||||||
|
}
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
31
agent/tactical/install/structs.go
Normal file
31
agent/tactical/install/structs.go
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
package install
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type Installer struct {
|
||||||
|
Headers map[string]string
|
||||||
|
RMM string
|
||||||
|
ClientID int
|
||||||
|
SiteID int
|
||||||
|
Description string
|
||||||
|
AgentType string
|
||||||
|
Power bool
|
||||||
|
RDP bool
|
||||||
|
Ping bool
|
||||||
|
Token string
|
||||||
|
LocalMesh string
|
||||||
|
Cert string
|
||||||
|
Proxy string
|
||||||
|
Timeout time.Duration
|
||||||
|
SaltMaster string
|
||||||
|
Silent bool
|
||||||
|
NoMesh bool
|
||||||
|
MeshDir string
|
||||||
|
MeshNodeID string
|
||||||
|
Version string
|
||||||
|
}
|
||||||
|
|
||||||
|
type NewAgentResp struct {
|
||||||
|
AgentPK int `json:"pk"`
|
||||||
|
Token string `json:"token"`
|
||||||
|
}
|
||||||
|
|
@ -1,472 +0,0 @@
|
||||||
package rpc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"runtime"
|
|
||||||
"strconv"
|
|
||||||
"sync"
|
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/amidaware/rmmagent/agent/choco"
|
|
||||||
"github.com/amidaware/rmmagent/agent/events"
|
|
||||||
"github.com/amidaware/rmmagent/agent/network"
|
|
||||||
"github.com/amidaware/rmmagent/agent/patching"
|
|
||||||
"github.com/amidaware/rmmagent/agent/services"
|
|
||||||
"github.com/amidaware/rmmagent/agent/software"
|
|
||||||
"github.com/amidaware/rmmagent/agent/system"
|
|
||||||
"github.com/amidaware/rmmagent/agent/tactical"
|
|
||||||
"github.com/amidaware/rmmagent/agent/tactical/api"
|
|
||||||
"github.com/amidaware/rmmagent/agent/tactical/checks"
|
|
||||||
"github.com/amidaware/rmmagent/agent/tactical/config"
|
|
||||||
"github.com/amidaware/rmmagent/agent/tactical/mesh"
|
|
||||||
"github.com/amidaware/rmmagent/agent/tactical/service"
|
|
||||||
"github.com/amidaware/rmmagent/agent/tactical/shared"
|
|
||||||
ttasks "github.com/amidaware/rmmagent/agent/tactical/tasks"
|
|
||||||
"github.com/amidaware/rmmagent/agent/tasks"
|
|
||||||
ksvc "github.com/kardianos/service"
|
|
||||||
nats "github.com/nats-io/nats.go"
|
|
||||||
"github.com/ugorji/go/codec"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
agentUpdateLocker uint32
|
|
||||||
getWinUpdateLocker uint32
|
|
||||||
installWinUpdateLocker uint32
|
|
||||||
)
|
|
||||||
|
|
||||||
func RunRPC(version string) error {
|
|
||||||
config := config.NewAgentConfig()
|
|
||||||
go service.RunAsService(version)
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
wg.Add(1)
|
|
||||||
opts := service.SetupNatsOptions()
|
|
||||||
server := fmt.Sprintf("tls://%s:4222", config.APIURL)
|
|
||||||
nc, err := nats.Connect(server, opts...)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
nc.Subscribe(config.AgentID, func(msg *nats.Msg) {
|
|
||||||
var payload *NatsMsg
|
|
||||||
var mh codec.MsgpackHandle
|
|
||||||
mh.RawToString = true
|
|
||||||
|
|
||||||
dec := codec.NewDecoderBytes(msg.Data, &mh)
|
|
||||||
if err := dec.Decode(&payload); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch payload.Func {
|
|
||||||
case "ping":
|
|
||||||
go func() {
|
|
||||||
var resp []byte
|
|
||||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
|
||||||
ret.Encode("pong")
|
|
||||||
msg.Respond(resp)
|
|
||||||
}()
|
|
||||||
|
|
||||||
case "patchmgmt":
|
|
||||||
go func(p *NatsMsg) {
|
|
||||||
var resp []byte
|
|
||||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
|
||||||
err := patching.PatchMgmnt(p.PatchMgmt)
|
|
||||||
if err != nil {
|
|
||||||
ret.Encode(err.Error())
|
|
||||||
} else {
|
|
||||||
ret.Encode("ok")
|
|
||||||
}
|
|
||||||
msg.Respond(resp)
|
|
||||||
}(payload)
|
|
||||||
|
|
||||||
case "schedtask":
|
|
||||||
go func(p *NatsMsg) {
|
|
||||||
var resp []byte
|
|
||||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
|
||||||
success, err := tasks.CreateSchedTask(p.ScheduledTask)
|
|
||||||
if err != nil {
|
|
||||||
ret.Encode(err.Error())
|
|
||||||
} else if !success {
|
|
||||||
ret.Encode("Something went wrong")
|
|
||||||
} else {
|
|
||||||
ret.Encode("ok")
|
|
||||||
}
|
|
||||||
msg.Respond(resp)
|
|
||||||
}(payload)
|
|
||||||
|
|
||||||
case "delschedtask":
|
|
||||||
go func(p *NatsMsg) {
|
|
||||||
var resp []byte
|
|
||||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
|
||||||
err := tasks.DeleteSchedTask(p.ScheduledTask.Name)
|
|
||||||
if err != nil {
|
|
||||||
ret.Encode(err.Error())
|
|
||||||
} else {
|
|
||||||
ret.Encode("ok")
|
|
||||||
}
|
|
||||||
msg.Respond(resp)
|
|
||||||
}(payload)
|
|
||||||
|
|
||||||
case "listschedtasks":
|
|
||||||
go func() {
|
|
||||||
var resp []byte
|
|
||||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
|
||||||
tasks, _ := tasks.ListSchedTasks()
|
|
||||||
ret.Encode(tasks)
|
|
||||||
msg.Respond(resp)
|
|
||||||
}()
|
|
||||||
|
|
||||||
case "eventlog":
|
|
||||||
go func(p *NatsMsg) {
|
|
||||||
var resp []byte
|
|
||||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
|
||||||
days, _ := strconv.Atoi(p.Data["days"])
|
|
||||||
evtLog, _ := events.GetEventLog(p.Data["logname"], days)
|
|
||||||
ret.Encode(evtLog)
|
|
||||||
msg.Respond(resp)
|
|
||||||
}(payload)
|
|
||||||
|
|
||||||
case "procs":
|
|
||||||
go func() {
|
|
||||||
var resp []byte
|
|
||||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
|
||||||
procs := system.GetProcsRPC()
|
|
||||||
ret.Encode(procs)
|
|
||||||
msg.Respond(resp)
|
|
||||||
}()
|
|
||||||
|
|
||||||
case "killproc":
|
|
||||||
go func(p *NatsMsg) {
|
|
||||||
var resp []byte
|
|
||||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
|
||||||
err := system.KillProc(p.ProcPID)
|
|
||||||
if err != nil {
|
|
||||||
ret.Encode(err.Error())
|
|
||||||
} else {
|
|
||||||
ret.Encode("ok")
|
|
||||||
}
|
|
||||||
msg.Respond(resp)
|
|
||||||
}(payload)
|
|
||||||
|
|
||||||
case "rawcmd":
|
|
||||||
go func(p *NatsMsg) {
|
|
||||||
var resp []byte
|
|
||||||
var resultData RawCMDResp
|
|
||||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
|
||||||
|
|
||||||
switch runtime.GOOS {
|
|
||||||
case "windows":
|
|
||||||
out, _ := system.CMDShell(p.Data["shell"], []string{}, p.Data["command"], p.Timeout, false)
|
|
||||||
if out[1] != "" {
|
|
||||||
ret.Encode(out[1])
|
|
||||||
resultData.Results = out[1]
|
|
||||||
} else {
|
|
||||||
ret.Encode(out[0])
|
|
||||||
resultData.Results = out[0]
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
opts := system.NewCMDOpts()
|
|
||||||
opts.Shell = p.Data["shell"]
|
|
||||||
opts.Command = p.Data["command"]
|
|
||||||
opts.Timeout = time.Duration(p.Timeout)
|
|
||||||
out := system.CmdV2(opts)
|
|
||||||
tmp := ""
|
|
||||||
if len(out.Stdout) > 0 {
|
|
||||||
tmp += out.Stdout
|
|
||||||
}
|
|
||||||
if len(out.Stderr) > 0 {
|
|
||||||
tmp += "\n"
|
|
||||||
tmp += out.Stderr
|
|
||||||
}
|
|
||||||
ret.Encode(tmp)
|
|
||||||
resultData.Results = tmp
|
|
||||||
}
|
|
||||||
|
|
||||||
msg.Respond(resp)
|
|
||||||
if p.ID != 0 {
|
|
||||||
api.Patch(resultData, fmt.Sprintf("/api/v3/%d/%s/histresult/", p.ID, config.AgentID))
|
|
||||||
}
|
|
||||||
}(payload)
|
|
||||||
|
|
||||||
case "winservices":
|
|
||||||
go func() {
|
|
||||||
var resp []byte
|
|
||||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
|
||||||
svcs, _, _ := services.GetServices()
|
|
||||||
ret.Encode(svcs)
|
|
||||||
msg.Respond(resp)
|
|
||||||
}()
|
|
||||||
|
|
||||||
case "winsvcdetail":
|
|
||||||
go func(p *NatsMsg) {
|
|
||||||
var resp []byte
|
|
||||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
|
||||||
svc := services.GetServiceDetail(p.Data["name"])
|
|
||||||
ret.Encode(svc)
|
|
||||||
msg.Respond(resp)
|
|
||||||
}(payload)
|
|
||||||
|
|
||||||
case "winsvcaction":
|
|
||||||
go func(p *NatsMsg) {
|
|
||||||
var resp []byte
|
|
||||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
|
||||||
retData := services.ControlService(p.Data["name"], p.Data["action"])
|
|
||||||
ret.Encode(retData)
|
|
||||||
msg.Respond(resp)
|
|
||||||
}(payload)
|
|
||||||
|
|
||||||
case "editwinsvc":
|
|
||||||
go func(p *NatsMsg) {
|
|
||||||
var resp []byte
|
|
||||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
|
||||||
retData := services.EditService(p.Data["name"], p.Data["startType"])
|
|
||||||
ret.Encode(retData)
|
|
||||||
msg.Respond(resp)
|
|
||||||
}(payload)
|
|
||||||
|
|
||||||
case "runscript":
|
|
||||||
go func(p *NatsMsg) {
|
|
||||||
var resp []byte
|
|
||||||
var retData string
|
|
||||||
var resultData RunScriptResp
|
|
||||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
|
||||||
start := time.Now()
|
|
||||||
stdout, stderr, retcode, err := system.RunScript(p.Data["code"], p.Data["shell"], p.ScriptArgs, p.Timeout)
|
|
||||||
resultData.ExecTime = time.Since(start).Seconds()
|
|
||||||
resultData.ID = p.ID
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
retData = err.Error()
|
|
||||||
resultData.Retcode = 1
|
|
||||||
resultData.Stderr = err.Error()
|
|
||||||
} else {
|
|
||||||
retData = stdout + stderr // to keep backwards compat
|
|
||||||
resultData.Retcode = retcode
|
|
||||||
resultData.Stdout = stdout
|
|
||||||
resultData.Stderr = stderr
|
|
||||||
}
|
|
||||||
//a.Logger.Debugln(retData)
|
|
||||||
ret.Encode(retData)
|
|
||||||
msg.Respond(resp)
|
|
||||||
if p.ID != 0 {
|
|
||||||
results := map[string]interface{}{"script_results": resultData}
|
|
||||||
api.Patch(results, fmt.Sprintf("/api/v3/%d/%s/histresult/", p.ID, config.AgentID))
|
|
||||||
}
|
|
||||||
}(payload)
|
|
||||||
|
|
||||||
case "runscriptfull":
|
|
||||||
go func(p *NatsMsg) {
|
|
||||||
var resp []byte
|
|
||||||
var retData RunScriptResp
|
|
||||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
|
||||||
start := time.Now()
|
|
||||||
stdout, stderr, retcode, err := system.RunScript(p.Data["code"], p.Data["shell"], p.ScriptArgs, p.Timeout)
|
|
||||||
|
|
||||||
retData.ExecTime = time.Since(start).Seconds()
|
|
||||||
if err != nil {
|
|
||||||
retData.Stderr = err.Error()
|
|
||||||
retData.Retcode = 1
|
|
||||||
} else {
|
|
||||||
retData.Stdout = stdout
|
|
||||||
retData.Stderr = stderr
|
|
||||||
retData.Retcode = retcode
|
|
||||||
}
|
|
||||||
retData.ID = p.ID
|
|
||||||
//a.Logger.Debugln(retData)
|
|
||||||
ret.Encode(retData)
|
|
||||||
msg.Respond(resp)
|
|
||||||
if p.ID != 0 {
|
|
||||||
results := map[string]interface{}{"script_results": retData}
|
|
||||||
api.Patch(results, fmt.Sprintf("/api/v3/%d/%s/histresult/", p.ID, config.AgentID))
|
|
||||||
}
|
|
||||||
}(payload)
|
|
||||||
|
|
||||||
case "recover":
|
|
||||||
go func(p *NatsMsg) {
|
|
||||||
var resp []byte
|
|
||||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
|
||||||
|
|
||||||
switch p.Data["mode"] {
|
|
||||||
case "mesh":
|
|
||||||
mesh.RecoverMesh()
|
|
||||||
}
|
|
||||||
|
|
||||||
ret.Encode("ok")
|
|
||||||
msg.Respond(resp)
|
|
||||||
}(payload)
|
|
||||||
case "softwarelist":
|
|
||||||
go func() {
|
|
||||||
var resp []byte
|
|
||||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
|
||||||
sw, _ := software.GetInstalledSoftware()
|
|
||||||
ret.Encode(sw)
|
|
||||||
msg.Respond(resp)
|
|
||||||
}()
|
|
||||||
|
|
||||||
case "rebootnow":
|
|
||||||
go func() {
|
|
||||||
//a.Logger.Debugln("Scheduling immediate reboot")
|
|
||||||
var resp []byte
|
|
||||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
|
||||||
ret.Encode("ok")
|
|
||||||
msg.Respond(resp)
|
|
||||||
if runtime.GOOS == "windows" {
|
|
||||||
system.CMD("shutdown.exe", []string{"/r", "/t", "5", "/f"}, 15, false)
|
|
||||||
} else {
|
|
||||||
opts := system.NewCMDOpts()
|
|
||||||
opts.Command = "reboot"
|
|
||||||
system.CmdV2(opts)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
case "needsreboot":
|
|
||||||
go func() {
|
|
||||||
var resp []byte
|
|
||||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
|
||||||
out, err := system.SystemRebootRequired()
|
|
||||||
if err == nil {
|
|
||||||
ret.Encode(out)
|
|
||||||
} else {
|
|
||||||
ret.Encode(false)
|
|
||||||
}
|
|
||||||
msg.Respond(resp)
|
|
||||||
}()
|
|
||||||
case "sysinfo":
|
|
||||||
go func() {
|
|
||||||
var resp []byte
|
|
||||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
|
||||||
modes := []string{"agent-agentinfo", "agent-disks", "agent-wmi", "agent-publicip"}
|
|
||||||
for _, m := range modes {
|
|
||||||
service.NatsMessage(version, nc, m)
|
|
||||||
}
|
|
||||||
ret.Encode("ok")
|
|
||||||
msg.Respond(resp)
|
|
||||||
}()
|
|
||||||
case "wmi":
|
|
||||||
go func() {
|
|
||||||
service.NatsMessage(version, nc, "agent-wmi")
|
|
||||||
}()
|
|
||||||
case "cpuloadavg":
|
|
||||||
go func() {
|
|
||||||
var resp []byte
|
|
||||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
|
||||||
loadAvg := system.GetCPULoadAvg()
|
|
||||||
ret.Encode(loadAvg)
|
|
||||||
msg.Respond(resp)
|
|
||||||
}()
|
|
||||||
case "runchecks":
|
|
||||||
go func() {
|
|
||||||
var resp []byte
|
|
||||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
|
||||||
if runtime.GOOS == "windows" {
|
|
||||||
if checks.ChecksRunning() {
|
|
||||||
ret.Encode("busy")
|
|
||||||
msg.Respond(resp)
|
|
||||||
} else {
|
|
||||||
ret.Encode("ok")
|
|
||||||
msg.Respond(resp)
|
|
||||||
_, checkerr := system.CMD(system.GetProgramBin(), []string{"-m", "runchecks"}, 600, false)
|
|
||||||
if checkerr != nil {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ret.Encode("ok")
|
|
||||||
msg.Respond(resp)
|
|
||||||
checks.RunChecks(config.AgentID, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
}()
|
|
||||||
case "runtask":
|
|
||||||
go func(p *NatsMsg) {
|
|
||||||
ttasks.RunTask(p.TaskPK)
|
|
||||||
}(payload)
|
|
||||||
|
|
||||||
case "publicip":
|
|
||||||
go func() {
|
|
||||||
var resp []byte
|
|
||||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
|
||||||
ret.Encode(network.PublicIP(config.Proxy))
|
|
||||||
msg.Respond(resp)
|
|
||||||
}()
|
|
||||||
case "installpython":
|
|
||||||
go shared.GetPython(true)
|
|
||||||
case "installchoco":
|
|
||||||
go choco.InstallChoco()
|
|
||||||
case "installwithchoco":
|
|
||||||
go func(p *NatsMsg) {
|
|
||||||
var resp []byte
|
|
||||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
|
||||||
ret.Encode("ok")
|
|
||||||
msg.Respond(resp)
|
|
||||||
out, _ := choco.InstallWithChoco(p.ChocoProgName)
|
|
||||||
results := map[string]string{"results": out}
|
|
||||||
url := fmt.Sprintf("/api/v3/%d/chocoresult/", p.PendingActionPK)
|
|
||||||
api.Patch(results, url)
|
|
||||||
}(payload)
|
|
||||||
case "getwinupdates":
|
|
||||||
go func() {
|
|
||||||
if !atomic.CompareAndSwapUint32(&getWinUpdateLocker, 0, 1) {
|
|
||||||
//a.Logger.Debugln("Already checking for windows updates")
|
|
||||||
} else {
|
|
||||||
//a.Logger.Debugln("Checking for windows updates")
|
|
||||||
defer atomic.StoreUint32(&getWinUpdateLocker, 0)
|
|
||||||
patching.GetUpdates()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
case "installwinupdates":
|
|
||||||
go func(p *NatsMsg) {
|
|
||||||
if !atomic.CompareAndSwapUint32(&installWinUpdateLocker, 0, 1) {
|
|
||||||
//a.Logger.Debugln("Already installing windows updates")
|
|
||||||
} else {
|
|
||||||
//a.Logger.Debugln("Installing windows updates", p.UpdateGUIDs)
|
|
||||||
defer atomic.StoreUint32(&installWinUpdateLocker, 0)
|
|
||||||
patching.InstallUpdates(p.UpdateGUIDs)
|
|
||||||
}
|
|
||||||
}(payload)
|
|
||||||
case "agentupdate":
|
|
||||||
go func(p *NatsMsg) {
|
|
||||||
var resp []byte
|
|
||||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
|
||||||
if !atomic.CompareAndSwapUint32(&agentUpdateLocker, 0, 1) {
|
|
||||||
//a.Logger.Debugln("Agent update already running")
|
|
||||||
ret.Encode("updaterunning")
|
|
||||||
msg.Respond(resp)
|
|
||||||
} else {
|
|
||||||
ret.Encode("ok")
|
|
||||||
msg.Respond(resp)
|
|
||||||
tactical.AgentUpdate(p.Data["url"], p.Data["inno"], p.Data["version"])
|
|
||||||
atomic.StoreUint32(&agentUpdateLocker, 0)
|
|
||||||
nc.Flush()
|
|
||||||
nc.Close()
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
}(payload)
|
|
||||||
|
|
||||||
case "uninstall":
|
|
||||||
go func(p *NatsMsg) {
|
|
||||||
var resp []byte
|
|
||||||
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
|
||||||
ret.Encode("ok")
|
|
||||||
msg.Respond(resp)
|
|
||||||
tactical.AgentUninstall(p.Code)
|
|
||||||
nc.Flush()
|
|
||||||
nc.Close()
|
|
||||||
os.Exit(0)
|
|
||||||
}(payload)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
nc.Flush()
|
|
||||||
|
|
||||||
if err := nc.LastError(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
wg.Wait()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func Start(version string, _ ksvc.Service) error {
|
|
||||||
go RunRPC(version)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
package rpc_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/amidaware/rmmagent/agent/tactical/rpc"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestRunRPC(t *testing.T) {
|
|
||||||
testTable := []struct {
|
|
||||||
name string
|
|
||||||
expectedError error
|
|
||||||
version string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "Run RPC",
|
|
||||||
expectedError: nil,
|
|
||||||
version: "development",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range testTable {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
err := rpc.RunRPC(tt.version)
|
|
||||||
if !errors.Is(tt.expectedError, err) {
|
|
||||||
t.Errorf("expected (%v), got (%v)", tt.expectedError, err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
package rpc
|
|
||||||
|
|
||||||
import "github.com/amidaware/rmmagent/agent/tasks"
|
|
||||||
|
|
||||||
type NatsMsg struct {
|
|
||||||
Func string `json:"func"`
|
|
||||||
Timeout int `json:"timeout"`
|
|
||||||
Data map[string]string `json:"payload"`
|
|
||||||
ScriptArgs []string `json:"script_args"`
|
|
||||||
ProcPID int32 `json:"procpid"`
|
|
||||||
TaskPK int `json:"taskpk"`
|
|
||||||
ScheduledTask tasks.SchedTask `json:"schedtaskpayload"`
|
|
||||||
RecoveryCommand string `json:"recoverycommand"`
|
|
||||||
UpdateGUIDs []string `json:"guids"`
|
|
||||||
ChocoProgName string `json:"choco_prog_name"`
|
|
||||||
PendingActionPK int `json:"pending_action_pk"`
|
|
||||||
PatchMgmt bool `json:"patch_mgmt"`
|
|
||||||
ID int `json:"id"`
|
|
||||||
Code string `json:"code"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type RawCMDResp struct {
|
|
||||||
Results string `json:"results"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type RunScriptResp struct {
|
|
||||||
Stdout string `json:"stdout"`
|
|
||||||
Stderr string `json:"stderr"`
|
|
||||||
Retcode int `json:"retcode"`
|
|
||||||
ExecTime float64 `json:"execution_time"`
|
|
||||||
ID int `json:"id"`
|
|
||||||
}
|
|
||||||
|
|
@ -2,20 +2,473 @@ package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/amidaware/rmmagent/agent/choco"
|
||||||
|
"github.com/amidaware/rmmagent/agent/events"
|
||||||
|
"github.com/amidaware/rmmagent/agent/network"
|
||||||
|
"github.com/amidaware/rmmagent/agent/patching"
|
||||||
|
"github.com/amidaware/rmmagent/agent/services"
|
||||||
|
"github.com/amidaware/rmmagent/agent/software"
|
||||||
|
"github.com/amidaware/rmmagent/agent/system"
|
||||||
|
"github.com/amidaware/rmmagent/agent/tactical"
|
||||||
"github.com/amidaware/rmmagent/agent/tactical/api"
|
"github.com/amidaware/rmmagent/agent/tactical/api"
|
||||||
"github.com/amidaware/rmmagent/agent/tactical/checks"
|
"github.com/amidaware/rmmagent/agent/tactical/checks"
|
||||||
"github.com/amidaware/rmmagent/agent/tactical/config"
|
"github.com/amidaware/rmmagent/agent/tactical/config"
|
||||||
"github.com/amidaware/rmmagent/agent/tactical/mesh"
|
"github.com/amidaware/rmmagent/agent/tactical/mesh"
|
||||||
"github.com/amidaware/rmmagent/agent/tactical/shared"
|
"github.com/amidaware/rmmagent/agent/tactical/shared"
|
||||||
|
ttasks "github.com/amidaware/rmmagent/agent/tactical/tasks"
|
||||||
|
"github.com/amidaware/rmmagent/agent/tasks"
|
||||||
"github.com/amidaware/rmmagent/agent/utils"
|
"github.com/amidaware/rmmagent/agent/utils"
|
||||||
|
ksvc "github.com/kardianos/service"
|
||||||
"github.com/nats-io/nats.go"
|
"github.com/nats-io/nats.go"
|
||||||
|
"github.com/ugorji/go/codec"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
agentUpdateLocker uint32
|
||||||
|
getWinUpdateLocker uint32
|
||||||
|
installWinUpdateLocker uint32
|
||||||
)
|
)
|
||||||
|
|
||||||
var natsCheckin = []string{"agent-hello", "agent-agentinfo", "agent-disks", "agent-winsvc", "agent-publicip", "agent-wmi"}
|
var natsCheckin = []string{"agent-hello", "agent-agentinfo", "agent-disks", "agent-winsvc", "agent-publicip", "agent-wmi"}
|
||||||
|
|
||||||
|
func RunRPC() error {
|
||||||
|
version := tactical.GetVersion()
|
||||||
|
config := config.NewAgentConfig()
|
||||||
|
go RunAsService(version)
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(1)
|
||||||
|
opts := SetupNatsOptions()
|
||||||
|
server := fmt.Sprintf("tls://%s:4222", config.APIURL)
|
||||||
|
nc, err := nats.Connect(server, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
nc.Subscribe(config.AgentID, func(msg *nats.Msg) {
|
||||||
|
var payload *NatsMsg
|
||||||
|
var mh codec.MsgpackHandle
|
||||||
|
mh.RawToString = true
|
||||||
|
|
||||||
|
dec := codec.NewDecoderBytes(msg.Data, &mh)
|
||||||
|
if err := dec.Decode(&payload); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch payload.Func {
|
||||||
|
case "ping":
|
||||||
|
go func() {
|
||||||
|
var resp []byte
|
||||||
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
ret.Encode("pong")
|
||||||
|
msg.Respond(resp)
|
||||||
|
}()
|
||||||
|
|
||||||
|
case "patchmgmt":
|
||||||
|
go func(p *NatsMsg) {
|
||||||
|
var resp []byte
|
||||||
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
err := patching.PatchMgmnt(p.PatchMgmt)
|
||||||
|
if err != nil {
|
||||||
|
ret.Encode(err.Error())
|
||||||
|
} else {
|
||||||
|
ret.Encode("ok")
|
||||||
|
}
|
||||||
|
msg.Respond(resp)
|
||||||
|
}(payload)
|
||||||
|
|
||||||
|
case "schedtask":
|
||||||
|
go func(p *NatsMsg) {
|
||||||
|
var resp []byte
|
||||||
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
success, err := tasks.CreateSchedTask(p.ScheduledTask)
|
||||||
|
if err != nil {
|
||||||
|
ret.Encode(err.Error())
|
||||||
|
} else if !success {
|
||||||
|
ret.Encode("Something went wrong")
|
||||||
|
} else {
|
||||||
|
ret.Encode("ok")
|
||||||
|
}
|
||||||
|
msg.Respond(resp)
|
||||||
|
}(payload)
|
||||||
|
|
||||||
|
case "delschedtask":
|
||||||
|
go func(p *NatsMsg) {
|
||||||
|
var resp []byte
|
||||||
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
err := tasks.DeleteSchedTask(p.ScheduledTask.Name)
|
||||||
|
if err != nil {
|
||||||
|
ret.Encode(err.Error())
|
||||||
|
} else {
|
||||||
|
ret.Encode("ok")
|
||||||
|
}
|
||||||
|
msg.Respond(resp)
|
||||||
|
}(payload)
|
||||||
|
|
||||||
|
case "listschedtasks":
|
||||||
|
go func() {
|
||||||
|
var resp []byte
|
||||||
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
tasks, _ := tasks.ListSchedTasks()
|
||||||
|
ret.Encode(tasks)
|
||||||
|
msg.Respond(resp)
|
||||||
|
}()
|
||||||
|
|
||||||
|
case "eventlog":
|
||||||
|
go func(p *NatsMsg) {
|
||||||
|
var resp []byte
|
||||||
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
days, _ := strconv.Atoi(p.Data["days"])
|
||||||
|
evtLog, _ := events.GetEventLog(p.Data["logname"], days)
|
||||||
|
ret.Encode(evtLog)
|
||||||
|
msg.Respond(resp)
|
||||||
|
}(payload)
|
||||||
|
|
||||||
|
case "procs":
|
||||||
|
go func() {
|
||||||
|
var resp []byte
|
||||||
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
procs := system.GetProcsRPC()
|
||||||
|
ret.Encode(procs)
|
||||||
|
msg.Respond(resp)
|
||||||
|
}()
|
||||||
|
|
||||||
|
case "killproc":
|
||||||
|
go func(p *NatsMsg) {
|
||||||
|
var resp []byte
|
||||||
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
err := system.KillProc(p.ProcPID)
|
||||||
|
if err != nil {
|
||||||
|
ret.Encode(err.Error())
|
||||||
|
} else {
|
||||||
|
ret.Encode("ok")
|
||||||
|
}
|
||||||
|
msg.Respond(resp)
|
||||||
|
}(payload)
|
||||||
|
|
||||||
|
case "rawcmd":
|
||||||
|
go func(p *NatsMsg) {
|
||||||
|
var resp []byte
|
||||||
|
var resultData RawCMDResp
|
||||||
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "windows":
|
||||||
|
out, _ := system.CMDShell(p.Data["shell"], []string{}, p.Data["command"], p.Timeout, false)
|
||||||
|
if out[1] != "" {
|
||||||
|
ret.Encode(out[1])
|
||||||
|
resultData.Results = out[1]
|
||||||
|
} else {
|
||||||
|
ret.Encode(out[0])
|
||||||
|
resultData.Results = out[0]
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
opts := system.NewCMDOpts()
|
||||||
|
opts.Shell = p.Data["shell"]
|
||||||
|
opts.Command = p.Data["command"]
|
||||||
|
opts.Timeout = time.Duration(p.Timeout)
|
||||||
|
out := system.CmdV2(opts)
|
||||||
|
tmp := ""
|
||||||
|
if len(out.Stdout) > 0 {
|
||||||
|
tmp += out.Stdout
|
||||||
|
}
|
||||||
|
if len(out.Stderr) > 0 {
|
||||||
|
tmp += "\n"
|
||||||
|
tmp += out.Stderr
|
||||||
|
}
|
||||||
|
ret.Encode(tmp)
|
||||||
|
resultData.Results = tmp
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.Respond(resp)
|
||||||
|
if p.ID != 0 {
|
||||||
|
api.Patch(resultData, fmt.Sprintf("/api/v3/%d/%s/histresult/", p.ID, config.AgentID))
|
||||||
|
}
|
||||||
|
}(payload)
|
||||||
|
|
||||||
|
case "winservices":
|
||||||
|
go func() {
|
||||||
|
var resp []byte
|
||||||
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
svcs, _, _ := services.GetServices()
|
||||||
|
ret.Encode(svcs)
|
||||||
|
msg.Respond(resp)
|
||||||
|
}()
|
||||||
|
|
||||||
|
case "winsvcdetail":
|
||||||
|
go func(p *NatsMsg) {
|
||||||
|
var resp []byte
|
||||||
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
svc := services.GetServiceDetail(p.Data["name"])
|
||||||
|
ret.Encode(svc)
|
||||||
|
msg.Respond(resp)
|
||||||
|
}(payload)
|
||||||
|
|
||||||
|
case "winsvcaction":
|
||||||
|
go func(p *NatsMsg) {
|
||||||
|
var resp []byte
|
||||||
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
retData := services.ControlService(p.Data["name"], p.Data["action"])
|
||||||
|
ret.Encode(retData)
|
||||||
|
msg.Respond(resp)
|
||||||
|
}(payload)
|
||||||
|
|
||||||
|
case "editwinsvc":
|
||||||
|
go func(p *NatsMsg) {
|
||||||
|
var resp []byte
|
||||||
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
retData := services.EditService(p.Data["name"], p.Data["startType"])
|
||||||
|
ret.Encode(retData)
|
||||||
|
msg.Respond(resp)
|
||||||
|
}(payload)
|
||||||
|
|
||||||
|
case "runscript":
|
||||||
|
go func(p *NatsMsg) {
|
||||||
|
var resp []byte
|
||||||
|
var retData string
|
||||||
|
var resultData RunScriptResp
|
||||||
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
start := time.Now()
|
||||||
|
stdout, stderr, retcode, err := system.RunScript(p.Data["code"], p.Data["shell"], p.ScriptArgs, p.Timeout)
|
||||||
|
resultData.ExecTime = time.Since(start).Seconds()
|
||||||
|
resultData.ID = p.ID
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
retData = err.Error()
|
||||||
|
resultData.Retcode = 1
|
||||||
|
resultData.Stderr = err.Error()
|
||||||
|
} else {
|
||||||
|
retData = stdout + stderr // to keep backwards compat
|
||||||
|
resultData.Retcode = retcode
|
||||||
|
resultData.Stdout = stdout
|
||||||
|
resultData.Stderr = stderr
|
||||||
|
}
|
||||||
|
//a.Logger.Debugln(retData)
|
||||||
|
ret.Encode(retData)
|
||||||
|
msg.Respond(resp)
|
||||||
|
if p.ID != 0 {
|
||||||
|
results := map[string]interface{}{"script_results": resultData}
|
||||||
|
api.Patch(results, fmt.Sprintf("/api/v3/%d/%s/histresult/", p.ID, config.AgentID))
|
||||||
|
}
|
||||||
|
}(payload)
|
||||||
|
|
||||||
|
case "runscriptfull":
|
||||||
|
go func(p *NatsMsg) {
|
||||||
|
var resp []byte
|
||||||
|
var retData RunScriptResp
|
||||||
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
start := time.Now()
|
||||||
|
stdout, stderr, retcode, err := system.RunScript(p.Data["code"], p.Data["shell"], p.ScriptArgs, p.Timeout)
|
||||||
|
|
||||||
|
retData.ExecTime = time.Since(start).Seconds()
|
||||||
|
if err != nil {
|
||||||
|
retData.Stderr = err.Error()
|
||||||
|
retData.Retcode = 1
|
||||||
|
} else {
|
||||||
|
retData.Stdout = stdout
|
||||||
|
retData.Stderr = stderr
|
||||||
|
retData.Retcode = retcode
|
||||||
|
}
|
||||||
|
retData.ID = p.ID
|
||||||
|
//a.Logger.Debugln(retData)
|
||||||
|
ret.Encode(retData)
|
||||||
|
msg.Respond(resp)
|
||||||
|
if p.ID != 0 {
|
||||||
|
results := map[string]interface{}{"script_results": retData}
|
||||||
|
api.Patch(results, fmt.Sprintf("/api/v3/%d/%s/histresult/", p.ID, config.AgentID))
|
||||||
|
}
|
||||||
|
}(payload)
|
||||||
|
|
||||||
|
case "recover":
|
||||||
|
go func(p *NatsMsg) {
|
||||||
|
var resp []byte
|
||||||
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
|
||||||
|
switch p.Data["mode"] {
|
||||||
|
case "mesh":
|
||||||
|
mesh.RecoverMesh()
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.Encode("ok")
|
||||||
|
msg.Respond(resp)
|
||||||
|
}(payload)
|
||||||
|
case "softwarelist":
|
||||||
|
go func() {
|
||||||
|
var resp []byte
|
||||||
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
sw, _ := software.GetInstalledSoftware()
|
||||||
|
ret.Encode(sw)
|
||||||
|
msg.Respond(resp)
|
||||||
|
}()
|
||||||
|
|
||||||
|
case "rebootnow":
|
||||||
|
go func() {
|
||||||
|
//a.Logger.Debugln("Scheduling immediate reboot")
|
||||||
|
var resp []byte
|
||||||
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
ret.Encode("ok")
|
||||||
|
msg.Respond(resp)
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
system.CMD("shutdown.exe", []string{"/r", "/t", "5", "/f"}, 15, false)
|
||||||
|
} else {
|
||||||
|
opts := system.NewCMDOpts()
|
||||||
|
opts.Command = "reboot"
|
||||||
|
system.CmdV2(opts)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
case "needsreboot":
|
||||||
|
go func() {
|
||||||
|
var resp []byte
|
||||||
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
out, err := system.SystemRebootRequired()
|
||||||
|
if err == nil {
|
||||||
|
ret.Encode(out)
|
||||||
|
} else {
|
||||||
|
ret.Encode(false)
|
||||||
|
}
|
||||||
|
msg.Respond(resp)
|
||||||
|
}()
|
||||||
|
case "sysinfo":
|
||||||
|
go func() {
|
||||||
|
var resp []byte
|
||||||
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
modes := []string{"agent-agentinfo", "agent-disks", "agent-wmi", "agent-publicip"}
|
||||||
|
for _, m := range modes {
|
||||||
|
NatsMessage(version, nc, m)
|
||||||
|
}
|
||||||
|
ret.Encode("ok")
|
||||||
|
msg.Respond(resp)
|
||||||
|
}()
|
||||||
|
case "wmi":
|
||||||
|
go func() {
|
||||||
|
NatsMessage(version, nc, "agent-wmi")
|
||||||
|
}()
|
||||||
|
case "cpuloadavg":
|
||||||
|
go func() {
|
||||||
|
var resp []byte
|
||||||
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
loadAvg := system.GetCPULoadAvg()
|
||||||
|
ret.Encode(loadAvg)
|
||||||
|
msg.Respond(resp)
|
||||||
|
}()
|
||||||
|
case "runchecks":
|
||||||
|
go func() {
|
||||||
|
var resp []byte
|
||||||
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
if checks.ChecksRunning() {
|
||||||
|
ret.Encode("busy")
|
||||||
|
msg.Respond(resp)
|
||||||
|
} else {
|
||||||
|
ret.Encode("ok")
|
||||||
|
msg.Respond(resp)
|
||||||
|
_, checkerr := system.CMD(shared.GetProgramBin(), []string{"-m", "runchecks"}, 600, false)
|
||||||
|
if checkerr != nil {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ret.Encode("ok")
|
||||||
|
msg.Respond(resp)
|
||||||
|
checks.RunChecks(config.AgentID, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
}()
|
||||||
|
case "runtask":
|
||||||
|
go func(p *NatsMsg) {
|
||||||
|
ttasks.RunTask(p.TaskPK)
|
||||||
|
}(payload)
|
||||||
|
|
||||||
|
case "publicip":
|
||||||
|
go func() {
|
||||||
|
var resp []byte
|
||||||
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
ret.Encode(network.PublicIP(config.Proxy))
|
||||||
|
msg.Respond(resp)
|
||||||
|
}()
|
||||||
|
case "installpython":
|
||||||
|
go shared.GetPython(true)
|
||||||
|
case "installchoco":
|
||||||
|
go choco.InstallChoco()
|
||||||
|
case "installwithchoco":
|
||||||
|
go func(p *NatsMsg) {
|
||||||
|
var resp []byte
|
||||||
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
ret.Encode("ok")
|
||||||
|
msg.Respond(resp)
|
||||||
|
out, _ := choco.InstallWithChoco(p.ChocoProgName)
|
||||||
|
results := map[string]string{"results": out}
|
||||||
|
url := fmt.Sprintf("/api/v3/%d/chocoresult/", p.PendingActionPK)
|
||||||
|
api.Patch(results, url)
|
||||||
|
}(payload)
|
||||||
|
case "getwinupdates":
|
||||||
|
go func() {
|
||||||
|
if !atomic.CompareAndSwapUint32(&getWinUpdateLocker, 0, 1) {
|
||||||
|
//a.Logger.Debugln("Already checking for windows updates")
|
||||||
|
} else {
|
||||||
|
//a.Logger.Debugln("Checking for windows updates")
|
||||||
|
defer atomic.StoreUint32(&getWinUpdateLocker, 0)
|
||||||
|
patching.GetUpdates()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
case "installwinupdates":
|
||||||
|
go func(p *NatsMsg) {
|
||||||
|
if !atomic.CompareAndSwapUint32(&installWinUpdateLocker, 0, 1) {
|
||||||
|
//a.Logger.Debugln("Already installing windows updates")
|
||||||
|
} else {
|
||||||
|
//a.Logger.Debugln("Installing windows updates", p.UpdateGUIDs)
|
||||||
|
defer atomic.StoreUint32(&installWinUpdateLocker, 0)
|
||||||
|
patching.InstallUpdates(p.UpdateGUIDs)
|
||||||
|
}
|
||||||
|
}(payload)
|
||||||
|
case "agentupdate":
|
||||||
|
go func(p *NatsMsg) {
|
||||||
|
var resp []byte
|
||||||
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
if !atomic.CompareAndSwapUint32(&agentUpdateLocker, 0, 1) {
|
||||||
|
//a.Logger.Debugln("Agent update already running")
|
||||||
|
ret.Encode("updaterunning")
|
||||||
|
msg.Respond(resp)
|
||||||
|
} else {
|
||||||
|
ret.Encode("ok")
|
||||||
|
msg.Respond(resp)
|
||||||
|
tactical.AgentUpdate(p.Data["url"], p.Data["inno"], p.Data["version"])
|
||||||
|
atomic.StoreUint32(&agentUpdateLocker, 0)
|
||||||
|
nc.Flush()
|
||||||
|
nc.Close()
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
}(payload)
|
||||||
|
|
||||||
|
case "uninstall":
|
||||||
|
go func(p *NatsMsg) {
|
||||||
|
var resp []byte
|
||||||
|
ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle))
|
||||||
|
ret.Encode("ok")
|
||||||
|
msg.Respond(resp)
|
||||||
|
tactical.AgentUninstall(p.Code)
|
||||||
|
nc.Flush()
|
||||||
|
nc.Close()
|
||||||
|
os.Exit(0)
|
||||||
|
}(payload)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
nc.Flush()
|
||||||
|
|
||||||
|
if err := nc.LastError(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func RunAsService(version string) {
|
func RunAsService(version string) {
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
|
|
@ -47,7 +500,7 @@ func AgentSvc(version string) error {
|
||||||
|
|
||||||
time.Sleep(time.Duration(utils.RandRange(1, 3)) * time.Second)
|
time.Sleep(time.Duration(utils.RandRange(1, 3)) * time.Second)
|
||||||
AgentStartup(config.AgentID)
|
AgentStartup(config.AgentID)
|
||||||
shared.SendSoftware()
|
SendSoftware()
|
||||||
|
|
||||||
checkInHelloTicker := time.NewTicker(time.Duration(utils.RandRange(30, 60)) * time.Second)
|
checkInHelloTicker := time.NewTicker(time.Duration(utils.RandRange(30, 60)) * time.Second)
|
||||||
checkInAgentInfoTicker := time.NewTicker(time.Duration(utils.RandRange(200, 400)) * time.Second)
|
checkInAgentInfoTicker := time.NewTicker(time.Duration(utils.RandRange(200, 400)) * time.Second)
|
||||||
|
|
@ -71,7 +524,7 @@ func AgentSvc(version string) error {
|
||||||
case <-checkInDisksTicker.C:
|
case <-checkInDisksTicker.C:
|
||||||
NatsMessage(version, nc, "agent-disks")
|
NatsMessage(version, nc, "agent-disks")
|
||||||
case <-checkInSWTicker.C:
|
case <-checkInSWTicker.C:
|
||||||
shared.SendSoftware()
|
SendSoftware()
|
||||||
case <-checkInWMITicker.C:
|
case <-checkInWMITicker.C:
|
||||||
NatsMessage(version, nc, "agent-wmi")
|
NatsMessage(version, nc, "agent-wmi")
|
||||||
case <-syncMeshTicker.C:
|
case <-syncMeshTicker.C:
|
||||||
|
|
@ -113,3 +566,22 @@ func AgentStartup(agentID string) error {
|
||||||
err := api.PostPayload(payload, "/api/v3/checkin/")
|
err := api.PostPayload(payload, "/api/v3/checkin/")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SendSoftware() error {
|
||||||
|
config := config.NewAgentConfig()
|
||||||
|
sw, _ := software.GetInstalledSoftware()
|
||||||
|
payload := map[string]interface{}{"agent_id": config.AgentID, "software": sw}
|
||||||
|
err := api.PostPayload(payload, "/api/v3/software/")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r IService) Start(_ ksvc.Service) error {
|
||||||
|
go RunRPC()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r IService) Stop(_ ksvc.Service) error { return nil }
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,10 @@ import (
|
||||||
"github.com/amidaware/rmmagent/agent/system"
|
"github.com/amidaware/rmmagent/agent/system"
|
||||||
"github.com/amidaware/rmmagent/agent/tactical/config"
|
"github.com/amidaware/rmmagent/agent/tactical/config"
|
||||||
"github.com/amidaware/rmmagent/agent/wmi"
|
"github.com/amidaware/rmmagent/agent/wmi"
|
||||||
|
ksvc "github.com/kardianos/service"
|
||||||
"github.com/nats-io/nats.go"
|
"github.com/nats-io/nats.go"
|
||||||
"github.com/ugorji/go/codec"
|
"github.com/ugorji/go/codec"
|
||||||
|
"golang.org/x/sys/windows/registry"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NatsMessage(version string, nc *nats.Conn, mode string) {
|
func NatsMessage(version string, nc *nats.Conn, mode string) {
|
||||||
|
|
@ -69,4 +71,24 @@ func NatsMessage(version string, nc *nats.Conn, mode string) {
|
||||||
|
|
||||||
ret.Encode(payload)
|
ret.Encode(payload)
|
||||||
nc.PublishRequest(config.AgentID, mode, resp)
|
nc.PublishRequest(config.AgentID, mode, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func InstallService(name string, svc IService, config *ksvc.Config) error {
|
||||||
|
exists, err := services.ServiceExists(name)
|
||||||
|
if exists {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip on first call of inno setup if this is a new install
|
||||||
|
_, err = registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\TacticalRMM`, registry.ALL_ACCESS)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
s, err := ksvc.New(svc, config)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ksvc.Control(s, "install")
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,11 @@ package service
|
||||||
import (
|
import (
|
||||||
"github.com/amidaware/rmmagent/agent/disk"
|
"github.com/amidaware/rmmagent/agent/disk"
|
||||||
"github.com/amidaware/rmmagent/agent/services"
|
"github.com/amidaware/rmmagent/agent/services"
|
||||||
|
"github.com/amidaware/rmmagent/agent/tasks"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type IService struct{}
|
||||||
|
|
||||||
type WinSvcNats struct {
|
type WinSvcNats struct {
|
||||||
Agentid string `json:"agent_id"`
|
Agentid string `json:"agent_id"`
|
||||||
WinSvcs []services.Service `json:"services"`
|
WinSvcs []services.Service `json:"services"`
|
||||||
|
|
@ -41,3 +44,32 @@ type PublicIPNats struct {
|
||||||
Agentid string `json:"agent_id"`
|
Agentid string `json:"agent_id"`
|
||||||
PublicIP string `json:"public_ip"`
|
PublicIP string `json:"public_ip"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type NatsMsg struct {
|
||||||
|
Func string `json:"func"`
|
||||||
|
Timeout int `json:"timeout"`
|
||||||
|
Data map[string]string `json:"payload"`
|
||||||
|
ScriptArgs []string `json:"script_args"`
|
||||||
|
ProcPID int32 `json:"procpid"`
|
||||||
|
TaskPK int `json:"taskpk"`
|
||||||
|
ScheduledTask tasks.SchedTask `json:"schedtaskpayload"`
|
||||||
|
RecoveryCommand string `json:"recoverycommand"`
|
||||||
|
UpdateGUIDs []string `json:"guids"`
|
||||||
|
ChocoProgName string `json:"choco_prog_name"`
|
||||||
|
PendingActionPK int `json:"pending_action_pk"`
|
||||||
|
PatchMgmt bool `json:"patch_mgmt"`
|
||||||
|
ID int `json:"id"`
|
||||||
|
Code string `json:"code"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RawCMDResp struct {
|
||||||
|
Results string `json:"results"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RunScriptResp struct {
|
||||||
|
Stdout string `json:"stdout"`
|
||||||
|
Stderr string `json:"stderr"`
|
||||||
|
Retcode int `json:"retcode"`
|
||||||
|
ExecTime float64 `json:"execution_time"`
|
||||||
|
ID int `json:"id"`
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
package shared
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/amidaware/rmmagent/agent/software"
|
|
||||||
"github.com/amidaware/rmmagent/agent/tactical/api"
|
|
||||||
"github.com/amidaware/rmmagent/agent/tactical/config"
|
|
||||||
)
|
|
||||||
|
|
||||||
func SendSoftware() error {
|
|
||||||
config := config.NewAgentConfig()
|
|
||||||
sw, _ := software.GetInstalledSoftware()
|
|
||||||
payload := map[string]interface{}{"agent_id": config.AgentID, "software": sw}
|
|
||||||
err := api.PostPayload(payload, "/api/v3/software/")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
@ -7,14 +7,18 @@ import (
|
||||||
"runtime"
|
"runtime"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/amidaware/rmmagent/agent/system"
|
|
||||||
"github.com/amidaware/rmmagent/agent/tactical/config"
|
"github.com/amidaware/rmmagent/agent/tactical/config"
|
||||||
"github.com/amidaware/rmmagent/agent/utils"
|
"github.com/amidaware/rmmagent/agent/utils"
|
||||||
"github.com/go-resty/resty/v2"
|
"github.com/go-resty/resty/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ProgFilesName = "TacticalAgent"
|
||||||
|
winExeName = "tacticalrmm.exe"
|
||||||
|
)
|
||||||
|
|
||||||
func GetPython(force bool) {
|
func GetPython(force bool) {
|
||||||
if utils.FileExists(system.GetPythonBin()) && !force {
|
if utils.FileExists(GetPythonBin()) && !force {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -28,8 +32,8 @@ func GetPython(force bool) {
|
||||||
archZip = "py38-x32.zip"
|
archZip = "py38-x32.zip"
|
||||||
folder = "py38-x32"
|
folder = "py38-x32"
|
||||||
}
|
}
|
||||||
pyFolder := filepath.Join(system.GetProgramDirectory(), folder)
|
pyFolder := filepath.Join(GetProgramDirectory(), folder)
|
||||||
pyZip := filepath.Join(system.GetProgramDirectory(), archZip)
|
pyZip := filepath.Join(GetProgramDirectory(), archZip)
|
||||||
defer os.Remove(pyZip)
|
defer os.Remove(pyZip)
|
||||||
|
|
||||||
if force {
|
if force {
|
||||||
|
|
@ -55,16 +59,38 @@ func GetPython(force bool) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = utils.Unzip(pyZip, system.GetProgramDirectory())
|
err = utils.Unzip(pyZip, GetProgramDirectory())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func RunMigrations() {
|
func RunMigrations() {
|
||||||
for _, i := range []string{"nssm.exe", "nssm-x86.exe"} {
|
for _, i := range []string{"nssm.exe", "nssm-x86.exe"} {
|
||||||
nssm := filepath.Join(system.GetProgramDirectory(), i)
|
nssm := filepath.Join(GetProgramDirectory(), i)
|
||||||
if utils.FileExists(nssm) {
|
if utils.FileExists(nssm) {
|
||||||
os.Remove(nssm)
|
os.Remove(nssm)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetPythonBin() string {
|
||||||
|
var pybin string
|
||||||
|
switch runtime.GOARCH {
|
||||||
|
case "amd64":
|
||||||
|
pybin = filepath.Join(GetProgramDirectory(), "py38-x64", "python.exe")
|
||||||
|
case "386":
|
||||||
|
pybin = filepath.Join(GetProgramDirectory(), "py38-x32", "python.exe")
|
||||||
|
}
|
||||||
|
|
||||||
|
return pybin
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetProgramDirectory() string {
|
||||||
|
pd := filepath.Join(os.Getenv("ProgramFiles"), ProgFilesName)
|
||||||
|
return pd
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetProgramBin() string {
|
||||||
|
exe := filepath.Join(GetProgramDirectory(), winExeName)
|
||||||
|
return exe
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
package tactical
|
|
||||||
|
|
||||||
|
|
@ -1,2 +1,12 @@
|
||||||
package tactical_test
|
package tactical_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/amidaware/rmmagent/agent/tactical"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetVersion(t *testing.T) {
|
||||||
|
version := tactical.GetVersion()
|
||||||
|
t.Logf("got version %s", version)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,14 +13,34 @@ import (
|
||||||
"github.com/amidaware/rmmagent/agent/services"
|
"github.com/amidaware/rmmagent/agent/services"
|
||||||
"github.com/amidaware/rmmagent/agent/system"
|
"github.com/amidaware/rmmagent/agent/system"
|
||||||
"github.com/amidaware/rmmagent/agent/tactical/config"
|
"github.com/amidaware/rmmagent/agent/tactical/config"
|
||||||
|
"github.com/amidaware/rmmagent/agent/tactical/shared"
|
||||||
"github.com/amidaware/rmmagent/agent/tasks"
|
"github.com/amidaware/rmmagent/agent/tasks"
|
||||||
"github.com/amidaware/rmmagent/agent/utils"
|
"github.com/amidaware/rmmagent/agent/utils"
|
||||||
rmm "github.com/amidaware/rmmagent/shared"
|
rmm "github.com/amidaware/rmmagent/shared"
|
||||||
"github.com/go-resty/resty/v2"
|
"github.com/go-resty/resty/v2"
|
||||||
|
"github.com/gonutz/w32/v2"
|
||||||
"golang.org/x/sys/windows"
|
"golang.org/x/sys/windows"
|
||||||
"golang.org/x/sys/windows/registry"
|
"golang.org/x/sys/windows/registry"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func GetVersion() string {
|
||||||
|
path := shared.GetProgramBin()
|
||||||
|
size := w32.GetFileVersionInfoSize(path)
|
||||||
|
info := make([]byte, size)
|
||||||
|
w32.GetFileVersionInfo(path, info)
|
||||||
|
fixed, _ := w32.VerQueryValueRoot(info)
|
||||||
|
version := fixed.FileVersion()
|
||||||
|
stringVersion := fmt.Sprintf(
|
||||||
|
"%d.%d.%d",
|
||||||
|
version&0xFFFF000000000000>>48,
|
||||||
|
version&0x0000FFFF00000000>>32,
|
||||||
|
version&0x00000000FFFF0000>>16,
|
||||||
|
//drop last digit version&0x000000000000FFFF>>0,
|
||||||
|
)
|
||||||
|
|
||||||
|
return stringVersion
|
||||||
|
}
|
||||||
|
|
||||||
func UninstallCleanup() {
|
func UninstallCleanup() {
|
||||||
registry.DeleteKey(registry.LOCAL_MACHINE, `SOFTWARE\TacticalRMM`)
|
registry.DeleteKey(registry.LOCAL_MACHINE, `SOFTWARE\TacticalRMM`)
|
||||||
patching.PatchMgmnt(false)
|
patching.PatchMgmnt(false)
|
||||||
|
|
@ -32,7 +52,7 @@ func AgentUpdate(url, inno, version string) {
|
||||||
time.Sleep(time.Duration(utils.RandRange(1, 15)) * time.Second)
|
time.Sleep(time.Duration(utils.RandRange(1, 15)) * time.Second)
|
||||||
system.KillHungUpdates()
|
system.KillHungUpdates()
|
||||||
CleanupAgentUpdates()
|
CleanupAgentUpdates()
|
||||||
updater := filepath.Join(system.GetProgramDirectory(), inno)
|
updater := filepath.Join(shared.GetProgramDirectory(), inno)
|
||||||
config := config.NewAgentConfig()
|
config := config.NewAgentConfig()
|
||||||
rClient := resty.New()
|
rClient := resty.New()
|
||||||
rClient.SetCloseConnection(true)
|
rClient.SetCloseConnection(true)
|
||||||
|
|
@ -71,7 +91,7 @@ func AgentUpdate(url, inno, version string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func CleanupAgentUpdates() {
|
func CleanupAgentUpdates() {
|
||||||
pd := filepath.Join(os.Getenv("ProgramFiles"), system.ProgFilesName)
|
pd := filepath.Join(os.Getenv("ProgramFiles"), shared.ProgFilesName)
|
||||||
cderr := os.Chdir(pd)
|
cderr := os.Chdir(pd)
|
||||||
if cderr != nil {
|
if cderr != nil {
|
||||||
return
|
return
|
||||||
|
|
@ -99,7 +119,7 @@ func CleanupAgentUpdates() {
|
||||||
|
|
||||||
func AgentUninstall(code string) {
|
func AgentUninstall(code string) {
|
||||||
system.KillHungUpdates()
|
system.KillHungUpdates()
|
||||||
tacUninst := filepath.Join(system.GetProgramDirectory(), GetUninstallExe())
|
tacUninst := filepath.Join(shared.GetProgramDirectory(), GetUninstallExe())
|
||||||
args := []string{"/C", tacUninst, "/VERYSILENT"}
|
args := []string{"/C", tacUninst, "/VERYSILENT"}
|
||||||
cmd := exec.Command("cmd.exe", args...)
|
cmd := exec.Command("cmd.exe", args...)
|
||||||
cmd.SysProcAttr = &windows.SysProcAttr{
|
cmd.SysProcAttr = &windows.SysProcAttr{
|
||||||
|
|
@ -109,7 +129,7 @@ func AgentUninstall(code string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetUninstallExe() string {
|
func GetUninstallExe() string {
|
||||||
cderr := os.Chdir(system.GetProgramDirectory())
|
cderr := os.Chdir(shared.GetProgramDirectory())
|
||||||
if cderr == nil {
|
if cderr == nil {
|
||||||
files, err := filepath.Glob("unins*.exe")
|
files, err := filepath.Glob("unins*.exe")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -186,3 +187,13 @@ func FileExists(path string) bool {
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTCP(addr string) error {
|
||||||
|
conn, err := net.Dial("tcp4", addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer conn.Close()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
||||||
2
main.go
2
main.go
|
|
@ -1,3 +1,5 @@
|
||||||
|
//go:generate goversioninfo
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Copyright 2022 AmidaWare LLC.
|
Copyright 2022 AmidaWare LLC.
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue