feat(telemetry): add hostnamectl system details, df -h device partition filtering, who logged-in users fallback, robust desktop controls, multiarch build (#7)
This commit is contained in:
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
+152
-33
@@ -55,6 +55,21 @@ type LoggedUser struct {
|
|||||||
Started int64 `json:"started"`
|
Started int64 `json:"started"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type HostDetails struct {
|
||||||
|
StaticHostname string `json:"static_hostname"`
|
||||||
|
IconName string `json:"icon_name"`
|
||||||
|
Chassis string `json:"chassis"`
|
||||||
|
MachineID string `json:"machine_id"`
|
||||||
|
BootID string `json:"boot_id"`
|
||||||
|
OS string `json:"os"`
|
||||||
|
Kernel string `json:"kernel"`
|
||||||
|
Arch string `json:"arch"`
|
||||||
|
HardwareVendor string `json:"hardware_vendor"`
|
||||||
|
HardwareModel string `json:"hardware_model"`
|
||||||
|
FirmwareVersion string `json:"firmware_version"`
|
||||||
|
FirmwareDate string `json:"firmware_date"`
|
||||||
|
}
|
||||||
|
|
||||||
type DiscoveryData struct {
|
type DiscoveryData struct {
|
||||||
Hostname string `json:"hostname"`
|
Hostname string `json:"hostname"`
|
||||||
IPs []string `json:"ip_addresses"`
|
IPs []string `json:"ip_addresses"`
|
||||||
@@ -68,6 +83,7 @@ type DiscoveryData struct {
|
|||||||
DiskTotalGB float64 `json:"disk_total_gb"`
|
DiskTotalGB float64 `json:"disk_total_gb"`
|
||||||
Disks []DiskItem `json:"disks"`
|
Disks []DiskItem `json:"disks"`
|
||||||
LoggedUsers []LoggedUser `json:"logged_users"`
|
LoggedUsers []LoggedUser `json:"logged_users"`
|
||||||
|
HostDetails HostDetails `json:"host_details"`
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
Location string `json:"location"`
|
Location string `json:"location"`
|
||||||
Capabilities map[string]interface{} `json:"capabilities"`
|
Capabilities map[string]interface{} `json:"capabilities"`
|
||||||
@@ -81,6 +97,7 @@ type TelemetryData struct {
|
|||||||
DiskUsagePercent float64 `json:"disk_usage_percent"`
|
DiskUsagePercent float64 `json:"disk_usage_percent"`
|
||||||
Disks []DiskItem `json:"disks"`
|
Disks []DiskItem `json:"disks"`
|
||||||
LoggedUsers []LoggedUser `json:"logged_users"`
|
LoggedUsers []LoggedUser `json:"logged_users"`
|
||||||
|
HostDetails HostDetails `json:"host_details"`
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
ZFSHealth string `json:"zfs_health,omitempty"`
|
ZFSHealth string `json:"zfs_health,omitempty"`
|
||||||
GPUUsage float64 `json:"gpu_usage_percent,omitempty"`
|
GPUUsage float64 `json:"gpu_usage_percent,omitempty"`
|
||||||
@@ -117,6 +134,19 @@ func collectCPUDetails() CPUDetails {
|
|||||||
mhz := 0.0
|
mhz := 0.0
|
||||||
if len(cpuInfo) > 0 {
|
if len(cpuInfo) > 0 {
|
||||||
model = cpuInfo[0].ModelName
|
model = cpuInfo[0].ModelName
|
||||||
|
if model == "" || strings.TrimSpace(model) == "154" || len(model) < 4 {
|
||||||
|
if data, err := os.ReadFile("/proc/cpuinfo"); err == nil {
|
||||||
|
for _, line := range strings.Split(string(data), "\n") {
|
||||||
|
if strings.HasPrefix(line, "model name") {
|
||||||
|
parts := strings.Split(line, ":")
|
||||||
|
if len(parts) > 1 {
|
||||||
|
model = strings.TrimSpace(parts[1])
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if model == "" {
|
if model == "" {
|
||||||
model = cpuInfo[0].Model
|
model = cpuInfo[0].Model
|
||||||
}
|
}
|
||||||
@@ -189,23 +219,51 @@ func getDriveType(device string) string {
|
|||||||
|
|
||||||
func collectLoggedUsers() []LoggedUser {
|
func collectLoggedUsers() []LoggedUser {
|
||||||
var list []LoggedUser
|
var list []LoggedUser
|
||||||
users, err := host.Users()
|
|
||||||
if err != nil || len(users) == 0 {
|
|
||||||
return list
|
|
||||||
}
|
|
||||||
seen := make(map[string]bool)
|
seen := make(map[string]bool)
|
||||||
for _, u := range users {
|
users, err := host.Users()
|
||||||
key := fmt.Sprintf("%s@%s:%s", u.User, u.Terminal, u.Host)
|
if err == nil {
|
||||||
if seen[key] {
|
for _, u := range users {
|
||||||
continue
|
key := fmt.Sprintf("%s@%s:%s", u.User, u.Terminal, u.Host)
|
||||||
|
if seen[key] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seen[key] = true
|
||||||
|
list = append(list, LoggedUser{
|
||||||
|
User: u.User,
|
||||||
|
Terminal: u.Terminal,
|
||||||
|
Host: u.Host,
|
||||||
|
Started: int64(u.Started),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(list) == 0 {
|
||||||
|
exec := SystemExecutor{}
|
||||||
|
out, err := exec.Execute("who")
|
||||||
|
if err == nil && len(out) > 0 {
|
||||||
|
lines := strings.Split(string(out), "\n")
|
||||||
|
for _, line := range lines {
|
||||||
|
fields := strings.Fields(line)
|
||||||
|
if len(fields) >= 2 {
|
||||||
|
user := fields[0]
|
||||||
|
term := fields[1]
|
||||||
|
hostStr := ""
|
||||||
|
if len(fields) >= 5 {
|
||||||
|
hostStr = strings.Trim(fields[4], "()")
|
||||||
|
}
|
||||||
|
key := fmt.Sprintf("%s@%s:%s", user, term, hostStr)
|
||||||
|
if !seen[key] {
|
||||||
|
seen[key] = true
|
||||||
|
list = append(list, LoggedUser{
|
||||||
|
User: user,
|
||||||
|
Terminal: term,
|
||||||
|
Host: hostStr,
|
||||||
|
Started: time.Now().Unix(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
seen[key] = true
|
|
||||||
list = append(list, LoggedUser{
|
|
||||||
User: u.User,
|
|
||||||
Terminal: u.Terminal,
|
|
||||||
Host: u.Host,
|
|
||||||
Started: int64(u.Started),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
return list
|
return list
|
||||||
}
|
}
|
||||||
@@ -218,6 +276,7 @@ func collectDiskItems() []DiskItem {
|
|||||||
if err2 == nil {
|
if err2 == nil {
|
||||||
items = append(items, DiskItem{
|
items = append(items, DiskItem{
|
||||||
Mountpoint: "/",
|
Mountpoint: "/",
|
||||||
|
Device: d.Path,
|
||||||
FSType: d.Fstype,
|
FSType: d.Fstype,
|
||||||
DriveType: getDriveType(d.Path),
|
DriveType: getDriveType(d.Path),
|
||||||
TotalBytes: d.Total,
|
TotalBytes: d.Total,
|
||||||
@@ -229,17 +288,9 @@ func collectDiskItems() []DiskItem {
|
|||||||
return items
|
return items
|
||||||
}
|
}
|
||||||
seen := make(map[string]bool)
|
seen := make(map[string]bool)
|
||||||
ignoredFSTypes := map[string]bool{
|
|
||||||
"tmpfs": true, "devtmpfs": true, "proc": true, "sysfs": true,
|
|
||||||
"cgroup": true, "cgroup2": true, "overlay": true, "squashfs": true,
|
|
||||||
"autofs": true, "devpts": true, "mqueue": true,
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, p := range partitions {
|
for _, p := range partitions {
|
||||||
if strings.HasPrefix(p.Mountpoint, "/proc") || strings.HasPrefix(p.Mountpoint, "/sys") || strings.HasPrefix(p.Mountpoint, "/dev") {
|
if !strings.HasPrefix(p.Device, "/dev/") || strings.HasPrefix(p.Device, "/dev/loop") {
|
||||||
continue
|
|
||||||
}
|
|
||||||
if ignoredFSTypes[strings.ToLower(p.Fstype)] {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if seen[p.Mountpoint] {
|
if seen[p.Mountpoint] {
|
||||||
@@ -270,6 +321,7 @@ func collectDiskItems() []DiskItem {
|
|||||||
if err2 == nil {
|
if err2 == nil {
|
||||||
items = append(items, DiskItem{
|
items = append(items, DiskItem{
|
||||||
Mountpoint: "/",
|
Mountpoint: "/",
|
||||||
|
Device: d.Path,
|
||||||
FSType: d.Fstype,
|
FSType: d.Fstype,
|
||||||
DriveType: getDriveType(d.Path),
|
DriveType: getDriveType(d.Path),
|
||||||
TotalBytes: d.Total,
|
TotalBytes: d.Total,
|
||||||
@@ -282,6 +334,68 @@ func collectDiskItems() []DiskItem {
|
|||||||
return items
|
return items
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func collectHostDetails() HostDetails {
|
||||||
|
details := HostDetails{}
|
||||||
|
exec := SystemExecutor{}
|
||||||
|
out, err := exec.Execute("hostnamectl")
|
||||||
|
if err == nil {
|
||||||
|
for _, line := range strings.Split(string(out), "\n") {
|
||||||
|
parts := strings.SplitN(line, ":", 2)
|
||||||
|
if len(parts) == 2 {
|
||||||
|
key := strings.TrimSpace(parts[0])
|
||||||
|
val := strings.TrimSpace(parts[1])
|
||||||
|
switch key {
|
||||||
|
case "Static hostname":
|
||||||
|
details.StaticHostname = val
|
||||||
|
case "Icon name":
|
||||||
|
details.IconName = val
|
||||||
|
case "Chassis":
|
||||||
|
details.Chassis = val
|
||||||
|
case "Machine ID":
|
||||||
|
details.MachineID = val
|
||||||
|
case "Boot ID":
|
||||||
|
details.BootID = val
|
||||||
|
case "Operating System":
|
||||||
|
details.OS = val
|
||||||
|
case "Kernel":
|
||||||
|
details.Kernel = val
|
||||||
|
case "Architecture":
|
||||||
|
details.Arch = val
|
||||||
|
case "Hardware Vendor":
|
||||||
|
details.HardwareVendor = val
|
||||||
|
case "Hardware Model":
|
||||||
|
details.HardwareModel = val
|
||||||
|
case "Firmware Version":
|
||||||
|
details.FirmwareVersion = val
|
||||||
|
case "Firmware Date":
|
||||||
|
details.FirmwareDate = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if details.HardwareVendor == "" {
|
||||||
|
if d, err := os.ReadFile("/sys/class/dmi/id/sys_vendor"); err == nil {
|
||||||
|
details.HardwareVendor = strings.TrimSpace(string(d))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if details.HardwareModel == "" {
|
||||||
|
if d, err := os.ReadFile("/sys/class/dmi/id/product_name"); err == nil {
|
||||||
|
details.HardwareModel = strings.TrimSpace(string(d))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if details.FirmwareVersion == "" {
|
||||||
|
if d, err := os.ReadFile("/sys/class/dmi/id/bios_version"); err == nil {
|
||||||
|
details.FirmwareVersion = strings.TrimSpace(string(d))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if details.FirmwareDate == "" {
|
||||||
|
if d, err := os.ReadFile("/sys/class/dmi/id/bios_date"); err == nil {
|
||||||
|
details.FirmwareDate = strings.TrimSpace(string(d))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return details
|
||||||
|
}
|
||||||
|
|
||||||
const AgentVersion = "v1.7.0"
|
const AgentVersion = "v1.7.0"
|
||||||
|
|
||||||
// CollectDiscoveryData gathers static host information.
|
// CollectDiscoveryData gathers static host information.
|
||||||
@@ -316,6 +430,8 @@ func CollectDiscoveryData(cfg *Config) DiscoveryData {
|
|||||||
diskTotalGB = float64(disks[0].TotalBytes) / (1024 * 1024 * 1024)
|
diskTotalGB = float64(disks[0].TotalBytes) / (1024 * 1024 * 1024)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hostDet := collectHostDetails()
|
||||||
|
|
||||||
return DiscoveryData{
|
return DiscoveryData{
|
||||||
Hostname: h.Hostname,
|
Hostname: h.Hostname,
|
||||||
IPs: ips,
|
IPs: ips,
|
||||||
@@ -329,19 +445,20 @@ func CollectDiscoveryData(cfg *Config) DiscoveryData {
|
|||||||
DiskTotalGB: diskTotalGB,
|
DiskTotalGB: diskTotalGB,
|
||||||
Disks: disks,
|
Disks: disks,
|
||||||
LoggedUsers: loggedUsers,
|
LoggedUsers: loggedUsers,
|
||||||
|
HostDetails: hostDet,
|
||||||
Version: AgentVersion,
|
Version: AgentVersion,
|
||||||
Location: cfg.Location,
|
Location: cfg.Location,
|
||||||
Capabilities: map[string]interface{}{
|
Capabilities: map[string]interface{}{
|
||||||
"telemetry": cfg.Capabilities.Telemetry,
|
"telemetry": cfg.Capabilities.Telemetry,
|
||||||
"configure_ldap": cfg.Capabilities.ConfigureLDAP,
|
"configure_ldap": cfg.Capabilities.ConfigureLDAP,
|
||||||
"ldap_tunnel": cfg.Capabilities.LdapTunnel,
|
"ldap_tunnel": cfg.Capabilities.LdapTunnel,
|
||||||
"secrets": cfg.Capabilities.Secrets,
|
"secrets": cfg.Capabilities.Secrets,
|
||||||
"iam": cfg.Capabilities.IAM,
|
"iam": cfg.Capabilities.IAM,
|
||||||
"reboot": cfg.Capabilities.Reboot,
|
"reboot": cfg.Capabilities.Reboot,
|
||||||
"shutdown": true,
|
"shutdown": true,
|
||||||
"desktop_controls": true,
|
"desktop_controls": true,
|
||||||
"service_control": cfg.Capabilities.ServiceControl,
|
"service_control": cfg.Capabilities.ServiceControl,
|
||||||
"arbitrary_bash": cfg.Capabilities.ArbitraryBash,
|
"arbitrary_bash": cfg.Capabilities.ArbitraryBash,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -353,6 +470,7 @@ func CollectTelemetryData(exec Executor) TelemetryData {
|
|||||||
disks := collectDiskItems()
|
disks := collectDiskItems()
|
||||||
cpuDet := collectCPUDetails()
|
cpuDet := collectCPUDetails()
|
||||||
loggedUsers := collectLoggedUsers()
|
loggedUsers := collectLoggedUsers()
|
||||||
|
hostDet := collectHostDetails()
|
||||||
|
|
||||||
cpuVal := 0.0
|
cpuVal := 0.0
|
||||||
if len(cpuPerc) > 0 {
|
if len(cpuPerc) > 0 {
|
||||||
@@ -378,6 +496,7 @@ func CollectTelemetryData(exec Executor) TelemetryData {
|
|||||||
DiskUsagePercent: diskVal,
|
DiskUsagePercent: diskVal,
|
||||||
Disks: disks,
|
Disks: disks,
|
||||||
LoggedUsers: loggedUsers,
|
LoggedUsers: loggedUsers,
|
||||||
|
HostDetails: hostDet,
|
||||||
Version: AgentVersion,
|
Version: AgentVersion,
|
||||||
ZFSHealth: collectZFSHealth(exec),
|
ZFSHealth: collectZFSHealth(exec),
|
||||||
GPUUsage: collectGPUUsage(exec),
|
GPUUsage: collectGPUUsage(exec),
|
||||||
|
|||||||
+9
-3
@@ -402,16 +402,22 @@ func handleCommand(cm *ConfigManager, msg WSMessage, c MessageWriter, exec Execu
|
|||||||
case "lock_session", "lock":
|
case "lock_session", "lock":
|
||||||
out, err = exec.Execute("loginctl", "lock-sessions")
|
out, err = exec.Execute("loginctl", "lock-sessions")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
out, err = exec.Execute("xset", "dpms", "force", "off")
|
out, err = exec.Execute("sh", "-c", "DISPLAY=:0 xdg-screensaver lock || DISPLAY=:0 xset dpms force off")
|
||||||
}
|
}
|
||||||
case "logout_user", "logout":
|
case "logout_user", "logout":
|
||||||
if targetUser != "" {
|
if targetUser != "" {
|
||||||
out, err = exec.Execute("pkill", "-KILL", "-u", targetUser)
|
out, err = exec.Execute("loginctl", "terminate-user", targetUser)
|
||||||
|
if err != nil {
|
||||||
|
out, err = exec.Execute("pkill", "-KILL", "-u", targetUser)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
out, err = exec.Execute("loginctl", "terminate-session")
|
out, err = exec.Execute("loginctl", "terminate-session")
|
||||||
|
if err != nil {
|
||||||
|
out, err = exec.Execute("pkill", "-9", "-f", "session-child")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case "display_off":
|
case "display_off":
|
||||||
out, err = exec.Execute("xset", "dpms", "force", "off")
|
out, err = exec.Execute("sh", "-c", "DISPLAY=:0 xset dpms force off || loginctl lock-sessions")
|
||||||
case "sleep_host", "sleep":
|
case "sleep_host", "sleep":
|
||||||
out, err = exec.Execute("systemctl", "suspend")
|
out, err = exec.Execute("systemctl", "suspend")
|
||||||
default:
|
default:
|
||||||
|
|||||||
Reference in New Issue
Block a user