228 lines
5.8 KiB
Go
228 lines
5.8 KiB
Go
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 }
|