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:
2026-08-08 19:35:43 -04:00
committed by GitHub
parent 57d5dfc174
commit efae05686e
9 changed files with 161 additions and 36 deletions
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
+152 -33
View File
@@ -55,6 +55,21 @@ type LoggedUser struct {
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 {
Hostname string `json:"hostname"`
IPs []string `json:"ip_addresses"`
@@ -68,6 +83,7 @@ type DiscoveryData struct {
DiskTotalGB float64 `json:"disk_total_gb"`
Disks []DiskItem `json:"disks"`
LoggedUsers []LoggedUser `json:"logged_users"`
HostDetails HostDetails `json:"host_details"`
Version string `json:"version"`
Location string `json:"location"`
Capabilities map[string]interface{} `json:"capabilities"`
@@ -81,6 +97,7 @@ type TelemetryData struct {
DiskUsagePercent float64 `json:"disk_usage_percent"`
Disks []DiskItem `json:"disks"`
LoggedUsers []LoggedUser `json:"logged_users"`
HostDetails HostDetails `json:"host_details"`
Version string `json:"version"`
ZFSHealth string `json:"zfs_health,omitempty"`
GPUUsage float64 `json:"gpu_usage_percent,omitempty"`
@@ -117,6 +134,19 @@ func collectCPUDetails() CPUDetails {
mhz := 0.0
if len(cpuInfo) > 0 {
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 == "" {
model = cpuInfo[0].Model
}
@@ -189,23 +219,51 @@ func getDriveType(device string) string {
func collectLoggedUsers() []LoggedUser {
var list []LoggedUser
users, err := host.Users()
if err != nil || len(users) == 0 {
return list
}
seen := make(map[string]bool)
for _, u := range users {
key := fmt.Sprintf("%s@%s:%s", u.User, u.Terminal, u.Host)
if seen[key] {
continue
users, err := host.Users()
if err == nil {
for _, u := range users {
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
}
@@ -218,6 +276,7 @@ func collectDiskItems() []DiskItem {
if err2 == nil {
items = append(items, DiskItem{
Mountpoint: "/",
Device: d.Path,
FSType: d.Fstype,
DriveType: getDriveType(d.Path),
TotalBytes: d.Total,
@@ -229,17 +288,9 @@ func collectDiskItems() []DiskItem {
return items
}
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 {
if strings.HasPrefix(p.Mountpoint, "/proc") || strings.HasPrefix(p.Mountpoint, "/sys") || strings.HasPrefix(p.Mountpoint, "/dev") {
continue
}
if ignoredFSTypes[strings.ToLower(p.Fstype)] {
if !strings.HasPrefix(p.Device, "/dev/") || strings.HasPrefix(p.Device, "/dev/loop") {
continue
}
if seen[p.Mountpoint] {
@@ -270,6 +321,7 @@ func collectDiskItems() []DiskItem {
if err2 == nil {
items = append(items, DiskItem{
Mountpoint: "/",
Device: d.Path,
FSType: d.Fstype,
DriveType: getDriveType(d.Path),
TotalBytes: d.Total,
@@ -282,6 +334,68 @@ func collectDiskItems() []DiskItem {
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"
// CollectDiscoveryData gathers static host information.
@@ -316,6 +430,8 @@ func CollectDiscoveryData(cfg *Config) DiscoveryData {
diskTotalGB = float64(disks[0].TotalBytes) / (1024 * 1024 * 1024)
}
hostDet := collectHostDetails()
return DiscoveryData{
Hostname: h.Hostname,
IPs: ips,
@@ -329,19 +445,20 @@ func CollectDiscoveryData(cfg *Config) DiscoveryData {
DiskTotalGB: diskTotalGB,
Disks: disks,
LoggedUsers: loggedUsers,
HostDetails: hostDet,
Version: AgentVersion,
Location: cfg.Location,
Capabilities: map[string]interface{}{
"telemetry": cfg.Capabilities.Telemetry,
"configure_ldap": cfg.Capabilities.ConfigureLDAP,
"ldap_tunnel": cfg.Capabilities.LdapTunnel,
"secrets": cfg.Capabilities.Secrets,
"iam": cfg.Capabilities.IAM,
"reboot": cfg.Capabilities.Reboot,
"shutdown": true,
"telemetry": cfg.Capabilities.Telemetry,
"configure_ldap": cfg.Capabilities.ConfigureLDAP,
"ldap_tunnel": cfg.Capabilities.LdapTunnel,
"secrets": cfg.Capabilities.Secrets,
"iam": cfg.Capabilities.IAM,
"reboot": cfg.Capabilities.Reboot,
"shutdown": true,
"desktop_controls": true,
"service_control": cfg.Capabilities.ServiceControl,
"arbitrary_bash": cfg.Capabilities.ArbitraryBash,
"service_control": cfg.Capabilities.ServiceControl,
"arbitrary_bash": cfg.Capabilities.ArbitraryBash,
},
}
}
@@ -353,6 +470,7 @@ func CollectTelemetryData(exec Executor) TelemetryData {
disks := collectDiskItems()
cpuDet := collectCPUDetails()
loggedUsers := collectLoggedUsers()
hostDet := collectHostDetails()
cpuVal := 0.0
if len(cpuPerc) > 0 {
@@ -378,6 +496,7 @@ func CollectTelemetryData(exec Executor) TelemetryData {
DiskUsagePercent: diskVal,
Disks: disks,
LoggedUsers: loggedUsers,
HostDetails: hostDet,
Version: AgentVersion,
ZFSHealth: collectZFSHealth(exec),
GPUUsage: collectGPUUsage(exec),
+9 -3
View File
@@ -402,16 +402,22 @@ func handleCommand(cm *ConfigManager, msg WSMessage, c MessageWriter, exec Execu
case "lock_session", "lock":
out, err = exec.Execute("loginctl", "lock-sessions")
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":
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 {
out, err = exec.Execute("loginctl", "terminate-session")
if err != nil {
out, err = exec.Execute("pkill", "-9", "-f", "session-child")
}
}
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":
out, err = exec.Execute("systemctl", "suspend")
default: