release: v1.8.0 - Active Logged-in Users, Physical Disks & Desktop Operations

This commit is contained in:
2026-08-08 18:12:16 -04:00
parent 4aa233f507
commit 57d5dfc174
10 changed files with 114 additions and 10 deletions
+8
View File
@@ -5,6 +5,14 @@ All notable changes to the `theta-agent` daemon will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [v1.8.0] - 2026-08-08
### Added
- **Active Logged-in User Sessions.** Added `collectLoggedUsers()` gathering terminal sessions (`who` / `host.Users()`) reported in discovery and live telemetry payloads.
- **Full Physical Partition & Filesystem Collection.** Switched to `disk.Partitions(true)` to list all physical drives, ZFS pools, and mount points while filtering pseudo/virtual filesystems.
- **Desktop Control Operations.** Implemented `desktop_control` WebSocket actions supporting `lock_session` (`loginctl lock-sessions`), `logout_user` (`pkill -u <user>`), `display_off` (`xset dpms force off`), and `sleep_host` (`systemctl suspend`).
- **Binary Version Reporting.** Included `AgentVersion` (`v1.8.0`) in discovery and telemetry frames.
## [v1.7.0] - 2026-08-08
### Added
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
Vendored Executable
BIN
View File
Binary file not shown.
Vendored Executable
BIN
View File
Binary file not shown.
Vendored Executable
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+53 -1
View File
@@ -48,6 +48,13 @@ type DiskItem struct {
UsagePercent float64 `json:"usage_percent"`
}
type LoggedUser struct {
User string `json:"user"`
Terminal string `json:"terminal"`
Host string `json:"host"`
Started int64 `json:"started"`
}
type DiscoveryData struct {
Hostname string `json:"hostname"`
IPs []string `json:"ip_addresses"`
@@ -60,6 +67,8 @@ type DiscoveryData struct {
RAMDetails RAMDetails `json:"ram_details"`
DiskTotalGB float64 `json:"disk_total_gb"`
Disks []DiskItem `json:"disks"`
LoggedUsers []LoggedUser `json:"logged_users"`
Version string `json:"version"`
Location string `json:"location"`
Capabilities map[string]interface{} `json:"capabilities"`
}
@@ -71,6 +80,8 @@ type TelemetryData struct {
RAMDetails RAMDetails `json:"ram_details"`
DiskUsagePercent float64 `json:"disk_usage_percent"`
Disks []DiskItem `json:"disks"`
LoggedUsers []LoggedUser `json:"logged_users"`
Version string `json:"version"`
ZFSHealth string `json:"zfs_health,omitempty"`
GPUUsage float64 `json:"gpu_usage_percent,omitempty"`
Timestamp string `json:"timestamp"`
@@ -176,9 +187,32 @@ func getDriveType(device string) string {
return "SSD/HDD"
}
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
}
seen[key] = true
list = append(list, LoggedUser{
User: u.User,
Terminal: u.Terminal,
Host: u.Host,
Started: int64(u.Started),
})
}
return list
}
func collectDiskItems() []DiskItem {
var items []DiskItem
partitions, err := disk.Partitions(false)
partitions, err := disk.Partitions(true)
if err != nil || len(partitions) == 0 {
d, err2 := disk.Usage("/")
if err2 == nil {
@@ -195,10 +229,19 @@ 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)] {
continue
}
if seen[p.Mountpoint] {
continue
}
@@ -239,6 +282,8 @@ func collectDiskItems() []DiskItem {
return items
}
const AgentVersion = "v1.7.0"
// CollectDiscoveryData gathers static host information.
func CollectDiscoveryData(cfg *Config) DiscoveryData {
h, _ := host.Info()
@@ -256,6 +301,7 @@ func CollectDiscoveryData(cfg *Config) DiscoveryData {
vm := collectRAMDetails()
disks := collectDiskItems()
cpuDet := collectCPUDetails()
loggedUsers := collectLoggedUsers()
pubIP := getPublicIP()
@@ -282,6 +328,8 @@ func CollectDiscoveryData(cfg *Config) DiscoveryData {
RAMDetails: vm,
DiskTotalGB: diskTotalGB,
Disks: disks,
LoggedUsers: loggedUsers,
Version: AgentVersion,
Location: cfg.Location,
Capabilities: map[string]interface{}{
"telemetry": cfg.Capabilities.Telemetry,
@@ -291,6 +339,7 @@ func CollectDiscoveryData(cfg *Config) DiscoveryData {
"iam": cfg.Capabilities.IAM,
"reboot": cfg.Capabilities.Reboot,
"shutdown": true,
"desktop_controls": true,
"service_control": cfg.Capabilities.ServiceControl,
"arbitrary_bash": cfg.Capabilities.ArbitraryBash,
},
@@ -303,6 +352,7 @@ func CollectTelemetryData(exec Executor) TelemetryData {
vm := collectRAMDetails()
disks := collectDiskItems()
cpuDet := collectCPUDetails()
loggedUsers := collectLoggedUsers()
cpuVal := 0.0
if len(cpuPerc) > 0 {
@@ -327,6 +377,8 @@ func CollectTelemetryData(exec Executor) TelemetryData {
RAMDetails: vm,
DiskUsagePercent: diskVal,
Disks: disks,
LoggedUsers: loggedUsers,
Version: AgentVersion,
ZFSHealth: collectZFSHealth(exec),
GPUUsage: collectGPUUsage(exec),
Timestamp: time.Now().Format(time.RFC3339),
+44
View File
@@ -388,6 +388,50 @@ func handleCommand(cm *ConfigManager, msg WSMessage, c MessageWriter, exec Execu
exec.Execute("poweroff")
}
return
case "desktop_control", "lock_session", "logout_user", "display_off", "sleep_host":
subAction, _ := msg.Payload["subAction"].(string)
if subAction == "" {
subAction = msg.Type
}
targetUser, _ := msg.Payload["user"].(string)
log.Printf("Executing desktop control action '%s' for user '%s'...", subAction, targetUser)
var out []byte
var err error
switch subAction {
case "lock_session", "lock":
out, err = exec.Execute("loginctl", "lock-sessions")
if err != nil {
out, err = exec.Execute("xset", "dpms", "force", "off")
}
case "logout_user", "logout":
if targetUser != "" {
out, err = exec.Execute("pkill", "-KILL", "-u", targetUser)
} else {
out, err = exec.Execute("loginctl", "terminate-session")
}
case "display_off":
out, err = exec.Execute("xset", "dpms", "force", "off")
case "sleep_host", "sleep":
out, err = exec.Execute("systemctl", "suspend")
default:
sendResponse("error", fmt.Sprintf("unknown desktop action '%s'", subAction))
return
}
errMsg := ""
if err != nil {
errMsg = err.Error()
}
respMap := map[string]interface{}{
"status": "ok",
"subAction": subAction,
"output": string(out),
"error": errMsg,
}
respPayload, _ := json.Marshal(respMap)
c.WriteMessage(websocket.TextMessage, respPayload)
return
case "systemd_action":
serviceName, _ := msg.Payload["service"].(string)
action, _ := msg.Payload["action"].(string)