diff --git a/agent/agent.go b/agent/agent.go index 2634c9f..ddf69e2 100644 --- a/agent/agent.go +++ b/agent/agent.go @@ -24,7 +24,6 @@ import ( "runtime" "strconv" "strings" - "time" rmm "github.com/amidaware/rmmagent/shared" @@ -85,15 +84,15 @@ func New(logger *logrus.Logger, version string) *Agent { restyC.SetRootCertificate(ac.Cert) } - var MeshSysBin string + var MeshSysExe string if len(ac.CustomMeshDir) > 0 { - MeshSysBin = filepath.Join(ac.CustomMeshDir, "MeshAgent.exe") + MeshSysExe = filepath.Join(ac.CustomMeshDir, "MeshAgent.exe") } else { - MeshSysBin = filepath.Join(os.Getenv("ProgramFiles"), "Mesh Agent", "MeshAgent.exe") + MeshSysExe = filepath.Join(os.Getenv("ProgramFiles"), "Mesh Agent", "MeshAgent.exe") } if runtime.GOOS == "linux" { - MeshSysBin = "/opt/tacticalmesh/meshagent" + MeshSysExe = "/opt/tacticalmesh/meshagent" } svcConf := &service.Config{ @@ -112,7 +111,6 @@ func New(logger *logrus.Logger, version string) *Agent { return &Agent{ Hostname: info.Hostname, - Arch: info.Architecture, BaseURL: ac.BaseURL, AgentID: ac.AgentID, ApiURL: ac.APIURL, @@ -123,7 +121,7 @@ func New(logger *logrus.Logger, version string) *Agent { EXE: exe, SystemDrive: sd, MeshInstaller: "meshagent.exe", - MeshSystemBin: MeshSysBin, + MeshSystemBin: MeshSysExe, MeshSVC: meshSvcName, PyBin: pybin, Headers: headers, diff --git a/agent/agent_linux.go b/agent/agent_linux.go deleted file mode 100644 index 8476e54..0000000 --- a/agent/agent_linux.go +++ /dev/null @@ -1,92 +0,0 @@ -package agent - -func New(logger *logrus.Logger, version string) *Agent { - host, _ := ps.Host() - info := host.Info() - pd := filepath.Join(os.Getenv("ProgramFiles"), progFilesName) - exe := filepath.Join(pd, winExeName) - sd := os.Getenv("SystemDrive") - - var pybin string - switch runtime.GOARCH { - case "amd64": - pybin = filepath.Join(pd, "py38-x64", "python.exe") - case "386": - pybin = filepath.Join(pd, "py38-x32", "python.exe") - } - - ac := NewAgentConfig() - - headers := make(map[string]string) - if len(ac.Token) > 0 { - headers["Content-Type"] = "application/json" - headers["Authorization"] = fmt.Sprintf("Token %s", ac.Token) - } - - restyC := resty.New() - restyC.SetBaseURL(ac.BaseURL) - restyC.SetCloseConnection(true) - restyC.SetHeaders(headers) - restyC.SetTimeout(15 * time.Second) - restyC.SetDebug(logger.IsLevelEnabled(logrus.DebugLevel)) - - if len(ac.Proxy) > 0 { - restyC.SetProxy(ac.Proxy) - } - - if len(ac.Cert) > 0 { - restyC.SetRootCertificate(ac.Cert) - } - - var MeshSysBin string - if len(ac.CustomMeshDir) > 0 { - MeshSysBin = filepath.Join(ac.CustomMeshDir, "MeshAgent.exe") - } else { - MeshSysBin = filepath.Join(os.Getenv("ProgramFiles"), "Mesh Agent", "MeshAgent.exe") - } - - if runtime.GOOS == "linux" { - MeshSysBin = "/opt/tacticalmesh/meshagent" - } - - svcConf := &service.Config{ - Executable: exe, - Name: winSvcName, - DisplayName: "TacticalRMM Agent Service", - Arguments: []string{"-m", "svc"}, - Description: "TacticalRMM Agent Service", - Option: service.KeyValue{ - "StartType": "automatic", - "OnFailure": "restart", - "OnFailureDelayDuration": "5s", - "OnFailureResetPeriod": 10, - }, - } - - return &Agent{ - Hostname: info.Hostname, - Arch: info.Architecture, - BaseURL: ac.BaseURL, - AgentID: ac.AgentID, - ApiURL: ac.APIURL, - Token: ac.Token, - AgentPK: ac.PK, - Cert: ac.Cert, - ProgramDir: pd, - EXE: exe, - SystemDrive: sd, - MeshInstaller: "meshagent.exe", - MeshSystemBin: MeshSysBin, - MeshSVC: meshSvcName, - PyBin: pybin, - Headers: headers, - Logger: logger, - Version: version, - Debug: logger.IsLevelEnabled(logrus.DebugLevel), - rClient: restyC, - Proxy: ac.Proxy, - Platform: runtime.GOOS, - GoArch: runtime.GOARCH, - ServiceConfig: svcConf, - } -} \ No newline at end of file diff --git a/agent/agent_unix.go b/agent/agent_unix.go new file mode 100644 index 0000000..12d96a8 --- /dev/null +++ b/agent/agent_unix.go @@ -0,0 +1,499 @@ +//go:build !windows +// +build !windows + +/* +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 ( + "bufio" + "fmt" + "os" + "runtime" + "strconv" + "strings" + "syscall" + "time" + + rmm "github.com/amidaware/rmmagent/shared" + ps "github.com/elastic/go-sysinfo" + "github.com/go-resty/resty/v2" + "github.com/jaypipes/ghw" + "github.com/kardianos/service" + "github.com/shirou/gopsutil/v3/cpu" + "github.com/shirou/gopsutil/v3/disk" + psHost "github.com/shirou/gopsutil/v3/host" + "github.com/spf13/viper" + trmm "github.com/wh1te909/trmm-shared" +) + +func ShowStatus(version string) { + fmt.Println(version) +} + +func (a *Agent) GetDisks() []trmm.Disk { + ret := make([]trmm.Disk, 0) + partitions, err := disk.Partitions(false) + if err != nil { + a.Logger.Debugln(err) + return ret + } + + for _, p := range partitions { + if strings.Contains(p.Device, "dev/loop") { + continue + } + usage, err := disk.Usage(p.Mountpoint) + if err != nil { + a.Logger.Debugln(err) + continue + } + + d := trmm.Disk{ + Device: p.Device, + Fstype: p.Fstype, + Total: ByteCountSI(usage.Total), + Used: ByteCountSI(usage.Used), + Free: ByteCountSI(usage.Free), + Percent: int(usage.UsedPercent), + } + ret = append(ret, d) + + } + return ret +} + +func (a *Agent) SystemRebootRequired() (bool, error) { + // deb + paths := [2]string{"/var/run/reboot-required", "/run/reboot-required"} + for _, p := range paths { + if trmm.FileExists(p) { + return true, nil + } + } + // rhel + bins := [2]string{"/usr/bin/needs-restarting", "/bin/needs-restarting"} + for _, bin := range bins { + if trmm.FileExists(bin) { + opts := a.NewCMDOpts() + // https://man7.org/linux/man-pages/man1/needs-restarting.1.html + // -r Only report whether a full reboot is required (exit code 1) or not (exit code 0). + opts.Command = fmt.Sprintf("%s -r", bin) + out := a.CmdV2(opts) + + if out.Status.Error != nil { + a.Logger.Debugln("SystemRebootRequired(): ", out.Status.Error.Error()) + continue + } + + if out.Status.Exit == 1 { + return true, nil + } + + return false, nil + } + } + return false, nil +} + +func (a *Agent) LoggedOnUser() string { + var ret string + users, err := psHost.Users() + if err != nil { + return ret + } + + // return the first logged in user + for _, user := range users { + if user.User != "" { + ret = user.User + break + } + } + return ret +} + +func (a *Agent) osString() string { + h, err := psHost.Info() + if err != nil { + return "error getting host info" + } + return fmt.Sprintf("%s %s %s %s", strings.Title(h.Platform), h.PlatformVersion, h.KernelArch, h.KernelVersion) +} + +func NewAgentConfig() *rmm.AgentConfig { + viper.SetConfigName("tacticalagent") + viper.SetConfigType("json") + viper.AddConfigPath("/etc/") + viper.AddConfigPath(".") + err := viper.ReadInConfig() + + if err != nil { + return &rmm.AgentConfig{} + } + + agentpk := viper.GetString("agentpk") + pk, _ := strconv.Atoi(agentpk) + + ret := &rmm.AgentConfig{ + BaseURL: viper.GetString("baseurl"), + AgentID: viper.GetString("agentid"), + APIURL: viper.GetString("apiurl"), + Token: viper.GetString("token"), + AgentPK: agentpk, + PK: pk, + Cert: viper.GetString("cert"), + Proxy: viper.GetString("proxy"), + CustomMeshDir: viper.GetString("meshdir"), + } + return ret +} + +func (a *Agent) RunScript(code string, shell string, args []string, timeout int) (stdout, stderr string, exitcode int, e error) { + code = removeWinNewLines(code) + content := []byte(code) + + f, err := createTmpFile() + if err != nil { + a.Logger.Errorln("RunScript createTmpFile()", err) + return "", err.Error(), 85, err + } + defer os.Remove(f.Name()) + + if _, err := f.Write(content); err != nil { + a.Logger.Errorln(err) + return "", err.Error(), 85, err + } + + if err := f.Close(); err != nil { + a.Logger.Errorln(err) + return "", err.Error(), 85, err + } + + if err := os.Chmod(f.Name(), 0770); err != nil { + a.Logger.Errorln(err) + return "", err.Error(), 85, err + } + + opts := a.NewCMDOpts() + opts.IsScript = true + opts.Shell = f.Name() + opts.Args = args + opts.Timeout = time.Duration(timeout) + out := a.CmdV2(opts) + retError := "" + if out.Status.Error != nil { + retError += CleanString(out.Status.Error.Error()) + retError += "\n" + } + if len(out.Stderr) > 0 { + retError += out.Stderr + } + return out.Stdout, retError, out.Status.Exit, nil +} + +func SetDetached() *syscall.SysProcAttr { + return &syscall.SysProcAttr{Setpgid: true} +} + +func (a *Agent) AgentUpdate(url, inno, version string) { + + self, err := os.Executable() + if err != nil { + a.Logger.Errorln("AgentUpdate() os.Executable():", err) + return + } + + f, err := createTmpFile() + if err != nil { + a.Logger.Errorln("AgentUpdate createTmpFile()", err) + return + } + defer os.Remove(f.Name()) + + a.Logger.Infof("Agent updating from %s to %s", a.Version, version) + a.Logger.Infoln("Downloading agent update from", url) + + rClient := resty.New() + rClient.SetCloseConnection(true) + rClient.SetTimeout(15 * time.Minute) + rClient.SetDebug(a.Debug) + if len(a.Proxy) > 0 { + rClient.SetProxy(a.Proxy) + } + + r, err := rClient.R().SetOutput(f.Name()).Get(url) + if err != nil { + a.Logger.Errorln("AgentUpdate() download:", err) + f.Close() + return + } + if r.IsError() { + a.Logger.Errorln("AgentUpdate() status code:", r.StatusCode()) + f.Close() + return + } + + f.Close() + os.Chmod(f.Name(), 0755) + err = os.Rename(f.Name(), self) + if err != nil { + a.Logger.Errorln("AgentUpdate() os.Rename():", err) + return + } + + opts := a.NewCMDOpts() + opts.Detached = true + opts.Command = "systemctl restart tacticalagent.service" + a.CmdV2(opts) +} + +func (a *Agent) AgentUninstall(code string) { + f, err := createTmpFile() + if err != nil { + a.Logger.Errorln("AgentUninstall createTmpFile():", err) + return + } + + f.Write([]byte(code)) + f.Close() + os.Chmod(f.Name(), 0770) + + opts := a.NewCMDOpts() + opts.IsScript = true + opts.Shell = f.Name() + opts.Args = []string{"uninstall"} + opts.Detached = true + a.CmdV2(opts) +} + +func (a *Agent) NixMeshNodeID() string { + var meshNodeID string + meshSuccess := false + a.Logger.Debugln("Getting mesh node id") + + if !trmm.FileExists(a.MeshSystemBin) { + a.Logger.Debugln(a.MeshSystemBin, "does not exist. Skipping.") + return "" + } + + opts := a.NewCMDOpts() + opts.IsExecutable = true + opts.Shell = a.MeshSystemBin + opts.Command = "-nodeid" + + for !meshSuccess { + out := a.CmdV2(opts) + meshNodeID = out.Stdout + a.Logger.Debugln("Stdout:", out.Stdout) + a.Logger.Debugln("Stderr:", out.Stderr) + if meshNodeID == "" { + time.Sleep(1 * time.Second) + continue + } else if strings.Contains(strings.ToLower(meshNodeID), "graphical version") || strings.Contains(strings.ToLower(meshNodeID), "zenity") { + time.Sleep(1 * time.Second) + continue + } + meshSuccess = true + } + return meshNodeID +} + +func (a *Agent) getMeshNodeID() (string, error) { + return a.NixMeshNodeID(), nil +} + +func (a *Agent) RecoverMesh() { + a.Logger.Infoln("Attempting mesh recovery") + opts := a.NewCMDOpts() + opts.Command = "systemctl restart meshagent.service" + a.CmdV2(opts) + a.SyncMeshNodeID() +} + +func (a *Agent) GetWMIInfo() map[string]interface{} { + wmiInfo := make(map[string]interface{}) + ips := make([]string, 0) + disks := make([]string, 0) + cpus := make([]string, 0) + gpus := make([]string, 0) + + // local ips + host, err := ps.Host() + if err != nil { + a.Logger.Errorln("GetWMIInfo() ps.Host()", err) + } else { + for _, ip := range host.Info().IPs { + if strings.Contains(ip, "127.0.") || strings.Contains(ip, "::1/128") { + continue + } + ips = append(ips, ip) + } + } + wmiInfo["local_ips"] = ips + + // disks + block, err := ghw.Block(ghw.WithDisableWarnings()) + if err != nil { + a.Logger.Errorln("ghw.Block()", err) + } else { + for _, disk := range block.Disks { + if disk.IsRemovable || strings.Contains(disk.Name, "ram") { + continue + } + ret := fmt.Sprintf("%s %s %s %s %s %s", disk.Vendor, disk.Model, disk.StorageController, disk.DriveType, disk.Name, ByteCountSI(disk.SizeBytes)) + ret = strings.TrimSpace(strings.ReplaceAll(ret, "unknown", "")) + disks = append(disks, ret) + } + } + wmiInfo["disks"] = disks + + // cpus + cpuInfo, err := cpu.Info() + if err != nil { + a.Logger.Errorln("cpu.Info()", err) + } else { + if len(cpuInfo) > 0 { + if cpuInfo[0].ModelName != "" { + cpus = append(cpus, cpuInfo[0].ModelName) + } + } + } + wmiInfo["cpus"] = cpus + + // make/model + wmiInfo["make_model"] = "" + chassis, err := ghw.Chassis(ghw.WithDisableWarnings()) + if err != nil { + a.Logger.Errorln("ghw.Chassis()", err) + } else { + if chassis.Vendor != "" || chassis.Version != "" { + wmiInfo["make_model"] = fmt.Sprintf("%s %s", chassis.Vendor, chassis.Version) + } + } + + // gfx cards + + gpu, err := ghw.GPU(ghw.WithDisableWarnings()) + if err != nil { + a.Logger.Errorln("ghw.GPU()", err) + } else { + for _, i := range gpu.GraphicsCards { + if i.DeviceInfo != nil { + ret := fmt.Sprintf("%s %s", i.DeviceInfo.Vendor.Name, i.DeviceInfo.Product.Name) + gpus = append(gpus, ret) + } + + } + } + wmiInfo["gpus"] = gpus + + // temp hack for ARM cpu/make/model if rasp pi + var makeModel string + if strings.Contains(runtime.GOARCH, "arm") { + file, _ := os.Open("/proc/cpuinfo") + scanner := bufio.NewScanner(file) + for scanner.Scan() { + if strings.Contains(strings.ToLower(scanner.Text()), "raspberry") { + model := strings.Split(scanner.Text(), ":") + if len(model) == 2 { + makeModel = strings.TrimSpace(model[1]) + break + } + } + } + } + + if len(cpus) == 0 { + wmiInfo["cpus"] = []string{makeModel} + } + if makeModel != "" && (wmiInfo["make_model"] == "" || wmiInfo["make_model"] == "unknown unknown") { + wmiInfo["make_model"] = makeModel + } + if len(gpus) == 1 && gpus[0] == "unknown unknown" { + wmiInfo["gpus"] = "" + } + + return wmiInfo +} + +// windows only below TODO add into stub file + +func (a *Agent) PlatVer() (string, error) { return "", nil } + +func (a *Agent) SendSoftware() {} + +func (a *Agent) UninstallCleanup() {} + +func (a *Agent) RunMigrations() {} + +func GetServiceStatus(name string) (string, error) { return "", nil } + +func (a *Agent) GetPython(force bool) {} + +type SchedTask struct{ Name string } + +func (a *Agent) PatchMgmnt(enable bool) error { return nil } + +func (a *Agent) CreateSchedTask(st SchedTask) (bool, error) { return false, nil } + +func DeleteSchedTask(name string) error { return nil } + +func ListSchedTasks() []string { return []string{} } + +func (a *Agent) GetEventLog(logName string, searchLastDays int) []rmm.EventLogMsg { + return []rmm.EventLogMsg{} +} + +func (a *Agent) GetServiceDetail(name string) trmm.WindowsService { return trmm.WindowsService{} } + +func (a *Agent) ControlService(name, action string) rmm.WinSvcResp { + return rmm.WinSvcResp{Success: false, ErrorMsg: "/na"} +} + +func (a *Agent) EditService(name, startupType string) rmm.WinSvcResp { + return rmm.WinSvcResp{Success: false, ErrorMsg: "/na"} +} + +func (a *Agent) GetInstalledSoftware() []trmm.WinSoftwareList { return []trmm.WinSoftwareList{} } + +func (a *Agent) ChecksRunning() bool { return false } + +func (a *Agent) RunTask(id int) error { return nil } + +func (a *Agent) InstallChoco() {} + +func (a *Agent) InstallWithChoco(name string) (string, error) { return "", nil } + +func (a *Agent) GetWinUpdates() {} + +func (a *Agent) InstallUpdates(guids []string) {} + +func (a *Agent) installMesh(meshbin, exe, proxy string) (string, error) { + return "not implemented", nil +} + +func CMDShell(shell string, cmdArgs []string, command string, timeout int, detached bool) (output [2]string, e error) { + return [2]string{"", ""}, nil +} + +func CMD(exe string, args []string, timeout int, detached bool) (output [2]string, e error) { + return [2]string{"", ""}, nil +} + +func (a *Agent) GetServices() []trmm.WindowsService { return []trmm.WindowsService{} } + +func (a *Agent) Start(_ service.Service) error { return nil } + +func (a *Agent) Stop(_ service.Service) error { return nil } + +func (a *Agent) InstallService() error { return nil } diff --git a/agent/agent_windows.go b/agent/agent_windows.go index 5d75b2d..0912cd2 100644 --- a/agent/agent_windows.go +++ b/agent/agent_windows.go @@ -48,7 +48,6 @@ var ( func NewAgentConfig() *rmm.AgentConfig { k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\TacticalRMM`, registry.ALL_ACCESS) - if err != nil { return &rmm.AgentConfig{} } @@ -798,7 +797,7 @@ func (a *Agent) RecoverMesh() { } func (a *Agent) getMeshNodeID() (string, error) { - out, err := CMD(a.MeshSystemBin, []string{"-nodeid"}, 10, false) + out, err := CMD(a.MeshSystemEXE, []string{"-nodeid"}, 10, false) if err != nil { a.Logger.Debugln(err) return "", err diff --git a/agent/choco/choco_unix.go b/agent/choco/choco_unix.go new file mode 100644 index 0000000..d2a3018 --- /dev/null +++ b/agent/choco/choco_unix.go @@ -0,0 +1,12 @@ +//go:build !windows +// +build !windows + +package choco + +//stubbed out for rpc +func InstallChoco() error { + return nil +} +func InstallWithChoco(name string) (string, error) { + return "", nil +} diff --git a/agent/disk/disk_linux.go b/agent/disk/disk_unix.go similarity index 79% rename from agent/disk/disk_linux.go rename to agent/disk/disk_unix.go index 9f90285..98fb680 100644 --- a/agent/disk/disk_linux.go +++ b/agent/disk/disk_unix.go @@ -1,18 +1,20 @@ +//go:build !windows +// +build !windows + package disk import ( "strings" d "github.com/shirou/gopsutil/v3/disk" - trmm "github.com/wh1te909/trmm-shared" "github.com/amidaware/rmmagent/agent/utils" ) -func GetDisks() []trmm.Disk { - ret := make([]trmm.Disk, 0) +func GetDisks() ([]Disk, error) { + ret := make([]Disk, 0) partitions, err := d.Partitions(false) if err != nil { - return nil + return []Disk{}, nil } for _, p := range partitions { @@ -24,7 +26,7 @@ func GetDisks() []trmm.Disk { continue } - d := trmm.Disk{ + d := Disk{ Device: p.Device, Fstype: p.Fstype, Total: utils.ByteCountSI(usage.Total), @@ -36,5 +38,5 @@ func GetDisks() []trmm.Disk { ret = append(ret, d) } - return ret + return ret, nil } diff --git a/agent/events/events_linux.go b/agent/events/events_unix.go similarity index 75% rename from agent/events/events_linux.go rename to agent/events/events_unix.go index 9174b02..c92143d 100644 --- a/agent/events/events_linux.go +++ b/agent/events/events_unix.go @@ -1,3 +1,6 @@ +//go:build !windows +// +build !windows + package events func GetEventLog(logName string, searchLastDays int) ([]EventLogMsg, error) { diff --git a/agent/patching/patching_linux.go b/agent/patching/patching_unix.go similarity index 50% rename from agent/patching/patching_linux.go rename to agent/patching/patching_unix.go index fe90636..7bc6c60 100644 --- a/agent/patching/patching_linux.go +++ b/agent/patching/patching_unix.go @@ -1,7 +1,12 @@ +//go:build !windows +// +build !windows + package patching func PatchMgmnt(enable bool) error { return nil } -func GetUpdates() {} +func GetUpdates()(PackageList, error) { + return PackageList{}, nil +} func InstallUpdates(guids []string) {} diff --git a/agent/patching/patching_windows.go b/agent/patching/patching_windows.go index 92bc5ef..1fad3de 100644 --- a/agent/patching/patching_windows.go +++ b/agent/patching/patching_windows.go @@ -36,7 +36,7 @@ func PatchMgmnt(enable bool) error { return nil } -type PackageList []Package + func GetUpdates() (PackageList, error) { wuaupdates, err := wua.WUAUpdates("IsInstalled=1 or IsInstalled=0 and Type='Software' and IsHidden=0") diff --git a/agent/patching/structs.go b/agent/patching/structs.go index 1dfe7f4..507cefd 100644 --- a/agent/patching/structs.go +++ b/agent/patching/structs.go @@ -30,3 +30,5 @@ type AgentNeedsReboot struct { AgentID string `json:"agent_id"` NeedsReboot bool `json:"needs_reboot"` } + +type PackageList []Package \ No newline at end of file diff --git a/agent/services/services_linux.go b/agent/services/services_linux.go deleted file mode 100644 index 60706cf..0000000 --- a/agent/services/services_linux.go +++ /dev/null @@ -1,25 +0,0 @@ -package services - -import ( - trmm "github.com/wh1te909/trmm-shared" - rmm "github.com/amidaware/rmmagent/shared" - "github.com/kardianos/service" -) - -func GetServiceDetail(name string) trmm.WindowsService { return trmm.WindowsService{} } - -func ControlService(name, action string) rmm.WinSvcResp { - return rmm.WinSvcResp{Success: false, ErrorMsg: "/na"} -} - -func EditService(name, startupType string) rmm.WinSvcResp { - return rmm.WinSvcResp{Success: false, ErrorMsg: "/na"} -} - -func GetServices() []trmm.WindowsService { return []trmm.WindowsService{} } - -func Start(_ service.Service) error { return nil } - -func Stop(_ service.Service) error { return nil } - -func InstallService() error { return nil } \ No newline at end of file diff --git a/agent/services/services_unix.go b/agent/services/services_unix.go new file mode 100644 index 0000000..375eb4f --- /dev/null +++ b/agent/services/services_unix.go @@ -0,0 +1,32 @@ +//go:build !windows +// +build !windows + +package services + +import ( + "github.com/kardianos/service" +) + +func GetServiceDetail(name string) Service { return Service{} } + +func ControlService(name, action string) WinSvcResp { + return WinSvcResp{Success: false, ErrorMsg: "/na"} +} + +func EditService(name, startupType string) WinSvcResp { + return WinSvcResp{Success: false, ErrorMsg: "/na"} +} + +func GetServices() ([]Service, []error, error) { + return []Service{}, []error{}, nil +} + +func Start(_ service.Service) error { return nil } + +func Stop(_ service.Service) error { return nil } + +func InstallService() error { return nil } + +func GetServiceStatus(name string) (string, error) { + return "", nil +} \ No newline at end of file diff --git a/agent/software/software_linux.go b/agent/software/software_linux.go deleted file mode 100644 index cf3af2e..0000000 --- a/agent/software/software_linux.go +++ /dev/null @@ -1,11 +0,0 @@ -package software - -import ( - trmm "github.com/wh1te909/trmm-shared" -) - -func GetInstalledSoftware() []SoftwareList { return []WinSoftwareList{} } - -func InstallChoco() {} - -func InstallWithChoco(name string) (string, error) { return "", nil } \ No newline at end of file diff --git a/agent/software/software_unix.go b/agent/software/software_unix.go new file mode 100644 index 0000000..13c986d --- /dev/null +++ b/agent/software/software_unix.go @@ -0,0 +1,12 @@ +//go:build !windows +// +build !windows + +package software + +func GetInstalledSoftware() ([]Software, error) { + return []Software{}, nil +} + +func InstallChoco() {} + +func InstallWithChoco(name string) (string, error) { return "", nil } \ No newline at end of file diff --git a/agent/software/structs.go b/agent/software/structs.go index 8cc9dcb..449111f 100644 --- a/agent/software/structs.go +++ b/agent/software/structs.go @@ -9,4 +9,4 @@ type Software struct { Source string `json:"source"` Location string `json:"location"` Uninstall string `json:"uninstall"` -} +} \ No newline at end of file diff --git a/agent/system/system_linux.go b/agent/system/system_unix.go similarity index 90% rename from agent/system/system_linux.go rename to agent/system/system_unix.go index 0636a35..f147e21 100644 --- a/agent/system/system_linux.go +++ b/agent/system/system_unix.go @@ -1,22 +1,30 @@ +//go:build !windows +// +build !windows + package system import ( "bufio" "fmt" "os" + "path/filepath" "runtime" "strings" "syscall" "time" + "github.com/amidaware/rmmagent/agent/events" "github.com/amidaware/rmmagent/agent/utils" ps "github.com/elastic/go-sysinfo" "github.com/jaypipes/ghw" "github.com/shirou/gopsutil/cpu" "github.com/shirou/gopsutil/process" psHost "github.com/shirou/gopsutil/v3/host" - rmm "github.com/amidaware/rmmagent/shared" - trmm "github.com/wh1te909/trmm-shared" +) + +const ( + ProgFilesName = "TacticalAgent" + winExeName = "tacticalrmm.exe" ) func SetDetached() *syscall.SysProcAttr { @@ -31,14 +39,14 @@ func SystemRebootRequired() (bool, error) { // deb paths := [2]string{"/var/run/reboot-required", "/run/reboot-required"} for _, p := range paths { - if trmm.FileExists(p) { + if utils.FileExists(p) { return true, nil } } // rhel bins := [2]string{"/usr/bin/needs-restarting", "/bin/needs-restarting"} for _, bin := range bins { - if trmm.FileExists(bin) { + if utils.FileExists(bin) { opts := NewCMDOpts() // https://man7.org/linux/man-pages/man1/needs-restarting.1.html // -r Only report whether a full reboot is required (exit code 1) or not (exit code 0). @@ -269,8 +277,8 @@ func DeleteSchedTask(name string) error { return nil } func ListSchedTasks() []string { return []string{} } -func GetEventLog(logName string, searchLastDays int) []rmm.EventLogMsg { - return []rmm.EventLogMsg{} +func GetEventLog(logName string, searchLastDays int) []events.EventLogMsg { + return []events.EventLogMsg{} } func CMDShell(shell string, cmdArgs []string, command string, timeout int, detached bool) (output [2]string, e error) { @@ -279,4 +287,21 @@ func CMDShell(shell string, cmdArgs []string, command string, timeout int, detac func CMD(exe string, args []string, timeout int, detached bool) (output [2]string, e error) { return [2]string{"", ""}, nil +} + +func GetPythonBin() string { + opts := NewCMDOpts() + opts.Command = "which python" + out := CmdV2(opts) + return out.Stdout +} + +func GetProgramDirectory() string { + pd := filepath.Join(os.Getenv("ProgramFiles"), ProgFilesName) + return pd +} + +func GetProgramBin() string { + exe := filepath.Join(GetProgramDirectory(), winExeName) + return exe } \ No newline at end of file diff --git a/agent/system/system_linux_test.go b/agent/system/system_unix_test.go similarity index 96% rename from agent/system/system_linux_test.go rename to agent/system/system_unix_test.go index 4c0ce86..bd6dd07 100644 --- a/agent/system/system_linux_test.go +++ b/agent/system/system_unix_test.go @@ -1,3 +1,6 @@ +//go:build !windows +// +build !windows + package system import ( diff --git a/agent/system/system_windows.go b/agent/system/system_windows.go index 8d4f99c..e417737 100644 --- a/agent/system/system_windows.go +++ b/agent/system/system_windows.go @@ -255,7 +255,7 @@ func GetProgramDirectory() string { return pd } -func GetProgramEXE() string { +func GetProgramBin() string { exe := filepath.Join(GetProgramDirectory(), winExeName) return exe } diff --git a/agent/tactical/checks/checks.go b/agent/tactical/checks/checks.go index e0dc313..f8ad34f 100644 --- a/agent/tactical/checks/checks.go +++ b/agent/tactical/checks/checks.go @@ -25,7 +25,7 @@ func CheckRunner(agentID string) error { for { interval, err := GetCheckInterval(agentID) if err == nil && !ChecksRunning() { - _, err = system.CMD(system.GetProgramEXE(), []string{"-m", "checkrunner"}, 600, false) + _, err = system.CMD(system.GetProgramBin(), []string{"-m", "checkrunner"}, 600, false) if err != nil { return err } @@ -66,7 +66,7 @@ Out: if p.PID == 0 { continue } - if p.Exe != system.GetProgramEXE() { + if p.Exe != system.GetProgramBin() { continue } diff --git a/agent/tactical/config/config_unix.go b/agent/tactical/config/config_unix.go new file mode 100644 index 0000000..3322c3d --- /dev/null +++ b/agent/tactical/config/config_unix.go @@ -0,0 +1,38 @@ +//go:build !windows +// +build !windows + +package config + +import ( + "strconv" + + "github.com/spf13/viper" +) + +func NewAgentConfig() *AgentConfig { + viper.SetConfigName("tacticalagent") + viper.SetConfigType("json") + viper.AddConfigPath("/etc/") + viper.AddConfigPath(".") + err := viper.ReadInConfig() + + if err != nil { + return &AgentConfig{} + } + + agentpk := viper.GetString("agentpk") + pk, _ := strconv.Atoi(agentpk) + + ret := &AgentConfig{ + BaseURL: viper.GetString("baseurl"), + AgentID: viper.GetString("agentid"), + APIURL: viper.GetString("apiurl"), + Token: viper.GetString("token"), + AgentPK: agentpk, + PK: pk, + Cert: viper.GetString("cert"), + Proxy: viper.GetString("proxy"), + CustomMeshDir: viper.GetString("meshdir"), + } + return ret +} \ No newline at end of file diff --git a/agent/tactical/mesh/mesh.go b/agent/tactical/mesh/mesh.go index b0c702f..7f7d058 100644 --- a/agent/tactical/mesh/mesh.go +++ b/agent/tactical/mesh/mesh.go @@ -1,6 +1,10 @@ package mesh import ( + "errors" + "strings" + + "github.com/amidaware/rmmagent/agent/system" "github.com/amidaware/rmmagent/agent/tactical/api" "github.com/amidaware/rmmagent/agent/tactical/config" "github.com/amidaware/rmmagent/agent/utils" @@ -26,3 +30,23 @@ func SyncMeshNodeID() error { return nil } + +func GetMeshNodeID() (string, error) { + out, err := system.CMD(GetMeshBinLocation(), []string{"-nodeid"}, 10, false) + if err != nil { + return "", err + } + + stdout := out[0] + stderr := out[1] + + if stderr != "" { + return "", err + } + + if stdout == "" || strings.Contains(strings.ToLower(utils.StripAll(stdout)), "not defined") { + return "", errors.New("failed to get mesh node id") + } + + return stdout, nil +} \ No newline at end of file diff --git a/agent/tactical/mesh/mesh_unix.go b/agent/tactical/mesh/mesh_unix.go new file mode 100644 index 0000000..4276943 --- /dev/null +++ b/agent/tactical/mesh/mesh_unix.go @@ -0,0 +1,24 @@ +//go:build !windows +// +build !windows + +package mesh + +import ( + "path/filepath" + + "github.com/amidaware/rmmagent/agent/tactical/config" +) + +func GetMeshBinLocation() string { + ac := config.NewAgentConfig() + var MeshSysBin string + if len(ac.CustomMeshDir) > 0 { + MeshSysBin = filepath.Join(ac.CustomMeshDir, "meshagent") + } else { + MeshSysBin = "/opt/tacticalmesh/meshagent" + } + + return MeshSysBin +} + +func RecoverMesh() { } \ No newline at end of file diff --git a/agent/tactical/mesh/mesh_windows.go b/agent/tactical/mesh/mesh_windows.go index ff7ee45..8b57c74 100644 --- a/agent/tactical/mesh/mesh_windows.go +++ b/agent/tactical/mesh/mesh_windows.go @@ -41,27 +41,7 @@ func ForceKillMesh() error { return nil } -func GetMeshNodeID() (string, error) { - out, err := system.CMD(getMeshBinLocation(), []string{"-nodeid"}, 10, false) - if err != nil { - return "", err - } - - stdout := out[0] - stderr := out[1] - - if stderr != "" { - return "", err - } - - if stdout == "" || strings.Contains(strings.ToLower(utils.StripAll(stdout)), "not defined") { - return "", errors.New("failed to get mesh node id") - } - - return stdout, nil -} - -func getMeshBinLocation() string { +func GetMeshBinLocation() string { ac := config.NewAgentConfig() var MeshSysBin string if len(ac.CustomMeshDir) > 0 { diff --git a/agent/tactical/rpc/rpc_windows.go b/agent/tactical/rpc/rpc.go similarity index 99% rename from agent/tactical/rpc/rpc_windows.go rename to agent/tactical/rpc/rpc.go index 9636176..c0ced8a 100644 --- a/agent/tactical/rpc/rpc_windows.go +++ b/agent/tactical/rpc/rpc.go @@ -131,7 +131,6 @@ func RunRPC(version string) { var resp []byte ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle)) procs := system.GetProcsRPC() - //a.Logger.Debugln(procs) ret.Encode(procs) msg.Respond(resp) }() @@ -365,7 +364,7 @@ func RunRPC(version string) { } else { ret.Encode("ok") msg.Respond(resp) - _, checkerr := system.CMD(system.GetProgramEXE(), []string{"-m", "runchecks"}, 600, false) + _, checkerr := system.CMD(system.GetProgramBin(), []string{"-m", "runchecks"}, 600, false) if checkerr != nil { } } diff --git a/agent/tactical/rpc/rpc_linux.go b/agent/tactical/rpc/rpc_linux.go deleted file mode 100644 index 98f0ea1..0000000 --- a/agent/tactical/rpc/rpc_linux.go +++ /dev/null @@ -1,4 +0,0 @@ -package rpc - -func RunRPC(version string) { -} diff --git a/agent/tactical/service/service.go b/agent/tactical/service/service.go index 84bc27d..ac79a43 100644 --- a/agent/tactical/service/service.go +++ b/agent/tactical/service/service.go @@ -2,23 +2,16 @@ package service import ( "fmt" - "runtime" "sync" "time" - "github.com/amidaware/rmmagent/agent/disk" - "github.com/amidaware/rmmagent/agent/network" - "github.com/amidaware/rmmagent/agent/services" - "github.com/amidaware/rmmagent/agent/system" "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/shared" "github.com/amidaware/rmmagent/agent/utils" - "github.com/amidaware/rmmagent/agent/wmi" "github.com/nats-io/nats.go" - "github.com/ugorji/go/codec" ) var natsCheckin = []string{"agent-hello", "agent-agentinfo", "agent-disks", "agent-winsvc", "agent-publicip", "agent-wmi"} @@ -98,65 +91,6 @@ func SetupNatsOptions() []nats.Option { return opts } -func NatsMessage(version string, nc *nats.Conn, mode string) { - config := config.NewAgentConfig() - var resp []byte - var payload interface{} - ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle)) - - switch mode { - case "agent-hello": - payload = CheckInNats{ - Agentid: config.AgentID, - Version: version, - } - case "agent-winsvc": - svcs, _, _ := services.GetServices() - payload = WinSvcNats{ - Agentid: config.AgentID, - WinSvcs: svcs, - } - case "agent-agentinfo": - osinfo := system.OsString() - reboot, err := system.SystemRebootRequired() - if err != nil { - reboot = false - } - payload = AgentInfoNats{ - Agentid: config.AgentID, - Username: system.LoggedOnUser(), - Hostname: system.GetHostname(), - OS: osinfo, - Platform: runtime.GOOS, - TotalRAM: system.TotalRAM(), - BootTime: system.BootTime(), - RebootNeeded: reboot, - GoArch: runtime.GOARCH, - } - case "agent-wmi": - wmiinfo, _ := wmi.GetWMIInfo() - payload = WinWMINats{ - Agentid: config.AgentID, - WMI: wmiinfo, - } - case "agent-disks": - disks, _ := disk.GetDisks() - payload = WinDisksNats{ - Agentid: config.AgentID, - Disks: disks, - } - case "agent-publicip": - payload = PublicIPNats{ - Agentid: config.AgentID, - PublicIP: network.PublicIP(config.Proxy), - } - } - - //a.Logger.Debugln(mode, payload) - ret.Encode(payload) - nc.PublishRequest(config.AgentID, mode, resp) -} - func DoNatsCheckIn(version string) { opts := SetupNatsOptions() server := fmt.Sprintf("tls://%s:4222", config.NewAgentConfig().APIURL) diff --git a/agent/tactical/service/service_unix.go b/agent/tactical/service/service_unix.go new file mode 100644 index 0000000..bd03781 --- /dev/null +++ b/agent/tactical/service/service_unix.go @@ -0,0 +1,75 @@ +//go:build !windows +// +build !windows + +package service + +import ( + "runtime" + + "github.com/amidaware/rmmagent/agent/disk" + "github.com/amidaware/rmmagent/agent/network" + "github.com/amidaware/rmmagent/agent/services" + "github.com/amidaware/rmmagent/agent/system" + "github.com/amidaware/rmmagent/agent/tactical/config" + "github.com/amidaware/rmmagent/agent/umi" + "github.com/nats-io/nats.go" + "github.com/ugorji/go/codec" +) + +func NatsMessage(version string, nc *nats.Conn, mode string) { + config := config.NewAgentConfig() + var resp []byte + var payload interface{} + ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle)) + + switch mode { + case "agent-hello": + payload = CheckInNats{ + Agentid: config.AgentID, + Version: version, + } + case "agent-winsvc": + svcs, _, _ := services.GetServices() + payload = WinSvcNats{ + Agentid: config.AgentID, + WinSvcs: svcs, + } + case "agent-agentinfo": + osinfo := system.OsString() + reboot, err := system.SystemRebootRequired() + if err != nil { + reboot = false + } + payload = AgentInfoNats{ + Agentid: config.AgentID, + Username: system.LoggedOnUser(), + Hostname: system.GetHostname(), + OS: osinfo, + Platform: runtime.GOOS, + TotalRAM: system.TotalRAM(), + BootTime: system.BootTime(), + RebootNeeded: reboot, + GoArch: runtime.GOARCH, + } + case "agent-wmi": + wmiinfo, _ := umi.GetInfo() + payload = WinWMINats{ + Agentid: config.AgentID, + WMI: wmiinfo, + } + case "agent-disks": + disks, _ := disk.GetDisks() + payload = WinDisksNats{ + Agentid: config.AgentID, + Disks: disks, + } + case "agent-publicip": + payload = PublicIPNats{ + Agentid: config.AgentID, + PublicIP: network.PublicIP(config.Proxy), + } + } + + ret.Encode(payload) + nc.PublishRequest(config.AgentID, mode, resp) +} \ No newline at end of file diff --git a/agent/tactical/service/service_windows.go b/agent/tactical/service/service_windows.go new file mode 100644 index 0000000..7901463 --- /dev/null +++ b/agent/tactical/service/service_windows.go @@ -0,0 +1,60 @@ +package service + +func NatsMessage(version string, nc *nats.Conn, mode string) { + config := config.NewAgentConfig() + var resp []byte + var payload interface{} + ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle)) + + switch mode { + case "agent-hello": + payload = CheckInNats{ + Agentid: config.AgentID, + Version: version, + } + case "agent-winsvc": + svcs, _, _ := services.GetServices() + payload = WinSvcNats{ + Agentid: config.AgentID, + WinSvcs: svcs, + } + case "agent-agentinfo": + osinfo := system.OsString() + reboot, err := system.SystemRebootRequired() + if err != nil { + reboot = false + } + payload = AgentInfoNats{ + Agentid: config.AgentID, + Username: system.LoggedOnUser(), + Hostname: system.GetHostname(), + OS: osinfo, + Platform: runtime.GOOS, + TotalRAM: system.TotalRAM(), + BootTime: system.BootTime(), + RebootNeeded: reboot, + GoArch: runtime.GOARCH, + } + case "agent-wmi": + wmiinfo, _ := wmi.GetWMIInfo() + payload = WinWMINats{ + Agentid: config.AgentID, + WMI: wmiinfo, + } + case "agent-disks": + disks, _ := disk.GetDisks() + payload = WinDisksNats{ + Agentid: config.AgentID, + Disks: disks, + } + case "agent-publicip": + payload = PublicIPNats{ + Agentid: config.AgentID, + PublicIP: network.PublicIP(config.Proxy), + } + } + + //a.Logger.Debugln(mode, payload) + ret.Encode(payload) + nc.PublishRequest(config.AgentID, mode, resp) +} \ No newline at end of file diff --git a/agent/tactical/shared/shared.go b/agent/tactical/shared/shared.go index 3263a50..c620a33 100644 --- a/agent/tactical/shared/shared.go +++ b/agent/tactical/shared/shared.go @@ -16,4 +16,4 @@ func SendSoftware() error { } return nil -} +} \ No newline at end of file diff --git a/agent/tactical/shared/shared_unix.go b/agent/tactical/shared/shared_unix.go new file mode 100644 index 0000000..2b8a792 --- /dev/null +++ b/agent/tactical/shared/shared_unix.go @@ -0,0 +1,8 @@ +//go:build !windows +// +build !windows + +package shared + +func GetPython(force bool) {} + +func RunMigrations() {} \ No newline at end of file diff --git a/agent/tactical/shared/shared_windows.go b/agent/tactical/shared/shared_windows.go index 899d811..eaad381 100644 --- a/agent/tactical/shared/shared_windows.go +++ b/agent/tactical/shared/shared_windows.go @@ -67,4 +67,4 @@ func RunMigrations() { os.Remove(nssm) } } -} +} \ No newline at end of file diff --git a/agent/tactical/tactical_linux.go b/agent/tactical/tactical_linux.go index fa3cd98..88da0c9 100644 --- a/agent/tactical/tactical_linux.go +++ b/agent/tactical/tactical_linux.go @@ -7,6 +7,7 @@ import ( "time" "github.com/amidaware/rmmagent/agent/system" + "github.com/amidaware/rmmagent/agent/tactical/mesh" "github.com/amidaware/rmmagent/agent/utils" "github.com/amidaware/rmmagent/shared" "github.com/go-resty/resty/v2" @@ -166,7 +167,7 @@ func RecoverMesh(agentID string) { opts := system.NewCMDOpts() opts.Command = "systemctl restart meshagent.service" system.CmdV2(opts) - SyncMeshNodeID() + mesh.SyncMeshNodeID() } func UninstallCleanup() {} diff --git a/agent/tasks/structs_linux.go b/agent/tasks/structs_unix.go similarity index 86% rename from agent/tasks/structs_linux.go rename to agent/tasks/structs_unix.go index f967485..cf84b60 100644 --- a/agent/tasks/structs_linux.go +++ b/agent/tasks/structs_unix.go @@ -1,3 +1,6 @@ +//go:build !windows +// +build !windows + package tasks type SchedTask struct { diff --git a/agent/tasks/tasks_linux.go b/agent/tasks/tasks_linux.go deleted file mode 100644 index 7e09456..0000000 --- a/agent/tasks/tasks_linux.go +++ /dev/null @@ -1,5 +0,0 @@ -package tasks - -func CreateSchedTask(st SchedTask) (bool, error) { - return true, nil -} \ No newline at end of file diff --git a/agent/tasks/tasks_unix.go b/agent/tasks/tasks_unix.go new file mode 100644 index 0000000..00f4817 --- /dev/null +++ b/agent/tasks/tasks_unix.go @@ -0,0 +1,16 @@ +//go:build !windows +// +build !windows + +package tasks + +func CreateSchedTask(st SchedTask) (bool, error) { + return true, nil +} + +func DeleteSchedTask(name string) error { + return nil +} + +func ListSchedTasks() ([]string, error) { + return []string{}, nil +} \ No newline at end of file diff --git a/agent/umi/umi_linux.go b/agent/umi/umi_linux.go new file mode 100644 index 0000000..7188df1 --- /dev/null +++ b/agent/umi/umi_linux.go @@ -0,0 +1,123 @@ +//go:build !windows +// +build !windows + +package umi + +import ( + "bufio" + "fmt" + "os" + "runtime" + "strings" + + "github.com/amidaware/rmmagent/agent/utils" + ps "github.com/elastic/go-sysinfo" + "github.com/jaypipes/ghw" + "github.com/shirou/gopsutil/cpu" +) + +func GetInfo() (map[string]interface{}, []error) { + info := make(map[string]interface{}) + errs := []error{} + ips := make([]string, 0) + disks := make([]string, 0) + cpus := make([]string, 0) + gpus := make([]string, 0) + + // local ips + host, err := ps.Host() + if err != nil { + errs = append(errs, err) + } else { + for _, ip := range host.Info().IPs { + if strings.Contains(ip, "127.0.") || strings.Contains(ip, "::1/128") { + continue + } + ips = append(ips, ip) + } + } + + info["local_ips"] = ips + // disks + block, err := ghw.Block(ghw.WithDisableWarnings()) + if err != nil { + errs = append(errs, err) + } else { + for _, disk := range block.Disks { + if disk.IsRemovable || strings.Contains(disk.Name, "ram") { + continue + } + ret := fmt.Sprintf("%s %s %s %s %s %s", disk.Vendor, disk.Model, disk.StorageController, disk.DriveType, disk.Name, utils.ByteCountSI(disk.SizeBytes)) + ret = strings.TrimSpace(strings.ReplaceAll(ret, "unknown", "")) + disks = append(disks, ret) + } + } + + info["disks"] = disks + // cpus + cpuInfo, err := cpu.Info() + if err != nil { + errs = append(errs, err) + } else { + if len(cpuInfo) > 0 { + if cpuInfo[0].ModelName != "" { + cpus = append(cpus, cpuInfo[0].ModelName) + } + } + } + + info["cpus"] = cpus + // make/model + info["make_model"] = "" + chassis, err := ghw.Chassis(ghw.WithDisableWarnings()) + if err != nil { + errs = append(errs, err) + } else { + if chassis.Vendor != "" || chassis.Version != "" { + info["make_model"] = fmt.Sprintf("%s %s", chassis.Vendor, chassis.Version) + } + } + + // gfx cards + gpu, err := ghw.GPU(ghw.WithDisableWarnings()) + if err != nil { + errs = append(errs, err) + } else { + for _, i := range gpu.GraphicsCards { + if i.DeviceInfo != nil { + ret := fmt.Sprintf("%s %s", i.DeviceInfo.Vendor.Name, i.DeviceInfo.Product.Name) + gpus = append(gpus, ret) + } + + } + } + + info["gpus"] = gpus + // temp hack for ARM cpu/make/model if rasp pi + var makeModel string + if strings.Contains(runtime.GOARCH, "arm") { + file, _ := os.Open("/proc/cpuinfo") + scanner := bufio.NewScanner(file) + for scanner.Scan() { + if strings.Contains(strings.ToLower(scanner.Text()), "raspberry") { + model := strings.Split(scanner.Text(), ":") + if len(model) == 2 { + makeModel = strings.TrimSpace(model[1]) + break + } + } + } + } + + if len(cpus) == 0 { + info["cpus"] = []string{makeModel} + } + if makeModel != "" && (info["make_model"] == "" || info["make_model"] == "unknown unknown") { + info["make_model"] = makeModel + } + if len(gpus) == 1 && gpus[0] == "unknown unknown" { + info["gpus"] = "" + } + + return info, errs +} \ No newline at end of file diff --git a/agent/utils.go b/agent/utils.go index 6d9a851..f5916f8 100644 --- a/agent/utils.go +++ b/agent/utils.go @@ -22,6 +22,7 @@ import ( "os" "path/filepath" "runtime" + goDebug "runtime/debug" "strings" "time" @@ -138,10 +139,13 @@ func GenerateAgentID() string { func ShowVersionInfo(ver string) { fmt.Println("Tactical RMM Agent:", ver) fmt.Println("Arch:", runtime.GOARCH) - fmt.Println("Go version:", runtime.Version()) if runtime.GOOS == "windows" { fmt.Println("Program Directory:", filepath.Join(os.Getenv("ProgramFiles"), progFilesName)) } + bi, ok := goDebug.ReadBuildInfo() + if ok { + fmt.Println(bi.String()) + } } // TotalRAM returns total RAM in GB