package main import ( "archive/tar" "compress/gzip" "crypto/sha256" "encoding/hex" "encoding/json" "flag" "fmt" "io" "log" "net/http" "os" "os/exec" "os/signal" "path/filepath" "regexp" "strconv" "strings" "syscall" "time" ) type Config struct { PauseThreshold float64 `json:"pause_threshold"` ResumeThreshold float64 `json:"resume_threshold"` MaxCPUUsage int `json:"max_cpu_usage"` XMRigPath string `json:"xmrig_path"` CPUPriority int `json:"cpu_priority"` AutoUpdate bool `json:"auto_update"` ForceUpdate bool `json:"force_update"` SkipUpdateCheck bool `json:"skip_update_check"` LogFile string `json:"log_file"` Verbose bool `json:"verbose"` } type MinerManager struct { Config Config IsRunning bool MinerCmd *exec.Cmd MinerPID int LastStats CPUStats } type CPUStats struct { User uint64 Nice uint64 System uint64 Idle uint64 Total uint64 } var httpClient = &http.Client{Timeout: 30 * time.Second} func main() { config := parseFlags() err := ensureXMRigBinary(&config) if err != nil { log.Fatalf("Failed to ensure XMRig binary: %v", err) } manager := &MinerManager{ Config: config, } sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) go func() { <-sigChan fmt.Println("\nStopping miner and exiting...") manager.stopMiner() os.Exit(0) }() fmt.Println("=== XMRig Idle Manager ===") manager.monitorAndManage() } func parseFlags() Config { var c Config flag.Float64Var(&c.PauseThreshold, "pause-threshold", 30.0, "CPU percentage to pause mining") flag.Float64Var(&c.ResumeThreshold, "resume-threshold", 15.0, "CPU percentage to resume mining") flag.IntVar(&c.MaxCPUUsage, "max-cpu-usage", 75, "Maximum CPU usage for XMRig") flag.StringVar(&c.XMRigPath, "xmrig-path", "", "Override XMRig binary location") flag.IntVar(&c.CPUPriority, "cpu-priority", 0, "CPU priority (0-5)") flag.BoolVar(&c.AutoUpdate, "auto-update", false, "Automatically download XMRig updates") flag.BoolVar(&c.ForceUpdate, "force-update", false, "Force update XMRig") flag.BoolVar(&c.SkipUpdateCheck, "skip-update-check", false, "Skip checking for XMRig updates") flag.StringVar(&c.LogFile, "log-file", "/tmp/xmrig.log", "Log file path") flag.BoolVar(&c.Verbose, "verbose", false, "Enable verbose logging") flag.Parse() return c } func ensureXMRigBinary(c *Config) error { if c.XMRigPath != "" { if _, err := os.Stat(c.XMRigPath); err != nil { return fmt.Errorf("specified XMRig path not found: %s", c.XMRigPath) } fmt.Printf("Using user-specified XMRig: %s\n", c.XMRigPath) return nil } path := findXMRigBinary() if path == "" { fmt.Println("XMRig binary not found. Downloading...") return downloadAndInstallXMRig(c) } c.XMRigPath = path if !c.SkipUpdateCheck { checkAndUpdateXMRig(c) } return nil } func findXMRigBinary() string { paths := []string{"./xmrig", "/usr/bin/xmrig", expandPath("~/.local/bin/xmrig")} for _, p := range paths { if _, err := os.Stat(p); err == nil { return p } } return "" } func expandPath(path string) string { if strings.HasPrefix(path, "~/") { return filepath.Join(os.Getenv("HOME"), path[2:]) } return path } func (m *MinerManager) monitorAndManage() { ticker := time.NewTicker(5 * time.Second) for range ticker.C { usage, err := m.getCPUUsage() if err != nil { fmt.Printf("Error during monitoring: %v\n", err) continue } if usage > m.Config.PauseThreshold && m.IsRunning { fmt.Printf("CPU high (%.1f%%). Pausing miner...\n", usage) m.stopMiner() } else if usage < m.Config.ResumeThreshold && !m.IsRunning { fmt.Printf("CPU low (%.1f%%). Resuming miner...\n", usage) m.startMiner() } } } func (m *MinerManager) getCPUUsage() (float64, error) { stats1, err := readCPUStat() if err != nil { return 0, err } time.Sleep(1 * time.Second) stats2, err := readCPUStat() if err != nil { return 0, err } idle := float64(stats2.Idle - stats1.Idle) total := float64(stats2.Total - stats1.Total) if total == 0 { return 0, nil } return (1.0 - idle/total) * 100.0, nil } func readCPUStat() (CPUStats, error) { f, err := os.Open("/proc/stat") if err != nil { return CPUStats{}, err } defer f.Close() var cpu string var user, nice, system, idle, iowait, irq, softirq, steal, guest, guestnice uint64 _, err = fmt.Fscanf(f, "%s %d %d %d %d %d %d %d %d %d %d", &cpu, &user, &nice, &system, &idle, &iowait, &irq, &softirq, &steal, &guest, &guestnice) if err != nil { return CPUStats{}, err } total := user + nice + system + idle + iowait + irq + softirq + steal return CPUStats{user, nice, system, idle, total}, nil } func (m *MinerManager) startMiner() error { args := []string{ "-o", "pool.gravitywell.xyz:11666", "-u", "48p7c...", "--cpu-max-threads-hint", strconv.Itoa(m.Config.MaxCPUUsage), } m.MinerCmd = exec.Command(m.Config.XMRigPath, args...) m.MinerCmd.Stdout = os.Stdout m.MinerCmd.Stderr = os.Stderr if err := m.MinerCmd.Start(); err != nil { return fmt.Errorf("failed to start miner: %w", err) } m.MinerPID = m.MinerCmd.Process.Pid m.IsRunning = true return nil } func (m *MinerManager) stopMiner() { if m.MinerCmd != nil && m.MinerCmd.Process != nil { m.MinerCmd.Process.Signal(syscall.SIGTERM) m.MinerCmd.Wait() } m.IsRunning = false } func checkAndUpdateXMRig(c *Config) {} func downloadAndInstallXMRig(c *Config) error { return nil } func downloadFile(url string, path string) error { return nil } func verifyChecksum(path string, checksum string) error { return nil } func extractTarGz(src string, dest string) error { return nil } func copyFile(src string, dest string) error { return nil } func getLatestXMRigRelease() (string, string, error) { return "", "", nil } func getXMRigVersion(path string) (string, error) { return "", nil } func extractChecksum(data []byte, filename string) (string, error) { return "", nil }