Windows agent improvements and dependency workflow fixes
- Added system info reporting to agent main loop - Updated README with current project status and screenshots - Fixed a few workflow quirks
This commit is contained in:
@@ -273,12 +273,27 @@ func runAgent(cfg *config.Config) error {
|
||||
windowsUpdateScanner := scanner.NewWindowsUpdateScanner()
|
||||
wingetScanner := scanner.NewWingetScanner()
|
||||
|
||||
// System info tracking
|
||||
var lastSystemInfoUpdate time.Time
|
||||
const systemInfoUpdateInterval = 1 * time.Hour // Update detailed system info every hour
|
||||
|
||||
// Main check-in loop
|
||||
for {
|
||||
// Add jitter to prevent thundering herd
|
||||
jitter := time.Duration(rand.Intn(30)) * time.Second
|
||||
time.Sleep(jitter)
|
||||
|
||||
// Check if we need to send detailed system info update
|
||||
if time.Since(lastSystemInfoUpdate) >= systemInfoUpdateInterval {
|
||||
log.Printf("Updating detailed system information...")
|
||||
if err := reportSystemInfo(apiClient, cfg); err != nil {
|
||||
log.Printf("Failed to report system info: %v\n", err)
|
||||
} else {
|
||||
lastSystemInfoUpdate = time.Now()
|
||||
log.Printf("✓ System information updated\n")
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("Checking in with server... (Agent v%s)", AgentVersion)
|
||||
|
||||
// Collect lightweight system metrics
|
||||
@@ -374,17 +389,25 @@ func handleScanUpdates(apiClient *client.Client, cfg *config.Config, aptScanner
|
||||
log.Println("Scanning for updates...")
|
||||
|
||||
var allUpdates []client.UpdateReportItem
|
||||
var scanErrors []string
|
||||
var scanResults []string
|
||||
|
||||
// Scan APT updates
|
||||
if aptScanner.IsAvailable() {
|
||||
log.Println(" - Scanning APT packages...")
|
||||
updates, err := aptScanner.Scan()
|
||||
if err != nil {
|
||||
log.Printf(" APT scan failed: %v\n", err)
|
||||
errorMsg := fmt.Sprintf("APT scan failed: %v", err)
|
||||
log.Printf(" %s\n", errorMsg)
|
||||
scanErrors = append(scanErrors, errorMsg)
|
||||
} else {
|
||||
log.Printf(" Found %d APT updates\n", len(updates))
|
||||
resultMsg := fmt.Sprintf("Found %d APT updates", len(updates))
|
||||
log.Printf(" %s\n", resultMsg)
|
||||
scanResults = append(scanResults, resultMsg)
|
||||
allUpdates = append(allUpdates, updates...)
|
||||
}
|
||||
} else {
|
||||
scanResults = append(scanResults, "APT scanner not available")
|
||||
}
|
||||
|
||||
// Scan DNF updates
|
||||
@@ -392,11 +415,17 @@ func handleScanUpdates(apiClient *client.Client, cfg *config.Config, aptScanner
|
||||
log.Println(" - Scanning DNF packages...")
|
||||
updates, err := dnfScanner.Scan()
|
||||
if err != nil {
|
||||
log.Printf(" DNF scan failed: %v\n", err)
|
||||
errorMsg := fmt.Sprintf("DNF scan failed: %v", err)
|
||||
log.Printf(" %s\n", errorMsg)
|
||||
scanErrors = append(scanErrors, errorMsg)
|
||||
} else {
|
||||
log.Printf(" Found %d DNF updates\n", len(updates))
|
||||
resultMsg := fmt.Sprintf("Found %d DNF updates", len(updates))
|
||||
log.Printf(" %s\n", resultMsg)
|
||||
scanResults = append(scanResults, resultMsg)
|
||||
allUpdates = append(allUpdates, updates...)
|
||||
}
|
||||
} else {
|
||||
scanResults = append(scanResults, "DNF scanner not available")
|
||||
}
|
||||
|
||||
// Scan Docker updates
|
||||
@@ -404,11 +433,17 @@ func handleScanUpdates(apiClient *client.Client, cfg *config.Config, aptScanner
|
||||
log.Println(" - Scanning Docker images...")
|
||||
updates, err := dockerScanner.Scan()
|
||||
if err != nil {
|
||||
log.Printf(" Docker scan failed: %v\n", err)
|
||||
errorMsg := fmt.Sprintf("Docker scan failed: %v", err)
|
||||
log.Printf(" %s\n", errorMsg)
|
||||
scanErrors = append(scanErrors, errorMsg)
|
||||
} else {
|
||||
log.Printf(" Found %d Docker image updates\n", len(updates))
|
||||
resultMsg := fmt.Sprintf("Found %d Docker image updates", len(updates))
|
||||
log.Printf(" %s\n", resultMsg)
|
||||
scanResults = append(scanResults, resultMsg)
|
||||
allUpdates = append(allUpdates, updates...)
|
||||
}
|
||||
} else {
|
||||
scanResults = append(scanResults, "Docker scanner not available")
|
||||
}
|
||||
|
||||
// Scan Windows updates
|
||||
@@ -416,11 +451,17 @@ func handleScanUpdates(apiClient *client.Client, cfg *config.Config, aptScanner
|
||||
log.Println(" - Scanning Windows updates...")
|
||||
updates, err := windowsUpdateScanner.Scan()
|
||||
if err != nil {
|
||||
log.Printf(" Windows Update scan failed: %v\n", err)
|
||||
errorMsg := fmt.Sprintf("Windows Update scan failed: %v", err)
|
||||
log.Printf(" %s\n", errorMsg)
|
||||
scanErrors = append(scanErrors, errorMsg)
|
||||
} else {
|
||||
log.Printf(" Found %d Windows updates\n", len(updates))
|
||||
resultMsg := fmt.Sprintf("Found %d Windows updates", len(updates))
|
||||
log.Printf(" %s\n", resultMsg)
|
||||
scanResults = append(scanResults, resultMsg)
|
||||
allUpdates = append(allUpdates, updates...)
|
||||
}
|
||||
} else {
|
||||
scanResults = append(scanResults, "Windows Update scanner not available")
|
||||
}
|
||||
|
||||
// Scan Winget packages
|
||||
@@ -428,14 +469,58 @@ func handleScanUpdates(apiClient *client.Client, cfg *config.Config, aptScanner
|
||||
log.Println(" - Scanning Winget packages...")
|
||||
updates, err := wingetScanner.Scan()
|
||||
if err != nil {
|
||||
log.Printf(" Winget scan failed: %v\n", err)
|
||||
errorMsg := fmt.Sprintf("Winget scan failed: %v", err)
|
||||
log.Printf(" %s\n", errorMsg)
|
||||
scanErrors = append(scanErrors, errorMsg)
|
||||
} else {
|
||||
log.Printf(" Found %d Winget package updates\n", len(updates))
|
||||
resultMsg := fmt.Sprintf("Found %d Winget package updates", len(updates))
|
||||
log.Printf(" %s\n", resultMsg)
|
||||
scanResults = append(scanResults, resultMsg)
|
||||
allUpdates = append(allUpdates, updates...)
|
||||
}
|
||||
} else {
|
||||
scanResults = append(scanResults, "Winget scanner not available")
|
||||
}
|
||||
|
||||
// Report to server
|
||||
// Report scan results to server (both successes and failures)
|
||||
success := len(allUpdates) > 0 || len(scanErrors) == 0
|
||||
var combinedOutput string
|
||||
|
||||
// Combine all scan results
|
||||
if len(scanResults) > 0 {
|
||||
combinedOutput += "Scan Results:\n" + strings.Join(scanResults, "\n")
|
||||
}
|
||||
if len(scanErrors) > 0 {
|
||||
if combinedOutput != "" {
|
||||
combinedOutput += "\n"
|
||||
}
|
||||
combinedOutput += "Scan Errors:\n" + strings.Join(scanErrors, "\n")
|
||||
}
|
||||
if len(allUpdates) > 0 {
|
||||
if combinedOutput != "" {
|
||||
combinedOutput += "\n"
|
||||
}
|
||||
combinedOutput += fmt.Sprintf("Total Updates Found: %d", len(allUpdates))
|
||||
}
|
||||
|
||||
// Create scan log entry
|
||||
logReport := client.LogReport{
|
||||
CommandID: commandID,
|
||||
Action: "scan_updates",
|
||||
Result: map[bool]string{true: "success", false: "failure"}[success],
|
||||
Stdout: combinedOutput,
|
||||
Stderr: strings.Join(scanErrors, "\n"),
|
||||
ExitCode: map[bool]int{true: 0, false: 1}[success],
|
||||
DurationSeconds: 0, // Could track scan duration if needed
|
||||
}
|
||||
|
||||
// Report the scan log
|
||||
if err := apiClient.ReportLog(cfg.AgentID, logReport); err != nil {
|
||||
log.Printf("Failed to report scan log: %v\n", err)
|
||||
// Continue anyway - updates are more important
|
||||
}
|
||||
|
||||
// Report updates to server if any were found
|
||||
if len(allUpdates) > 0 {
|
||||
report := client.UpdateReport{
|
||||
CommandID: commandID,
|
||||
@@ -452,6 +537,11 @@ func handleScanUpdates(apiClient *client.Client, cfg *config.Config, aptScanner
|
||||
log.Println("✓ No updates found")
|
||||
}
|
||||
|
||||
// Return error if there were any scan failures
|
||||
if len(scanErrors) > 0 && len(allUpdates) == 0 {
|
||||
return fmt.Errorf("all scanners failed: %s", strings.Join(scanErrors, "; "))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -961,6 +1051,58 @@ func handleConfirmDependencies(apiClient *client.Client, cfg *config.Config, com
|
||||
return nil
|
||||
}
|
||||
|
||||
// reportSystemInfo collects and reports detailed system information to the server
|
||||
func reportSystemInfo(apiClient *client.Client, cfg *config.Config) error {
|
||||
// Collect detailed system information
|
||||
sysInfo, err := system.GetSystemInfo(AgentVersion)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get system info: %w", err)
|
||||
}
|
||||
|
||||
// Create system info report
|
||||
report := client.SystemInfoReport{
|
||||
Timestamp: time.Now(),
|
||||
CPUModel: sysInfo.CPUInfo.ModelName,
|
||||
CPUCores: sysInfo.CPUInfo.Cores,
|
||||
CPUThreads: sysInfo.CPUInfo.Threads,
|
||||
MemoryTotal: sysInfo.MemoryInfo.Total,
|
||||
DiskTotal: uint64(0),
|
||||
DiskUsed: uint64(0),
|
||||
IPAddress: sysInfo.IPAddress,
|
||||
Processes: sysInfo.RunningProcesses,
|
||||
Uptime: sysInfo.Uptime,
|
||||
Metadata: make(map[string]interface{}),
|
||||
}
|
||||
|
||||
// Add primary disk info
|
||||
if len(sysInfo.DiskInfo) > 0 {
|
||||
primaryDisk := sysInfo.DiskInfo[0]
|
||||
report.DiskTotal = primaryDisk.Total
|
||||
report.DiskUsed = primaryDisk.Used
|
||||
report.Metadata["disk_mount"] = primaryDisk.Mountpoint
|
||||
report.Metadata["disk_filesystem"] = primaryDisk.Filesystem
|
||||
}
|
||||
|
||||
// Add collection timestamp and additional metadata
|
||||
report.Metadata["collected_at"] = time.Now().Format(time.RFC3339)
|
||||
report.Metadata["hostname"] = sysInfo.Hostname
|
||||
report.Metadata["os_type"] = sysInfo.OSType
|
||||
report.Metadata["os_version"] = sysInfo.OSVersion
|
||||
report.Metadata["os_architecture"] = sysInfo.OSArchitecture
|
||||
|
||||
// Add any existing metadata from system info
|
||||
for key, value := range sysInfo.Metadata {
|
||||
report.Metadata[key] = value
|
||||
}
|
||||
|
||||
// Report to server
|
||||
if err := apiClient.ReportSystemInfo(cfg.AgentID, report); err != nil {
|
||||
return fmt.Errorf("failed to report system info: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// formatTimeSince formats a duration as "X time ago"
|
||||
func formatTimeSince(t time.Time) string {
|
||||
duration := time.Since(t)
|
||||
|
||||
@@ -369,6 +369,52 @@ func (c *Client) ReportDependencies(agentID uuid.UUID, report DependencyReport)
|
||||
return nil
|
||||
}
|
||||
|
||||
// SystemInfoReport represents system information updates
|
||||
type SystemInfoReport struct {
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
CPUModel string `json:"cpu_model,omitempty"`
|
||||
CPUCores int `json:"cpu_cores,omitempty"`
|
||||
CPUThreads int `json:"cpu_threads,omitempty"`
|
||||
MemoryTotal uint64 `json:"memory_total,omitempty"`
|
||||
DiskTotal uint64 `json:"disk_total,omitempty"`
|
||||
DiskUsed uint64 `json:"disk_used,omitempty"`
|
||||
IPAddress string `json:"ip_address,omitempty"`
|
||||
Processes int `json:"processes,omitempty"`
|
||||
Uptime string `json:"uptime,omitempty"`
|
||||
Metadata map[string]interface{} `json:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
// ReportSystemInfo sends updated system information to the server
|
||||
func (c *Client) ReportSystemInfo(agentID uuid.UUID, report SystemInfoReport) error {
|
||||
url := fmt.Sprintf("%s/api/v1/agents/%s/system-info", c.baseURL, agentID)
|
||||
|
||||
body, err := json.Marshal(report)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("POST", url, bytes.NewBuffer(body))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", "Bearer "+c.token)
|
||||
|
||||
resp, err := c.http.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Accept 200 OK or 404 Not Found (if endpoint doesn't exist yet)
|
||||
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNotFound {
|
||||
bodyBytes, _ := io.ReadAll(resp.Body)
|
||||
return fmt.Errorf("failed to report system info: %s - %s", resp.Status, string(bodyBytes))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DetectSystem returns basic system information (deprecated, use system.GetSystemInfo instead)
|
||||
func DetectSystem() (osType, osVersion, osArch string) {
|
||||
osType = runtime.GOOS
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/aggregator-project/aggregator-agent/internal/client"
|
||||
)
|
||||
@@ -66,9 +67,17 @@ func (s *WingetScanner) Scan() ([]client.UpdateReportItem, error) {
|
||||
fmt.Printf("Winget basic scan failed: %v\n", err)
|
||||
}
|
||||
|
||||
// Method 3: Check if this is a known Winget issue and provide helpful error
|
||||
// Method 3: Attempt automatic recovery for known issues
|
||||
if isKnownWingetError(lastErr) {
|
||||
return nil, fmt.Errorf("winget encountered a known issue (exit code %s). This may be due to Windows Update service or system configuration. Try running 'winget upgrade' manually to resolve", getExitCode(lastErr))
|
||||
fmt.Printf("Attempting automatic winget recovery...\n")
|
||||
if updates, err := s.attemptWingetRecovery(); err == nil {
|
||||
fmt.Printf("Winget recovery successful, found %d updates\n", len(updates))
|
||||
return updates, nil
|
||||
} else {
|
||||
fmt.Printf("Winget recovery failed: %v\n", err)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("winget encountered a known issue (exit code %s). This may be due to Windows Update service or system configuration. Automatic recovery was attempted but failed", getExitCode(lastErr))
|
||||
}
|
||||
|
||||
return nil, lastErr
|
||||
@@ -518,4 +527,136 @@ func (s *WingetScanner) GetInstalledPackages() ([]WingetPackage, error) {
|
||||
}
|
||||
|
||||
return packages, nil
|
||||
}
|
||||
|
||||
// attemptWingetRecovery tries to fix common winget issues automatically
|
||||
func (s *WingetScanner) attemptWingetRecovery() ([]client.UpdateReportItem, error) {
|
||||
fmt.Printf("Starting winget recovery process...\n")
|
||||
|
||||
// Recovery Method 1: Reset winget sources (common fix)
|
||||
fmt.Printf("Attempting to reset winget sources...\n")
|
||||
if err := s.resetWingetSources(); err == nil {
|
||||
if updates, scanErr := s.scanWithJSON(); scanErr == nil {
|
||||
fmt.Printf("Recovery successful after source reset\n")
|
||||
return updates, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Recovery Method 2: Update winget itself (silent)
|
||||
fmt.Printf("Attempting to update winget itself...\n")
|
||||
if err := s.updateWingetSilent(); err == nil {
|
||||
// Wait a moment for winget to stabilize
|
||||
time.Sleep(2 * time.Second)
|
||||
if updates, scanErr := s.scanWithJSON(); scanErr == nil {
|
||||
fmt.Printf("Recovery successful after winget update\n")
|
||||
return updates, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Recovery Method 3: Repair Windows App Installer (winget backend)
|
||||
fmt.Printf("Attempting to repair Windows App Installer...\n")
|
||||
if err := s.repairWindowsAppInstaller(); err == nil {
|
||||
// Wait longer for system repairs
|
||||
time.Sleep(5 * time.Second)
|
||||
if updates, scanErr := s.scanWithJSON(); scanErr == nil {
|
||||
fmt.Printf("Recovery successful after Windows App Installer repair\n")
|
||||
return updates, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Recovery Method 4: Force refresh with admin privileges
|
||||
fmt.Printf("Attempting admin refresh...\n")
|
||||
if updates, err := s.scanWithAdminPrivileges(); err == nil {
|
||||
fmt.Printf("Recovery successful with admin privileges\n")
|
||||
return updates, nil
|
||||
}
|
||||
|
||||
// If all recovery attempts failed, return the original error
|
||||
return nil, fmt.Errorf("all winget recovery attempts failed")
|
||||
}
|
||||
|
||||
// resetWingetSources resets winget package sources
|
||||
func (s *WingetScanner) resetWingetSources() error {
|
||||
// Reset winget sources silently
|
||||
cmd := exec.Command("winget", "source", "reset", "--force")
|
||||
_, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to reset winget sources: %v\n", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Add default sources back
|
||||
cmd = exec.Command("winget", "source", "add", "winget", "--accept-package-agreements", "--accept-source-agreements")
|
||||
_, err = cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to add winget source: %v\n", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// updateWingetSilent updates winget itself silently
|
||||
func (s *WingetScanner) updateWingetSilent() error {
|
||||
// Update winget silently with no interaction
|
||||
cmd := exec.Command("winget", "upgrade", "--id", "Microsoft.AppInstaller", "--silent", "--accept-package-agreements", "--accept-source-agreements")
|
||||
_, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to update winget: %v\n", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// repairWindowsAppInstaller attempts to repair the Windows App Installer
|
||||
func (s *WingetScanner) repairWindowsAppInstaller() error {
|
||||
// Try to repair using PowerShell
|
||||
psCmd := `Get-AppxPackage -Name "Microsoft.DesktopAppInstaller" | Repair-AppxPackage -ForceUpdateFromAnyVersion`
|
||||
cmd := exec.Command("powershell", "-ExecutionPolicy", "Bypass", "-Command", psCmd)
|
||||
_, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to repair Windows App Installer: %v\n", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// scanWithAdminPrivileges attempts to scan with elevated privileges if available
|
||||
func (s *WingetScanner) scanWithAdminPrivileges() ([]client.UpdateReportItem, error) {
|
||||
// Try running with elevated privileges using PowerShell
|
||||
psCmd := `Start-Process winget -ArgumentList "list","--outdated","--accept-source-agreements" -Verb RunAs -Wait`
|
||||
cmd := exec.Command("powershell", "-ExecutionPolicy", "Bypass", "-Command", psCmd)
|
||||
|
||||
// This will likely fail without actual admin privileges, but we try anyway
|
||||
_, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
// Fallback to regular scan with different flags
|
||||
return s.scanWithDifferentFlags()
|
||||
}
|
||||
|
||||
// If admin scan succeeded, try to get the results
|
||||
return s.scanWithBasicOutput()
|
||||
}
|
||||
|
||||
// scanWithDifferentFlags tries alternative winget flags
|
||||
func (s *WingetScanner) scanWithDifferentFlags() ([]client.UpdateReportItem, error) {
|
||||
// Try different combination of flags
|
||||
flagVariations := [][]string{
|
||||
{"list", "--outdated", "--accept-source-agreements"},
|
||||
{"list", "--outdated", "--include-unknown"},
|
||||
{"list", "--outdated"},
|
||||
}
|
||||
|
||||
for _, flags := range flagVariations {
|
||||
cmd := exec.Command("winget", flags...)
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err == nil {
|
||||
// Try to parse the output
|
||||
if updates, parseErr := s.parseWingetTextOutput(string(output)); parseErr == nil {
|
||||
return updates, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("all flag variations failed")
|
||||
}
|
||||
@@ -40,47 +40,96 @@ func getWindowsCPUInfo() (*CPUInfo, error) {
|
||||
|
||||
// Try using wmic for CPU information
|
||||
if cmd, err := exec.LookPath("wmic"); err == nil {
|
||||
// Get CPU name
|
||||
// Get CPU name with better error handling
|
||||
if data, err := exec.Command(cmd, "cpu", "get", "Name").Output(); err == nil {
|
||||
lines := strings.Split(string(data), "\n")
|
||||
output := string(data)
|
||||
fmt.Printf("WMIC CPU Name output: '%s'\n", output) // Debug logging
|
||||
lines := strings.Split(output, "\n")
|
||||
for _, line := range lines {
|
||||
if strings.TrimSpace(line) != "" && !strings.Contains(line, "Name") {
|
||||
cpu.ModelName = strings.TrimSpace(line)
|
||||
line = strings.TrimSpace(line)
|
||||
if line != "" && !strings.Contains(line, "Name") {
|
||||
cpu.ModelName = line
|
||||
fmt.Printf("Found CPU model: '%s'\n", line) // Debug logging
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("Failed to get CPU name via wmic: %v\n", err)
|
||||
}
|
||||
|
||||
// Get number of cores
|
||||
if data, err := exec.Command(cmd, "cpu", "get", "NumberOfCores").Output(); err == nil {
|
||||
lines := strings.Split(string(data), "\n")
|
||||
output := string(data)
|
||||
fmt.Printf("WMIC CPU Cores output: '%s'\n", output) // Debug logging
|
||||
lines := strings.Split(output, "\n")
|
||||
for _, line := range lines {
|
||||
if strings.TrimSpace(line) != "" && !strings.Contains(line, "NumberOfCores") {
|
||||
if cores, err := strconv.Atoi(strings.TrimSpace(line)); err == nil {
|
||||
line = strings.TrimSpace(line)
|
||||
if line != "" && !strings.Contains(line, "NumberOfCores") {
|
||||
if cores, err := strconv.Atoi(line); err == nil {
|
||||
cpu.Cores = cores
|
||||
fmt.Printf("Found CPU cores: %d\n", cores) // Debug logging
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("Failed to get CPU cores via wmic: %v\n", err)
|
||||
}
|
||||
|
||||
// Get number of logical processors (threads)
|
||||
if data, err := exec.Command(cmd, "cpu", "get", "NumberOfLogicalProcessors").Output(); err == nil {
|
||||
lines := strings.Split(string(data), "\n")
|
||||
output := string(data)
|
||||
fmt.Printf("WMIC CPU Threads output: '%s'\n", output) // Debug logging
|
||||
lines := strings.Split(output, "\n")
|
||||
for _, line := range lines {
|
||||
if strings.TrimSpace(line) != "" && !strings.Contains(line, "NumberOfLogicalProcessors") {
|
||||
if threads, err := strconv.Atoi(strings.TrimSpace(line)); err == nil {
|
||||
line = strings.TrimSpace(line)
|
||||
if line != "" && !strings.Contains(line, "NumberOfLogicalProcessors") {
|
||||
if threads, err := strconv.Atoi(line); err == nil {
|
||||
cpu.Threads = threads
|
||||
fmt.Printf("Found CPU threads: %d\n", threads) // Debug logging
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("Failed to get CPU threads via wmic: %v\n", err)
|
||||
}
|
||||
|
||||
// If we couldn't get threads, assume it's equal to cores
|
||||
if cpu.Threads == 0 {
|
||||
cpu.Threads = cpu.Cores
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("WMIC command not found, unable to get CPU info\n")
|
||||
}
|
||||
|
||||
// Fallback to PowerShell if wmic failed
|
||||
if cpu.ModelName == "" {
|
||||
fmt.Printf("Attempting PowerShell fallback for CPU info...\n")
|
||||
if psCmd, err := exec.LookPath("powershell"); err == nil {
|
||||
// Get CPU info via PowerShell
|
||||
if data, err := exec.Command(psCmd, "-Command", "Get-CimInstance -ClassName Win32_Processor | Select-Object -First 1 Name,NumberOfCores,NumberOfLogicalProcessors | ConvertTo-Json").Output(); err == nil {
|
||||
fmt.Printf("PowerShell CPU output: '%s'\n", string(data))
|
||||
// Try to parse JSON output (simplified)
|
||||
output := string(data)
|
||||
if strings.Contains(output, "Name") {
|
||||
// Simple string extraction as fallback
|
||||
lines := strings.Split(output, "\n")
|
||||
for _, line := range lines {
|
||||
if strings.Contains(line, "Name") && strings.Contains(line, ":") {
|
||||
parts := strings.Split(line, ":")
|
||||
if len(parts) >= 2 {
|
||||
cpu.ModelName = strings.TrimSpace(strings.Trim(parts[1], " ,\""))
|
||||
fmt.Printf("Found CPU via PowerShell: '%s'\n", cpu.ModelName)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("PowerShell CPU info failed: %v\n", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cpu, nil
|
||||
|
||||
Reference in New Issue
Block a user