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 {
var list []LoggedUser
seen := make(map[string]bool)
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
// 1. Try loginctl list-sessions --no-legend (systemd logind)
exec := SystemExecutor{}
if out, err := exec.Execute("loginctl", "list-sessions", "--no-legend"); err == nil && len(out) > 0 {
lines := strings.Split(string(out), "\n")
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 {
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(),
})
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] {
seen[key] = true
list = append(list, LoggedUser{
User: u.User,
Terminal: u.Terminal,
Host: u.Host,
Started: int64(u.Started),
})
}
}
}
if len(list) == 0 {
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(),
})
}
}
}
}
@@ -530,8 +559,9 @@ func collectGPUUsage(exec Executor) float64 {
func StartTelemetryLoop(c MessageWriter, cm *ConfigManager, exec Executor, stopCh <-chan struct{}) {
cfg := cm.Get()
// 1. Immediate Discovery Push
// 1. Immediate Discovery Push & Initial Telemetry Frame
pushDiscovery(c, cfg)
pushTelemetry(c, exec)
// If telemetry capability is disabled in agent.yml, return early after discovery
if !cfg.Capabilities.Telemetry {
@@ -562,30 +592,33 @@ func StartTelemetryLoop(c MessageWriter, cm *ConfigManager, exec Executor, stopC
lastIPs = currentIPs
}
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,
"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
}
pushTelemetry(c, exec)
}
}
}()
}
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 {
var ips []string
addrs, _ := net.InterfaceAddrs()