feat(telemetry): include logged_users and host_details in periodic stream and detect systemd logind user sessions (#11)

This commit is contained in:
2026-08-09 00:09:23 -04:00
committed by GitHub
parent 37e7ffd25d
commit a423fec835
8 changed files with 88 additions and 55 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.
+88 -55
View File
@@ -220,46 +220,75 @@ func getDriveType(device string) string {
func collectLoggedUsers() []LoggedUser { func collectLoggedUsers() []LoggedUser {
var list []LoggedUser var list []LoggedUser
seen := make(map[string]bool) seen := make(map[string]bool)
users, err := host.Users()
if err == nil { // 1. Try loginctl list-sessions --no-legend (systemd logind)
for _, u := range users { exec := SystemExecutor{}
key := fmt.Sprintf("%s@%s:%s", u.User, u.Terminal, u.Host) if out, err := exec.Execute("loginctl", "list-sessions", "--no-legend"); err == nil && len(out) > 0 {
if seen[key] { lines := strings.Split(string(out), "\n")
continue for _, line := range lines {
fields := strings.Fields(line)
// Format: SESSION UID USER SEAT TTY STATE IDLE SINCE
// e.g. c2 1000 william seat0 tty7 active no -
if len(fields) >= 3 {
user := fields[2]
term := ""
if len(fields) >= 5 && fields[4] != "-" {
term = fields[4]
}
key := fmt.Sprintf("%s@%s", user, term)
if !seen[key] && user != "" {
seen[key] = true
list = append(list, LoggedUser{
User: user,
Terminal: term,
Host: "localhost",
Started: time.Now().Unix(),
})
}
} }
seen[key] = true
list = append(list, LoggedUser{
User: u.User,
Terminal: u.Terminal,
Host: u.Host,
Started: int64(u.Started),
})
} }
} }
// 2. Fallback to gopsutil / who if loginctl returned nothing
if len(list) == 0 { if len(list) == 0 {
exec := SystemExecutor{} users, err := host.Users()
out, err := exec.Execute("who") if err == nil {
if err == nil && len(out) > 0 { for _, u := range users {
lines := strings.Split(string(out), "\n") key := fmt.Sprintf("%s@%s:%s", u.User, u.Terminal, u.Host)
for _, line := range lines { if !seen[key] {
fields := strings.Fields(line) seen[key] = true
if len(fields) >= 2 { list = append(list, LoggedUser{
user := fields[0] User: u.User,
term := fields[1] Terminal: u.Terminal,
hostStr := "" Host: u.Host,
if len(fields) >= 5 { Started: int64(u.Started),
hostStr = strings.Trim(fields[4], "()") })
} }
key := fmt.Sprintf("%s@%s:%s", user, term, hostStr) }
if !seen[key] { }
seen[key] = true if len(list) == 0 {
list = append(list, LoggedUser{ out, err := exec.Execute("who")
User: user, if err == nil && len(out) > 0 {
Terminal: term, lines := strings.Split(string(out), "\n")
Host: hostStr, for _, line := range lines {
Started: time.Now().Unix(), 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(),
})
}
} }
} }
} }
@@ -530,8 +559,9 @@ func collectGPUUsage(exec Executor) float64 {
func StartTelemetryLoop(c MessageWriter, cm *ConfigManager, exec Executor, stopCh <-chan struct{}) { func StartTelemetryLoop(c MessageWriter, cm *ConfigManager, exec Executor, stopCh <-chan struct{}) {
cfg := cm.Get() cfg := cm.Get()
// 1. Immediate Discovery Push // 1. Immediate Discovery Push & Initial Telemetry Frame
pushDiscovery(c, cfg) pushDiscovery(c, cfg)
pushTelemetry(c, exec)
// If telemetry capability is disabled in agent.yml, return early after discovery // If telemetry capability is disabled in agent.yml, return early after discovery
if !cfg.Capabilities.Telemetry { if !cfg.Capabilities.Telemetry {
@@ -562,30 +592,33 @@ func StartTelemetryLoop(c MessageWriter, cm *ConfigManager, exec Executor, stopC
lastIPs = currentIPs lastIPs = currentIPs
} }
telemetry := CollectTelemetryData(exec) pushTelemetry(c, exec)
payload, _ := json.Marshal(WSMessage{
Type: "telemetry",
Payload: map[string]interface{}{
"cpu_usage_percent": telemetry.CPUUsagePercent,
"cpu_details": telemetry.CPUDetails,
"ram_usage_percent": telemetry.RAMUsagePercent,
"ram_details": telemetry.RAMDetails,
"disk_usage_percent": telemetry.DiskUsagePercent,
"disks": telemetry.Disks,
"zfs_health": telemetry.ZFSHealth,
"gpu_usage_percent": telemetry.GPUUsage,
"timestamp": telemetry.Timestamp,
},
})
if err := c.WriteMessage(websocket.TextMessage, payload); err != nil {
log.Printf("Failed to stream telemetry: %v", err)
return
}
} }
} }
}() }()
} }
func pushTelemetry(c MessageWriter, exec Executor) {
telemetry := CollectTelemetryData(exec)
payload, _ := json.Marshal(WSMessage{
Type: "telemetry",
Payload: map[string]interface{}{
"cpu_usage_percent": telemetry.CPUUsagePercent,
"cpu_details": telemetry.CPUDetails,
"ram_usage_percent": telemetry.RAMUsagePercent,
"ram_details": telemetry.RAMDetails,
"disk_usage_percent": telemetry.DiskUsagePercent,
"disks": telemetry.Disks,
"logged_users": telemetry.LoggedUsers,
"host_details": telemetry.HostDetails,
"zfs_health": telemetry.ZFSHealth,
"gpu_usage_percent": telemetry.GPUUsage,
"timestamp": telemetry.Timestamp,
},
})
_ = c.WriteMessage(websocket.TextMessage, payload)
}
func collectIPs() []string { func collectIPs() []string {
var ips []string var ips []string
addrs, _ := net.InterfaceAddrs() addrs, _ := net.InterfaceAddrs()