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