2025-11-26 11:39:20 +08:00
|
|
|
|
package linux
|
|
|
|
|
|
|
|
|
|
|
|
// lspci | grep -i vga
|
|
|
|
|
|
import (
|
2025-12-01 15:45:05 +08:00
|
|
|
|
"client/internal/db"
|
2025-11-26 11:39:20 +08:00
|
|
|
|
message "client/internal/msg"
|
|
|
|
|
|
"client/internal/utils"
|
|
|
|
|
|
"fmt"
|
|
|
|
|
|
"log"
|
|
|
|
|
|
"os"
|
|
|
|
|
|
"os/exec"
|
2025-12-01 15:45:05 +08:00
|
|
|
|
"os/user"
|
2025-11-26 11:39:20 +08:00
|
|
|
|
"path/filepath"
|
|
|
|
|
|
"strings"
|
|
|
|
|
|
"sync"
|
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
|
|
"gopkg.in/ini.v1"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
type LinuxClient struct {
|
2025-12-01 15:45:05 +08:00
|
|
|
|
mu sync.Mutex
|
|
|
|
|
|
Auth string
|
|
|
|
|
|
MachineCode string
|
|
|
|
|
|
ID string
|
|
|
|
|
|
MiningConfig message.MiningConfig
|
|
|
|
|
|
db *db.SQLiteServer
|
|
|
|
|
|
Status int // 当前client状态,1正在工作,2空闲
|
|
|
|
|
|
currentProcess *currentProcess // 当前挖矿进程及类型
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
type currentProcess struct {
|
|
|
|
|
|
process *exec.Cmd
|
|
|
|
|
|
miner string
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// CheckPermission 检查当前进程是否具备运行本客户端所需的权限(Linux)
|
|
|
|
|
|
// 要求使用 root 运行,否则很多命令(如 dmidecode、nvidia-smi 等)可能失败。
|
|
|
|
|
|
func CheckPermission() error {
|
|
|
|
|
|
u, err := user.Current()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return fmt.Errorf("获取当前用户信息失败: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
if u.Uid != "0" {
|
|
|
|
|
|
return fmt.Errorf("当前用户(%s)不是 root 用户,请使用 sudo 或 root 账号运行本程序", u.Username)
|
|
|
|
|
|
}
|
|
|
|
|
|
return nil
|
2025-11-26 11:39:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func NewLinuxClient(auth string) *LinuxClient {
|
|
|
|
|
|
cfg, err := ini.Load("mining.linux.conf")
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
log.Fatalf("获取挖矿配置失败: %v", err)
|
|
|
|
|
|
log.Printf("客户端已退出,请重新检查配置文件(%s)是否存在", "mining.linux.conf")
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
// 解析配置
|
|
|
|
|
|
var miningConfig message.MiningConfig
|
|
|
|
|
|
// 解析 [bzminer] 部分
|
|
|
|
|
|
sectionBzMiner := cfg.Section("bzminer")
|
|
|
|
|
|
miningConfig.BzMinerPath = sectionBzMiner.Key("path").String()
|
|
|
|
|
|
|
|
|
|
|
|
// 解析 [lolminer] 部分
|
|
|
|
|
|
sectionLolMiner := cfg.Section("lolminer")
|
|
|
|
|
|
miningConfig.LolMinerPath = sectionLolMiner.Key("path").String()
|
|
|
|
|
|
|
|
|
|
|
|
// 解析 [rigel] 部分
|
|
|
|
|
|
sectionRigel := cfg.Section("rigel")
|
|
|
|
|
|
miningConfig.RigelPath = sectionRigel.Key("path").String()
|
|
|
|
|
|
|
|
|
|
|
|
// 解析 [proxy] 部分
|
|
|
|
|
|
sectionProxy := cfg.Section("proxy")
|
|
|
|
|
|
miningConfig.ProxyEnabled, _ = sectionProxy.Key("proxy").Bool()
|
|
|
|
|
|
|
|
|
|
|
|
if miningConfig.BzMinerPath != "" {
|
|
|
|
|
|
result := utils.CheckFileExists(miningConfig.BzMinerPath)
|
|
|
|
|
|
if !result {
|
|
|
|
|
|
log.Fatalf("未检测到bzminer挖矿软件的存在,请确认是否已经安装bzminer,并核对下列路径:%s", miningConfig.BzMinerPath)
|
|
|
|
|
|
log.Println("客户端已退出,确认路径后请重启客户端")
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if miningConfig.LolMinerPath != "" {
|
|
|
|
|
|
result := utils.CheckFileExists(miningConfig.LolMinerPath)
|
|
|
|
|
|
if !result {
|
|
|
|
|
|
log.Fatalf("未检测到lolminer挖矿软件的存在,请确认是否已经安装lolminer,并核对下列路径:%s", miningConfig.LolMinerPath)
|
|
|
|
|
|
log.Println("客户端已退出,确认路径后请重启客户端")
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if miningConfig.RigelPath != "" {
|
|
|
|
|
|
result := utils.CheckFileExists(miningConfig.RigelPath)
|
|
|
|
|
|
if !result {
|
|
|
|
|
|
log.Fatalf("未检测到rigel挖矿软件的存在,请确认是否已经安装rigel,并核对下列路径:%s", miningConfig.RigelPath)
|
|
|
|
|
|
log.Println("客户端已退出,确认路径后请重启客户端")
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
var client = &LinuxClient{}
|
|
|
|
|
|
client.Auth = auth
|
|
|
|
|
|
client.MiningConfig = miningConfig
|
|
|
|
|
|
client.Status = 2
|
2025-12-01 15:45:05 +08:00
|
|
|
|
|
|
|
|
|
|
// 初始化sqlite3数据库
|
|
|
|
|
|
db := db.NewSQLiteServer()
|
|
|
|
|
|
client.db = db
|
|
|
|
|
|
client.initHistoryTask()
|
|
|
|
|
|
// 获取主机身份
|
2025-11-26 11:39:20 +08:00
|
|
|
|
mac, err := client.GetMACAddress()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
log.Fatalln(err)
|
|
|
|
|
|
panic("获取当前主机信息失败,程序已退出,请检查网络后重新启动本客户端。")
|
|
|
|
|
|
}
|
|
|
|
|
|
client.MachineCode = mac
|
|
|
|
|
|
client.ID = auth + "." + mac
|
2025-12-01 15:45:05 +08:00
|
|
|
|
|
2025-11-26 11:39:20 +08:00
|
|
|
|
return client
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-01 15:45:05 +08:00
|
|
|
|
func (l *LinuxClient) initHistoryTask() {
|
|
|
|
|
|
exsits, task := l.db.LoadMiningTask()
|
|
|
|
|
|
if exsits {
|
|
|
|
|
|
currentTs := time.Now().Unix()
|
|
|
|
|
|
if currentTs < int64(task.EndTimestamp) {
|
|
|
|
|
|
l.mu.Lock()
|
|
|
|
|
|
l.Status = 1
|
|
|
|
|
|
l.mu.Unlock()
|
|
|
|
|
|
err := l.Mining(task)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
log.Fatalf("重新开启挖矿任务失败,请手动检查:%v", err)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
l.db.FinishMiningTask(task)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-26 11:39:20 +08:00
|
|
|
|
// 获取 NVIDIA 显卡信息
|
|
|
|
|
|
func getNvidiaGPUInfo() (map[int]message.GPU, error) {
|
|
|
|
|
|
// 使用 nvidia-smi 列出所有 GPU
|
|
|
|
|
|
cmd := exec.Command("nvidia-smi", "--query-gpu=name,memory.total", "--format=csv,noheader,nounits")
|
|
|
|
|
|
output, err := cmd.Output()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, fmt.Errorf("无法执行 nvidia-smi 命令: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 解析输出
|
|
|
|
|
|
info := strings.TrimSpace(string(output))
|
|
|
|
|
|
lines := strings.Split(info, "\n")
|
|
|
|
|
|
|
|
|
|
|
|
gpus := make(map[int]message.GPU)
|
|
|
|
|
|
|
|
|
|
|
|
for i, line := range lines {
|
|
|
|
|
|
parts := strings.Split(strings.TrimSpace(line), ", ")
|
|
|
|
|
|
if len(parts) < 2 {
|
|
|
|
|
|
return nil, fmt.Errorf("nvidia-smi 输出格式错误")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 解析显存容量
|
|
|
|
|
|
mem, err := parseMemory(parts[1])
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, fmt.Errorf("无法解析显存容量: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 将每个 GPU 的信息存储到 map 中
|
|
|
|
|
|
gpus[i] = message.GPU{
|
|
|
|
|
|
Brand: "NVIDIA",
|
|
|
|
|
|
Model: parts[0],
|
|
|
|
|
|
Mem: mem,
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return gpus, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 解析显存容量字符串并返回 float64 类型
|
|
|
|
|
|
func parseMemory(memStr string) (float64, error) {
|
|
|
|
|
|
var mem float64
|
|
|
|
|
|
_, err := fmt.Sscanf(memStr, "%f", &mem)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return 0, fmt.Errorf("显存解析失败: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
return mem, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取其他显卡信息(如 Intel 或 AMD)
|
|
|
|
|
|
func getOtherGPUInfo() (map[int]message.GPU, error) {
|
|
|
|
|
|
// 使用 lspci 列出所有显卡设备
|
|
|
|
|
|
cmd := exec.Command("lspci", "-v")
|
|
|
|
|
|
output, err := cmd.Output()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, fmt.Errorf("无法执行 lspci 命令: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 解析输出,获取显卡信息
|
|
|
|
|
|
info := strings.TrimSpace(string(output))
|
|
|
|
|
|
lines := strings.Split(info, "\n")
|
|
|
|
|
|
|
|
|
|
|
|
gpus := make(map[int]message.GPU)
|
|
|
|
|
|
gpuIndex := 0
|
|
|
|
|
|
|
|
|
|
|
|
for _, line := range lines {
|
|
|
|
|
|
// 假设输出中包含 Intel 或 AMD GPU
|
|
|
|
|
|
if strings.Contains(line, "VGA compatible controller") {
|
|
|
|
|
|
if strings.Contains(line, "Intel") || strings.Contains(line, "AMD") {
|
|
|
|
|
|
// 将 Intel/AMD GPU 信息存储到 map 中
|
|
|
|
|
|
gpus[gpuIndex] = message.GPU{
|
|
|
|
|
|
Brand: "Intel/AMD",
|
|
|
|
|
|
Model: line, // 这里可以根据实际 lspci 输出提取显卡型号
|
|
|
|
|
|
Mem: 0, // 无法通过 lspci 获取显存
|
|
|
|
|
|
}
|
|
|
|
|
|
gpuIndex++
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if len(gpus) == 0 {
|
|
|
|
|
|
return nil, fmt.Errorf("未找到显卡信息")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return gpus, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取所有 GPU 信息
|
|
|
|
|
|
func (l *LinuxClient) GetGPUInfo() (map[int]message.GPU, error) {
|
|
|
|
|
|
// 尝试获取 NVIDIA GPU 信息
|
|
|
|
|
|
gpus, err := getNvidiaGPUInfo()
|
|
|
|
|
|
if err == nil {
|
|
|
|
|
|
return gpus, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 如果没有 NVIDIA 显卡,尝试获取其他类型显卡信息
|
|
|
|
|
|
return getOtherGPUInfo()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取 MAC 地址
|
2025-12-01 15:45:05 +08:00
|
|
|
|
// 获取“机器码”:Linux 下改为使用主机 UUID
|
|
|
|
|
|
// 优先从 /sys/class/dmi/id/product_uuid 读取,失败时再尝试 dmidecode
|
2025-11-26 11:39:20 +08:00
|
|
|
|
func (l *LinuxClient) GetMACAddress() (string, error) {
|
2025-12-01 15:45:05 +08:00
|
|
|
|
// 1. 优先读取内核提供的 product_uuid 文件(大多数物理机/虚拟机都支持)
|
|
|
|
|
|
const uuidPath = "/sys/class/dmi/id/product_uuid"
|
|
|
|
|
|
if data, err := os.ReadFile(uuidPath); err == nil {
|
|
|
|
|
|
uuid := strings.TrimSpace(string(data))
|
|
|
|
|
|
if uuid != "" && !strings.EqualFold(uuid, "FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF") {
|
|
|
|
|
|
uuid = strings.Trim(uuid, "{}")
|
|
|
|
|
|
uuid = strings.ToUpper(uuid)
|
|
|
|
|
|
return uuid, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 退回使用 dmidecode(需要有权限)
|
|
|
|
|
|
cmd := exec.Command("dmidecode", "-s", "system-uuid")
|
2025-11-26 11:39:20 +08:00
|
|
|
|
out, err := cmd.Output()
|
|
|
|
|
|
if err != nil {
|
2025-12-01 15:45:05 +08:00
|
|
|
|
return "", fmt.Errorf("获取主机 UUID 失败: %v", err)
|
2025-11-26 11:39:20 +08:00
|
|
|
|
}
|
2025-12-01 15:45:05 +08:00
|
|
|
|
uuid := strings.TrimSpace(string(out))
|
|
|
|
|
|
if uuid == "" || strings.EqualFold(uuid, "FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF") {
|
|
|
|
|
|
return "", fmt.Errorf("获取到的主机 UUID 无效: %q", uuid)
|
2025-11-26 11:39:20 +08:00
|
|
|
|
}
|
2025-12-01 15:45:05 +08:00
|
|
|
|
uuid = strings.Trim(uuid, "{}")
|
|
|
|
|
|
uuid = strings.ToUpper(uuid)
|
|
|
|
|
|
return uuid, nil
|
2025-11-26 11:39:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
配置lolminer
|
|
|
|
|
|
#!/bin/bash
|
|
|
|
|
|
|
|
|
|
|
|
POOL=47.108.221.51:3333
|
|
|
|
|
|
WALLET=m2test.11x12
|
|
|
|
|
|
ALGO=NEXA
|
|
|
|
|
|
|
|
|
|
|
|
./lolMiner --algo $ALGO --pool $POOL --user $WALLET $@
|
|
|
|
|
|
*/
|
|
|
|
|
|
func (l *LinuxClient) lolminer(cfg message.ConfigurationMiningMsg) {
|
|
|
|
|
|
l.mu.Lock()
|
|
|
|
|
|
if l.Status != 2 {
|
|
|
|
|
|
log.Fatalf("当前还有挖矿任务正在进行中:币=%s, 算法=%s, 矿池=%s, 截止时间=%d", cfg.Coin, cfg.Algo, cfg.Pool, cfg.EndTimestamp)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
l.Status = 1
|
|
|
|
|
|
l.mu.Unlock()
|
|
|
|
|
|
|
|
|
|
|
|
var address string
|
|
|
|
|
|
if cfg.WalletMining {
|
|
|
|
|
|
address = cfg.WalletAddress
|
|
|
|
|
|
} else {
|
|
|
|
|
|
address = cfg.PoolUser
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
dir := l.MiningConfig.LolMinerPath
|
|
|
|
|
|
name := filepath.Join(dir, "lolMiner")
|
|
|
|
|
|
args := []string{"--algo", cfg.Coin, "--pool", cfg.PoolUrl, "--user", address + "." + cfg.WorkerID}
|
|
|
|
|
|
cmd := exec.Command(name, args...)
|
|
|
|
|
|
cmd.Dir = dir
|
|
|
|
|
|
cmd.Stdout = os.Stdout
|
|
|
|
|
|
cmd.Stderr = os.Stderr
|
|
|
|
|
|
log.Printf("执行命令:%s %s", name, strings.Join(args, " "))
|
|
|
|
|
|
// 启动进程
|
|
|
|
|
|
err := cmd.Start()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
log.Fatalf("Error starting lolMiner: %v", err)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2025-12-01 15:45:05 +08:00
|
|
|
|
// 添加执行记录
|
|
|
|
|
|
go func() {
|
|
|
|
|
|
err := l.db.InsertMiningTask(cfg)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
log.Fatalf("本次挖矿任务记录失败:%v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
}()
|
2025-11-26 11:39:20 +08:00
|
|
|
|
// 获取 lolMiner 的进程 ID
|
|
|
|
|
|
fmt.Printf("lolMiner started with PID: %d\n", cmd.Process.Pid)
|
2025-12-01 15:45:05 +08:00
|
|
|
|
|
|
|
|
|
|
// 记录当前挖矿进程
|
|
|
|
|
|
l.mu.Lock()
|
|
|
|
|
|
l.currentProcess = ¤tProcess{
|
|
|
|
|
|
process: cmd,
|
|
|
|
|
|
miner: "lolminer",
|
|
|
|
|
|
}
|
|
|
|
|
|
l.mu.Unlock()
|
2025-11-26 11:39:20 +08:00
|
|
|
|
// 获取当前时间戳(秒级)
|
|
|
|
|
|
currentTimestamp := time.Now().Unix()
|
|
|
|
|
|
endTimestamp := int64(cfg.EndTimestamp)
|
|
|
|
|
|
// 计算目标时间戳和当前时间戳之间的差值
|
|
|
|
|
|
if endTimestamp <= currentTimestamp {
|
2025-12-01 15:45:05 +08:00
|
|
|
|
// 如果目标时间已经到达,立即结束挖矿进程
|
|
|
|
|
|
fmt.Println("目标时间已经到达,立即结束 lolMiner 挖矿进程")
|
|
|
|
|
|
l.StopMining()
|
2025-11-26 11:39:20 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
// 计算需要等待的秒数
|
|
|
|
|
|
waitDuration := time.Second * time.Duration(endTimestamp-currentTimestamp)
|
|
|
|
|
|
fmt.Printf("当前时间戳:%d,目标时间戳:%d,剩余时间:%v\n", currentTimestamp, endTimestamp, waitDuration)
|
|
|
|
|
|
// 使用 time.Sleep 等待直到目标时间戳
|
|
|
|
|
|
time.Sleep(waitDuration)
|
2025-12-01 15:45:05 +08:00
|
|
|
|
fmt.Println("目标时间到达,开始执行操作,停止 lolMiner 挖矿进程")
|
|
|
|
|
|
// 通过 StopMining 统一停止挖矿进程
|
|
|
|
|
|
l.StopMining()
|
|
|
|
|
|
// 修改执行记录
|
|
|
|
|
|
go func() {
|
|
|
|
|
|
err := l.db.FinishMiningTask(cfg)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
log.Fatalf("修改执行记录失败:%v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
}()
|
2025-11-26 11:39:20 +08:00
|
|
|
|
// 输出进程被杀死的信息
|
|
|
|
|
|
fmt.Println("lolMiner process killed.")
|
|
|
|
|
|
}
|
|
|
|
|
|
log.Printf("当前挖矿任务已执行完毕:币=%s, 算法=%s, 矿池=%s, 截止时间=%d", cfg.Coin, cfg.Algo, cfg.Pool, cfg.EndTimestamp)
|
|
|
|
|
|
l.mu.Lock()
|
|
|
|
|
|
l.Status = 2
|
|
|
|
|
|
l.mu.Unlock()
|
2025-12-01 15:45:05 +08:00
|
|
|
|
|
2025-11-26 11:39:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
配置bzminer
|
|
|
|
|
|
#!/bin/bash
|
|
|
|
|
|
|
|
|
|
|
|
POOL=47.108.221.51:3333
|
|
|
|
|
|
WALLET=m2test.11x12
|
|
|
|
|
|
ALGO=NEXA
|
|
|
|
|
|
|
|
|
|
|
|
./bzminer -a $ALGO -w $WALLET -p $POOL
|
|
|
|
|
|
*/
|
|
|
|
|
|
func (l *LinuxClient) bzminer(cfg message.ConfigurationMiningMsg) {
|
|
|
|
|
|
l.mu.Lock()
|
|
|
|
|
|
if l.Status != 2 {
|
|
|
|
|
|
log.Fatalf("当前还有挖矿任务正在进行中:币=%s, 算法=%s, 矿池=%s, 截止时间=%d", cfg.Coin, cfg.Algo, cfg.Pool, cfg.EndTimestamp)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
l.Status = 1
|
|
|
|
|
|
l.mu.Unlock()
|
|
|
|
|
|
var address string
|
|
|
|
|
|
if cfg.WalletMining {
|
|
|
|
|
|
address = cfg.WalletAddress
|
|
|
|
|
|
} else {
|
|
|
|
|
|
address = cfg.PoolUser
|
|
|
|
|
|
}
|
|
|
|
|
|
dir := l.MiningConfig.BzMinerPath
|
|
|
|
|
|
name := filepath.Join(dir, "bzminer")
|
|
|
|
|
|
args := []string{"-a", cfg.Coin, "-w", address + "." + cfg.WorkerID, "-p", cfg.PoolUrl}
|
|
|
|
|
|
cmd := exec.Command(name, args...)
|
|
|
|
|
|
cmd.Dir = dir
|
|
|
|
|
|
cmd.Stdout = os.Stdout
|
|
|
|
|
|
cmd.Stderr = os.Stderr
|
|
|
|
|
|
log.Printf("执行命令:%s %s", name, strings.Join(args, " "))
|
|
|
|
|
|
// 启动进程
|
|
|
|
|
|
err := cmd.Start()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
log.Fatalf("Error starting bzminer: %v", err)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2025-12-01 15:45:05 +08:00
|
|
|
|
// 添加执行记录
|
|
|
|
|
|
go func() {
|
|
|
|
|
|
err := l.db.InsertMiningTask(cfg)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
log.Fatalf("本次挖矿任务记录失败:%v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
}()
|
2025-11-26 11:39:20 +08:00
|
|
|
|
// 获取 bzminer 的进程 ID
|
|
|
|
|
|
fmt.Printf("bzminer started with PID: %d\n", cmd.Process.Pid)
|
2025-12-01 15:45:05 +08:00
|
|
|
|
|
|
|
|
|
|
// 记录当前挖矿进程
|
|
|
|
|
|
l.mu.Lock()
|
|
|
|
|
|
l.currentProcess = ¤tProcess{
|
|
|
|
|
|
process: cmd,
|
|
|
|
|
|
miner: "bzminer",
|
|
|
|
|
|
}
|
|
|
|
|
|
l.mu.Unlock()
|
2025-11-26 11:39:20 +08:00
|
|
|
|
// 获取当前时间戳(秒级)
|
|
|
|
|
|
currentTimestamp := time.Now().Unix()
|
|
|
|
|
|
endTimestamp := int64(cfg.EndTimestamp)
|
|
|
|
|
|
// 计算目标时间戳和当前时间戳之间的差值
|
|
|
|
|
|
if endTimestamp <= currentTimestamp {
|
2025-12-01 15:45:05 +08:00
|
|
|
|
fmt.Println("目标时间已经到达,停止 bzminer 挖矿进程")
|
|
|
|
|
|
l.StopMining()
|
2025-11-26 11:39:20 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
// 计算需要等待的秒数
|
|
|
|
|
|
waitDuration := time.Second * time.Duration(endTimestamp-currentTimestamp)
|
|
|
|
|
|
fmt.Printf("当前时间戳:%d,目标时间戳:%d,剩余时间:%v\n", currentTimestamp, endTimestamp, waitDuration)
|
|
|
|
|
|
// 使用 time.Sleep 等待直到目标时间戳
|
|
|
|
|
|
time.Sleep(waitDuration)
|
2025-12-01 15:45:05 +08:00
|
|
|
|
fmt.Println("目标时间到达,开始执行操作,停止 bzminer 挖矿进程")
|
|
|
|
|
|
// 通过 StopMining 统一停止挖矿进程
|
|
|
|
|
|
l.StopMining()
|
|
|
|
|
|
// 修改执行记录
|
|
|
|
|
|
go func() {
|
|
|
|
|
|
err := l.db.FinishMiningTask(cfg)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
log.Fatalf("修改执行记录失败:%v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
}()
|
2025-11-26 11:39:20 +08:00
|
|
|
|
// 输出进程被杀死的信息
|
|
|
|
|
|
fmt.Println("bzminer process killed.")
|
|
|
|
|
|
}
|
|
|
|
|
|
log.Printf("当前挖矿任务已执行完毕:币=%s, 算法=%s, 矿池=%s, 截止时间=%d", cfg.Coin, cfg.Algo, cfg.Pool, cfg.EndTimestamp)
|
|
|
|
|
|
l.mu.Lock()
|
|
|
|
|
|
l.Status = 2
|
|
|
|
|
|
l.mu.Unlock()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
配置rigel
|
|
|
|
|
|
#!/bin/bash
|
|
|
|
|
|
|
|
|
|
|
|
POOL=47.108.221.51:3333
|
|
|
|
|
|
WALLET=m2test
|
|
|
|
|
|
USER=11x12
|
|
|
|
|
|
ALGO=nexapow
|
|
|
|
|
|
|
|
|
|
|
|
./rigel -a $ALGO -o $POOL -u $WALLET -w $USER --log-file logs/miner.log
|
|
|
|
|
|
*/
|
|
|
|
|
|
func (l *LinuxClient) rigel(cfg message.ConfigurationMiningMsg) {
|
|
|
|
|
|
l.mu.Lock()
|
|
|
|
|
|
if l.Status != 2 {
|
|
|
|
|
|
log.Fatalf("当前还有挖矿任务正在进行中:币=%s, 算法=%s, 矿池=%s, 截止时间=%d", cfg.Coin, cfg.Algo, cfg.Pool, cfg.EndTimestamp)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
l.Status = 1
|
|
|
|
|
|
l.mu.Unlock()
|
|
|
|
|
|
var address string
|
|
|
|
|
|
if cfg.WalletMining {
|
|
|
|
|
|
address = cfg.WalletAddress
|
|
|
|
|
|
} else {
|
|
|
|
|
|
address = cfg.PoolUser
|
|
|
|
|
|
}
|
|
|
|
|
|
dir := l.MiningConfig.RigelPath
|
|
|
|
|
|
name := filepath.Join(dir, "rigel")
|
2025-12-01 15:45:05 +08:00
|
|
|
|
// 禁用 rigel 内置 watchdog,避免自动拉起子进程
|
|
|
|
|
|
args := []string{"--no-watchdog", "-a", strings.ToLower(cfg.Algo), "-o", cfg.PoolUrl, "-u", address, "-w", cfg.WorkerID, "--log-file", "logs/miner.log"}
|
2025-11-26 11:39:20 +08:00
|
|
|
|
cmd := exec.Command(name, args...)
|
|
|
|
|
|
cmd.Dir = dir
|
|
|
|
|
|
cmd.Stdout = os.Stdout
|
|
|
|
|
|
cmd.Stderr = os.Stderr
|
|
|
|
|
|
log.Printf("执行命令:%s %s", name, strings.Join(args, " "))
|
|
|
|
|
|
// 启动进程
|
|
|
|
|
|
err := cmd.Start()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
log.Fatalf("Error starting rigel: %v", err)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2025-12-01 15:45:05 +08:00
|
|
|
|
// 添加执行记录
|
|
|
|
|
|
go func() {
|
|
|
|
|
|
err := l.db.InsertMiningTask(cfg)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
log.Fatalf("本次挖矿任务记录失败:%v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
}()
|
2025-11-26 11:39:20 +08:00
|
|
|
|
// 获取 rigel 的进程 ID
|
|
|
|
|
|
fmt.Printf("rigel started with PID: %d\n", cmd.Process.Pid)
|
2025-12-01 15:45:05 +08:00
|
|
|
|
|
|
|
|
|
|
// 记录当前挖矿进程
|
|
|
|
|
|
l.mu.Lock()
|
|
|
|
|
|
l.currentProcess = ¤tProcess{
|
|
|
|
|
|
process: cmd,
|
|
|
|
|
|
miner: "rigel",
|
|
|
|
|
|
}
|
|
|
|
|
|
l.mu.Unlock()
|
2025-11-26 11:39:20 +08:00
|
|
|
|
// 获取当前时间戳(秒级)
|
|
|
|
|
|
currentTimestamp := time.Now().Unix()
|
|
|
|
|
|
endTimestamp := int64(cfg.EndTimestamp)
|
|
|
|
|
|
// 计算目标时间戳和当前时间戳之间的差值
|
|
|
|
|
|
if endTimestamp <= currentTimestamp {
|
2025-12-01 15:45:05 +08:00
|
|
|
|
// 如果目标时间已经到达,立即结束 rigel 挖矿进程
|
|
|
|
|
|
fmt.Println("目标时间已经到达,立即结束 rigel 挖矿进程")
|
|
|
|
|
|
l.StopMining()
|
2025-11-26 11:39:20 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
// 计算需要等待的秒数
|
|
|
|
|
|
waitDuration := time.Second * time.Duration(endTimestamp-currentTimestamp)
|
|
|
|
|
|
fmt.Printf("当前时间戳:%d,目标时间戳:%d,剩余时间:%v\n", currentTimestamp, endTimestamp, waitDuration)
|
|
|
|
|
|
// 使用 time.Sleep 等待直到目标时间戳
|
|
|
|
|
|
time.Sleep(waitDuration)
|
2025-12-01 15:45:05 +08:00
|
|
|
|
fmt.Println("目标时间到达,开始执行操作,结束 rigel 挖矿进程")
|
|
|
|
|
|
// 通过 StopMining 统一停止挖矿进程
|
|
|
|
|
|
l.StopMining()
|
|
|
|
|
|
// 修改执行记录
|
|
|
|
|
|
go func() {
|
|
|
|
|
|
err := l.db.FinishMiningTask(cfg)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
log.Fatalf("修改执行记录失败:%v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
}()
|
|
|
|
|
|
// 输出进程被结束的信息
|
2025-11-26 11:39:20 +08:00
|
|
|
|
fmt.Println("rigel process killed.")
|
|
|
|
|
|
}
|
|
|
|
|
|
log.Printf("当前挖矿任务已执行完毕:币=%s, 算法=%s, 矿池=%s, 截止时间=%d", cfg.Coin, cfg.Algo, cfg.Pool, cfg.EndTimestamp)
|
|
|
|
|
|
l.mu.Lock()
|
|
|
|
|
|
l.Status = 2
|
|
|
|
|
|
l.mu.Unlock()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// XTM-rigel(1%), XNA-bzminer(1%), CLORE-bzminer(1%), CFX-rigel(2%), IRON-lolminer(1%), NEXA-lolminer(2%), KLS-lolminer(1%), RVN-bzminer(1%), ERG-bzminer(1%), XEL-rigel(2%)
|
|
|
|
|
|
func (l *LinuxClient) Mining(cfg message.ConfigurationMiningMsg) error {
|
|
|
|
|
|
info := cfg.Coin + "-" + cfg.Algo
|
|
|
|
|
|
switch info {
|
|
|
|
|
|
case "XTM-SHA3X":
|
|
|
|
|
|
go l.lolminer(cfg)
|
|
|
|
|
|
case "XNA-KawPow":
|
|
|
|
|
|
go l.bzminer(cfg)
|
|
|
|
|
|
case "CLORE-KawPow":
|
|
|
|
|
|
go l.bzminer(cfg)
|
|
|
|
|
|
case "CFX-Octopus":
|
|
|
|
|
|
go l.rigel(cfg)
|
|
|
|
|
|
case "IRON-IronFish":
|
|
|
|
|
|
go l.lolminer(cfg)
|
|
|
|
|
|
case "NEXA-NexaPow":
|
|
|
|
|
|
go l.lolminer(cfg)
|
|
|
|
|
|
case "KLS-KarlsenHash":
|
|
|
|
|
|
go l.lolminer(cfg)
|
|
|
|
|
|
case "RVN-KawPow":
|
|
|
|
|
|
go l.bzminer(cfg)
|
|
|
|
|
|
case "ERG-Autolykos":
|
|
|
|
|
|
go l.bzminer(cfg)
|
|
|
|
|
|
case "XEL-Xelishashv2":
|
|
|
|
|
|
go l.rigel(cfg)
|
|
|
|
|
|
default:
|
|
|
|
|
|
return fmt.Errorf("不支持%s算法", info)
|
|
|
|
|
|
}
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
2025-12-01 15:45:05 +08:00
|
|
|
|
|
|
|
|
|
|
// 主动终止当前挖矿进程
|
|
|
|
|
|
func (l *LinuxClient) StopMining() {
|
|
|
|
|
|
l.mu.Lock()
|
|
|
|
|
|
cp := l.currentProcess
|
|
|
|
|
|
l.mu.Unlock()
|
|
|
|
|
|
|
|
|
|
|
|
if cp == nil || cp.process == nil || cp.process.Process == nil {
|
|
|
|
|
|
log.Println("当前没有正在进行的挖矿任务")
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
log.Printf("准备停止当前挖矿任务,miner=%s, pid=%d", cp.miner, cp.process.Process.Pid)
|
|
|
|
|
|
|
|
|
|
|
|
// 根据挖矿软件类型选择合适的停止方式
|
|
|
|
|
|
switch strings.ToLower(cp.miner) {
|
|
|
|
|
|
case "rigel":
|
|
|
|
|
|
// 优先尝试优雅退出(Interrupt),失败再 Kill
|
|
|
|
|
|
if err := cp.process.Process.Signal(os.Interrupt); err != nil {
|
|
|
|
|
|
log.Printf("向 rigel 发送 Interrupt 失败,尝试 Kill: %v", err)
|
|
|
|
|
|
if errKill := cp.process.Process.Kill(); errKill != nil {
|
|
|
|
|
|
log.Printf("Kill rigel 失败:%v", errKill)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
default:
|
|
|
|
|
|
// 其它挖矿软件直接 Kill
|
|
|
|
|
|
if err := cp.process.Process.Kill(); err != nil {
|
|
|
|
|
|
log.Printf("停止挖矿进程失败:%v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 清理状态
|
|
|
|
|
|
l.mu.Lock()
|
|
|
|
|
|
l.currentProcess = nil
|
|
|
|
|
|
l.Status = 2
|
|
|
|
|
|
l.mu.Unlock()
|
|
|
|
|
|
|
|
|
|
|
|
log.Println("当前挖矿任务已被手动停止")
|
|
|
|
|
|
}
|