From e49b8c2349c53ef2ff7b887d1d47563ecccd8380 Mon Sep 17 00:00:00 2001 From: redanthrax Date: Fri, 15 Apr 2022 10:38:16 -0700 Subject: [PATCH] added support for mac agent --- agent/agent_darwin.go | 344 ++++++++++++++++++ agent/agent_macos.go | 133 ------- agent/{install_macos.go => install_darwin.go} | 0 main.go | 2 + 4 files changed, 346 insertions(+), 133 deletions(-) create mode 100644 agent/agent_darwin.go delete mode 100644 agent/agent_macos.go rename agent/{install_macos.go => install_darwin.go} (100%) diff --git a/agent/agent_darwin.go b/agent/agent_darwin.go new file mode 100644 index 0000000..d1d851c --- /dev/null +++ b/agent/agent_darwin.go @@ -0,0 +1,344 @@ +/* +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/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) { return false, nil } + +func (a *Agent) LoggedOnUser() string { return "" } + +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) {} + +func (a *Agent) AgentUninstall(code string) {} + +func (a *Agent) NixMeshNodeID() string { return "" } + +func (a *Agent) getMeshNodeID() (string, error) { return "", nil } + +func (a *Agent) RecoverMesh() {} + +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_macos.go b/agent/agent_macos.go deleted file mode 100644 index 6e15a66..0000000 --- a/agent/agent_macos.go +++ /dev/null @@ -1,133 +0,0 @@ -/* -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 ( - "fmt" - "strings" - "syscall" - - rmm "github.com/amidaware/rmmagent/shared" - "github.com/kardianos/service" - psHost "github.com/shirou/gopsutil/v3/host" - trmm "github.com/wh1te909/trmm-shared" -) - -func ShowStatus(version string) { - fmt.Println(version) -} - -func (a *Agent) GetDisks() []trmm.Disk { return nil } - -func (a *Agent) SystemRebootRequired() (bool, error) { return false, nil } - -func (a *Agent) LoggedOnUser() string { return "" } - -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 { return nil } - -func (a *Agent) RunScript(code string, shell string, args []string, timeout int) (stdout, stderr string, exitcode int, e error) { - return "", "", 0, nil -} - -func SetDetached() *syscall.SysProcAttr { return nil } - -func (a *Agent) AgentUpdate(url, inno, version string) {} - -func (a *Agent) AgentUninstall(code string) {} - -func (a *Agent) NixMeshNodeID() string { return "" } - -func (a *Agent) getMeshNodeID() (string, error) { return "", nil } - -func (a *Agent) RecoverMesh() {} - -func (a *Agent) GetWMIInfo() map[string]interface{} { return nil } - -// 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/install_macos.go b/agent/install_darwin.go similarity index 100% rename from agent/install_macos.go rename to agent/install_darwin.go diff --git a/main.go b/main.go index 16f48f7..46bbb03 100644 --- a/main.go +++ b/main.go @@ -185,6 +185,8 @@ func setupLogging(level, to *string) { logFile, _ = os.OpenFile(filepath.Join(os.Getenv("ProgramFiles"), "TacticalAgent", "agent.log"), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0664) case "linux": logFile, _ = os.OpenFile(filepath.Join("/var/log/", "tacticalagent.log"), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0664) + case "darwin": + logFile, _ = os.OpenFile(filepath.Join("/var/log/", "tacticalagent.log"), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0664) } log.SetOutput(logFile) }