Files
nannyagent/system_info.go
Harshavardhan Musanalli 4b442ab169 Adding ebpf capability now
2025-09-28 12:10:52 +02:00

202 lines
5.4 KiB
Go

package main
import (
"fmt"
"net"
"runtime"
"strings"
"time"
)
// SystemInfo represents basic system information
type SystemInfo struct {
Hostname string `json:"hostname"`
OS string `json:"os"`
Kernel string `json:"kernel"`
Architecture string `json:"architecture"`
CPUCores string `json:"cpu_cores"`
Memory string `json:"memory"`
Uptime string `json:"uptime"`
PrivateIPs string `json:"private_ips"`
LoadAverage string `json:"load_average"`
DiskUsage string `json:"disk_usage"`
}
// GatherSystemInfo collects basic system information
func GatherSystemInfo() *SystemInfo {
info := &SystemInfo{}
executor := NewCommandExecutor(5 * time.Second)
// Basic system info
if result := executor.Execute(Command{ID: "hostname", Command: "hostname"}); result.ExitCode == 0 {
info.Hostname = strings.TrimSpace(result.Output)
}
if result := executor.Execute(Command{ID: "os", Command: "lsb_release -d 2>/dev/null | cut -f2 || cat /etc/os-release | grep PRETTY_NAME | cut -d'=' -f2 | tr -d '\"'"}); result.ExitCode == 0 {
info.OS = strings.TrimSpace(result.Output)
}
if result := executor.Execute(Command{ID: "kernel", Command: "uname -r"}); result.ExitCode == 0 {
info.Kernel = strings.TrimSpace(result.Output)
}
if result := executor.Execute(Command{ID: "arch", Command: "uname -m"}); result.ExitCode == 0 {
info.Architecture = strings.TrimSpace(result.Output)
}
if result := executor.Execute(Command{ID: "cores", Command: "nproc"}); result.ExitCode == 0 {
info.CPUCores = strings.TrimSpace(result.Output)
}
if result := executor.Execute(Command{ID: "memory", Command: "free -h | grep Mem | awk '{print $2}'"}); result.ExitCode == 0 {
info.Memory = strings.TrimSpace(result.Output)
}
if result := executor.Execute(Command{ID: "uptime", Command: "uptime -p"}); result.ExitCode == 0 {
info.Uptime = strings.TrimSpace(result.Output)
}
if result := executor.Execute(Command{ID: "load", Command: "uptime | awk -F'load average:' '{print $2}' | xargs"}); result.ExitCode == 0 {
info.LoadAverage = strings.TrimSpace(result.Output)
}
if result := executor.Execute(Command{ID: "disk", Command: "df -h / | tail -1 | awk '{print \"Root: \" $3 \"/\" $2 \" (\" $5 \" used)\"}'"}); result.ExitCode == 0 {
info.DiskUsage = strings.TrimSpace(result.Output)
}
// Get private IP addresses
info.PrivateIPs = getPrivateIPs()
return info
}
// getPrivateIPs returns private IP addresses
func getPrivateIPs() string {
var privateIPs []string
interfaces, err := net.Interfaces()
if err != nil {
return "Unable to determine"
}
for _, iface := range interfaces {
if iface.Flags&net.FlagUp == 0 || iface.Flags&net.FlagLoopback != 0 {
continue // Skip down or loopback interfaces
}
addrs, err := iface.Addrs()
if err != nil {
continue
}
for _, addr := range addrs {
if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
if isPrivateIP(ipnet.IP) {
privateIPs = append(privateIPs, fmt.Sprintf("%s (%s)", ipnet.IP.String(), iface.Name))
}
}
}
}
if len(privateIPs) == 0 {
return "No private IPs found"
}
return strings.Join(privateIPs, ", ")
}
// isPrivateIP checks if an IP address is private
func isPrivateIP(ip net.IP) bool {
// RFC 1918 private address ranges
private := []string{
"10.0.0.0/8",
"172.16.0.0/12",
"192.168.0.0/16",
}
for _, cidr := range private {
_, subnet, _ := net.ParseCIDR(cidr)
if subnet.Contains(ip) {
return true
}
}
return false
}
// FormatSystemInfoForPrompt formats system information for inclusion in diagnostic prompts
func FormatSystemInfoForPrompt(info *SystemInfo) string {
return fmt.Sprintf(`SYSTEM INFORMATION:
- Hostname: %s
- Operating System: %s
- Kernel Version: %s
- Architecture: %s
- CPU Cores: %s
- Total Memory: %s
- System Uptime: %s
- Current Load Average: %s
- Root Disk Usage: %s
- Private IP Addresses: %s
- Go Runtime: %s
ISSUE DESCRIPTION:`,
info.Hostname,
info.OS,
info.Kernel,
info.Architecture,
info.CPUCores,
info.Memory,
info.Uptime,
info.LoadAverage,
info.DiskUsage,
info.PrivateIPs,
runtime.Version())
}
// FormatSystemInfoWithEBPFForPrompt formats system information including eBPF capabilities
func FormatSystemInfoWithEBPFForPrompt(info *SystemInfo, ebpfManager EBPFManagerInterface) string {
baseInfo := FormatSystemInfoForPrompt(info)
if ebpfManager == nil {
return baseInfo + "\neBPF CAPABILITIES: Not available\n"
}
capabilities := ebpfManager.GetCapabilities()
summary := ebpfManager.GetSummary()
ebpfInfo := fmt.Sprintf(`
eBPF MONITORING CAPABILITIES:
- System Call Tracing: %v
- Network Activity Tracing: %v
- Process Monitoring: %v
- File System Monitoring: %v
- Performance Monitoring: %v
- Security Event Monitoring: %v
eBPF INTEGRATION GUIDE:
To request eBPF monitoring during diagnosis, include these fields in your JSON response:
{
"response_type": "diagnostic",
"reasoning": "explanation of why eBPF monitoring is needed",
"commands": [regular diagnostic commands],
"ebpf_capabilities": ["syscall_trace", "network_trace", "process_trace"],
"ebpf_duration_seconds": 15,
"ebpf_filters": {"pid": "process_id", "comm": "process_name", "path": "/specific/path"}
}
Available eBPF capabilities: %v
eBPF Status: %v
`,
capabilities["tracepoint"],
capabilities["kprobe"],
capabilities["kernel_support"],
capabilities["tracepoint"],
capabilities["kernel_support"],
capabilities["bpftrace_available"],
capabilities,
summary)
return baseInfo + ebpfInfo
}