feat(telemetry): include logged_users and host_details in periodic stream and detect systemd logind user sessions (#11)
This commit is contained in:
+88
-55
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user