update v-advance

This commit is contained in:
lzx
2025-12-01 15:45:05 +08:00
parent 16173b7ccd
commit de010e39ee
22 changed files with 1963 additions and 175 deletions

View File

@@ -2,12 +2,14 @@ package linux
// lspci | grep -i vga
import (
"client/internal/db"
message "client/internal/msg"
"client/internal/utils"
"fmt"
"log"
"os"
"os/exec"
"os/user"
"path/filepath"
"strings"
"sync"
@@ -17,12 +19,32 @@ import (
)
type LinuxClient struct {
mu sync.Mutex
Auth string
MachineCode string
ID string
MiningConfig message.MiningConfig
Status int // 当前client状态1正在工作2空闲
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
}
func NewLinuxClient(auth string) *LinuxClient {
@@ -80,6 +102,12 @@ func NewLinuxClient(auth string) *LinuxClient {
client.Auth = auth
client.MiningConfig = miningConfig
client.Status = 2
// 初始化sqlite3数据库
db := db.NewSQLiteServer()
client.db = db
client.initHistoryTask()
// 获取主机身份
mac, err := client.GetMACAddress()
if err != nil {
log.Fatalln(err)
@@ -87,9 +115,29 @@ func NewLinuxClient(auth string) *LinuxClient {
}
client.MachineCode = mac
client.ID = auth + "." + mac
return client
}
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)
}
}
}
// 获取 NVIDIA 显卡信息
func getNvidiaGPUInfo() (map[int]message.GPU, error) {
// 使用 nvidia-smi 列出所有 GPU
@@ -189,24 +237,33 @@ func (l *LinuxClient) GetGPUInfo() (map[int]message.GPU, error) {
}
// 获取 MAC 地址
// 获取“机器码”Linux 下改为使用主机 UUID
// 优先从 /sys/class/dmi/id/product_uuid 读取,失败时再尝试 dmidecode
func (l *LinuxClient) GetMACAddress() (string, error) {
// 执行 ifconfig 或 ip link 命令
cmd := exec.Command("ifconfig", "-a")
out, err := cmd.Output()
if err != nil {
return "", err
}
// 查找 MAC 地址
lines := strings.Split(string(out), "\n")
for _, line := range lines {
if strings.Contains(line, "ether") {
parts := strings.Fields(line)
return parts[1], nil
// 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
}
}
return "", fmt.Errorf("MAC address not found")
// 2. 退回使用 dmidecode需要有权限
cmd := exec.Command("dmidecode", "-s", "system-uuid")
out, err := cmd.Output()
if err != nil {
return "", fmt.Errorf("获取主机 UUID 失败: %v", err)
}
uuid := strings.TrimSpace(string(out))
if uuid == "" || strings.EqualFold(uuid, "FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF") {
return "", fmt.Errorf("获取到的主机 UUID 无效: %q", uuid)
}
uuid = strings.Trim(uuid, "{}")
uuid = strings.ToUpper(uuid)
return uuid, nil
}
/*
@@ -249,27 +306,47 @@ func (l *LinuxClient) lolminer(cfg message.ConfigurationMiningMsg) {
log.Fatalf("Error starting lolMiner: %v", err)
return
}
// 添加执行记录
go func() {
err := l.db.InsertMiningTask(cfg)
if err != nil {
log.Fatalf("本次挖矿任务记录失败:%v", err)
}
}()
// 获取 lolMiner 的进程 ID
fmt.Printf("lolMiner started with PID: %d\n", cmd.Process.Pid)
// 记录当前挖矿进程
l.mu.Lock()
l.currentProcess = &currentProcess{
process: cmd,
miner: "lolminer",
}
l.mu.Unlock()
// 获取当前时间戳(秒级)
currentTimestamp := time.Now().Unix()
endTimestamp := int64(cfg.EndTimestamp)
// 计算目标时间戳和当前时间戳之间的差值
if endTimestamp <= currentTimestamp {
fmt.Println("目标时间已经到达,直接执行操作")
// 如果目标时间已经到达,立即结束挖矿进程
fmt.Println("目标时间已经到达,立即结束 lolMiner 挖矿进程")
l.StopMining()
} else {
// 计算需要等待的秒数
waitDuration := time.Second * time.Duration(endTimestamp-currentTimestamp)
fmt.Printf("当前时间戳:%d目标时间戳%d剩余时间%v\n", currentTimestamp, endTimestamp, waitDuration)
// 使用 time.Sleep 等待直到目标时间戳
time.Sleep(waitDuration)
fmt.Println("目标时间到达,开始执行操作")
// 杀掉进程
err = cmd.Process.Kill()
if err != nil {
log.Fatalf("Error killing lolMiner: %v", err)
}
fmt.Println("目标时间到达,开始执行操作,停止 lolMiner 挖矿进程")
// 通过 StopMining 统一停止挖矿进程
l.StopMining()
// 修改执行记录
go func() {
err := l.db.FinishMiningTask(cfg)
if err != nil {
log.Fatalf("修改执行记录失败:%v", err)
}
}()
// 输出进程被杀死的信息
fmt.Println("lolMiner process killed.")
}
@@ -277,6 +354,7 @@ func (l *LinuxClient) lolminer(cfg message.ConfigurationMiningMsg) {
l.mu.Lock()
l.Status = 2
l.mu.Unlock()
}
/*
@@ -317,27 +395,46 @@ func (l *LinuxClient) bzminer(cfg message.ConfigurationMiningMsg) {
log.Fatalf("Error starting bzminer: %v", err)
return
}
// 添加执行记录
go func() {
err := l.db.InsertMiningTask(cfg)
if err != nil {
log.Fatalf("本次挖矿任务记录失败:%v", err)
}
}()
// 获取 bzminer 的进程 ID
fmt.Printf("bzminer started with PID: %d\n", cmd.Process.Pid)
// 记录当前挖矿进程
l.mu.Lock()
l.currentProcess = &currentProcess{
process: cmd,
miner: "bzminer",
}
l.mu.Unlock()
// 获取当前时间戳(秒级)
currentTimestamp := time.Now().Unix()
endTimestamp := int64(cfg.EndTimestamp)
// 计算目标时间戳和当前时间戳之间的差值
if endTimestamp <= currentTimestamp {
fmt.Println("目标时间已经到达,直接执行操作")
fmt.Println("目标时间已经到达,停止 bzminer 挖矿进程")
l.StopMining()
} else {
// 计算需要等待的秒数
waitDuration := time.Second * time.Duration(endTimestamp-currentTimestamp)
fmt.Printf("当前时间戳:%d目标时间戳%d剩余时间%v\n", currentTimestamp, endTimestamp, waitDuration)
// 使用 time.Sleep 等待直到目标时间戳
time.Sleep(waitDuration)
fmt.Println("目标时间到达,开始执行操作")
// 杀掉进程
err = cmd.Process.Kill()
if err != nil {
log.Fatalf("Error killing bzminer: %v", err)
}
fmt.Println("目标时间到达,开始执行操作,停止 bzminer 挖矿进程")
// 通过 StopMining 统一停止挖矿进程
l.StopMining()
// 修改执行记录
go func() {
err := l.db.FinishMiningTask(cfg)
if err != nil {
log.Fatalf("修改执行记录失败:%v", err)
}
}()
// 输出进程被杀死的信息
fmt.Println("bzminer process killed.")
}
@@ -374,7 +471,8 @@ func (l *LinuxClient) rigel(cfg message.ConfigurationMiningMsg) {
}
dir := l.MiningConfig.RigelPath
name := filepath.Join(dir, "rigel")
args := []string{"-a", strings.ToLower(cfg.Algo), "-o", cfg.PoolUrl, "-u", address, "-w", cfg.WorkerID, "--log-file", "logs/miner.log"}
// 禁用 rigel 内置 watchdog避免自动拉起子进程
args := []string{"--no-watchdog", "-a", strings.ToLower(cfg.Algo), "-o", cfg.PoolUrl, "-u", address, "-w", cfg.WorkerID, "--log-file", "logs/miner.log"}
cmd := exec.Command(name, args...)
cmd.Dir = dir
cmd.Stdout = os.Stdout
@@ -386,28 +484,48 @@ func (l *LinuxClient) rigel(cfg message.ConfigurationMiningMsg) {
log.Fatalf("Error starting rigel: %v", err)
return
}
// 添加执行记录
go func() {
err := l.db.InsertMiningTask(cfg)
if err != nil {
log.Fatalf("本次挖矿任务记录失败:%v", err)
}
}()
// 获取 rigel 的进程 ID
fmt.Printf("rigel started with PID: %d\n", cmd.Process.Pid)
// 记录当前挖矿进程
l.mu.Lock()
l.currentProcess = &currentProcess{
process: cmd,
miner: "rigel",
}
l.mu.Unlock()
// 获取当前时间戳(秒级)
currentTimestamp := time.Now().Unix()
endTimestamp := int64(cfg.EndTimestamp)
// 计算目标时间戳和当前时间戳之间的差值
if endTimestamp <= currentTimestamp {
fmt.Println("目标时间已经到达,直接执行操作")
// 如果目标时间已经到达,立即结束 rigel 挖矿进程
fmt.Println("目标时间已经到达,立即结束 rigel 挖矿进程")
l.StopMining()
} else {
// 计算需要等待的秒数
waitDuration := time.Second * time.Duration(endTimestamp-currentTimestamp)
fmt.Printf("当前时间戳:%d目标时间戳%d剩余时间%v\n", currentTimestamp, endTimestamp, waitDuration)
// 使用 time.Sleep 等待直到目标时间戳
time.Sleep(waitDuration)
fmt.Println("目标时间到达,开始执行操作")
// 杀掉进程
err = cmd.Process.Kill()
if err != nil {
log.Fatalf("Error killing rigel: %v", err)
}
// 输出进程被杀死的信息
fmt.Println("目标时间到达,开始执行操作,结束 rigel 挖矿进程")
// 通过 StopMining 统一停止挖矿进程
l.StopMining()
// 修改执行记录
go func() {
err := l.db.FinishMiningTask(cfg)
if err != nil {
log.Fatalf("修改执行记录失败:%v", err)
}
}()
// 输出进程被结束的信息
fmt.Println("rigel process killed.")
}
log.Printf("当前挖矿任务已执行完毕:币=%s, 算法=%s, 矿池=%s, 截止时间=%d", cfg.Coin, cfg.Algo, cfg.Pool, cfg.EndTimestamp)
@@ -445,3 +563,42 @@ func (l *LinuxClient) Mining(cfg message.ConfigurationMiningMsg) error {
}
return nil
}
// 主动终止当前挖矿进程
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("当前挖矿任务已被手动停止")
}

View File

@@ -3,12 +3,14 @@ package src
import (
message "client/internal/msg"
"fmt"
"log"
"sync"
)
type OS interface {
GetGPUInfo() (map[int]message.GPU, error)
Mining(cfg message.ConfigurationMiningMsg) error
StopMining()
GetMACAddress() (string, error)
}
@@ -61,3 +63,11 @@ func (s *SystemServer) GetMACAddress(osName string) (string, error) {
}
return "", fmt.Errorf("错误的操作系统:%s", osName)
}
func (s *SystemServer) StopMining(osName string) {
if srv, ok := s.systems[osName]; ok {
srv.StopMining()
} else {
log.Fatalf("错误的操作系统:%s", osName)
}
}

View File

@@ -1,13 +1,139 @@
package windows
// Windows 版本的客户端实现
import (
"client/internal/db"
message "client/internal/msg"
"client/internal/utils"
"fmt"
"log"
"os"
"os/exec"
"strconv"
"path/filepath"
"strings"
"sync"
"time"
"gopkg.in/ini.v1"
)
type WindowsClient struct {
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 检查当前进程是否具有管理员权限Windows
// 通过执行 `net session` 命令来判断:该命令只有在管理员权限下才会成功。
func CheckPermission() error {
cmd := exec.Command("net", "session")
if err := cmd.Run(); err != nil {
return fmt.Errorf("当前用户不是管理员,请以管理员身份运行本程序(右键以管理员运行)")
}
return nil
}
func NewWindowsClient(auth string) *WindowsClient {
cfg, err := ini.Load("mining.windows.conf")
if err != nil {
log.Fatalf("获取挖矿配置失败: %v", err)
log.Printf("客户端已退出,请重新检查配置文件(%s)是否存在", "mining.windows.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 = &WindowsClient{}
client.Auth = auth
client.MiningConfig = miningConfig
client.Status = 2
// 初始化sqlite3数据库
db := db.NewSQLiteServer()
client.db = db
mac, err := client.GetMACAddress()
if err != nil {
log.Fatalln(err)
panic("获取当前主机信息失败,程序已退出,请检查网络后重新启动本客户端。")
}
client.MachineCode = mac
client.ID = auth + "." + mac
return client
}
func (w *WindowsClient) initHistoryTask() {
exsits, task := w.db.LoadMiningTask()
if exsits {
currentTs := time.Now().Unix()
if currentTs < int64(task.EndTimestamp) {
w.mu.Lock()
w.Status = 1
w.mu.Unlock()
err := w.Mining(task)
if err != nil {
log.Fatalf("重新开启挖矿任务失败,请手动检查:%v", err)
return
}
} else {
w.db.FinishMiningTask(task)
}
}
}
// 获取 NVIDIA 显卡信息
func getNvidiaGPUInfo() (map[int]message.GPU, error) {
// 使用 nvidia-smi 列出所有 GPU
@@ -56,10 +182,10 @@ func parseMemory(memStr string) (float64, error) {
return mem, nil
}
// 获取 AMD 显卡信息
func getAmdGPUInfo() (map[int]message.GPU, error) {
// 使用 wmic 列出所有显卡信息
cmd := exec.Command("wmic", "path", "Win32_VideoController", "get", "Name,AdapterRAM")
// 获取其他显卡信息(如 Intel 或 AMD
func getOtherGPUInfo() (map[int]message.GPU, error) {
// Windows 下使用 wmic 命令获取显卡信息
cmd := exec.Command("wmic", "path", "win32_VideoController", "get", "name")
output, err := cmd.Output()
if err != nil {
return nil, fmt.Errorf("无法执行 wmic 命令: %v", err)
@@ -72,27 +198,29 @@ func getAmdGPUInfo() (map[int]message.GPU, error) {
gpus := make(map[int]message.GPU)
gpuIndex := 0
for _, line := range lines[1:] { // 跳过标题行
for _, line := range lines {
line = strings.TrimSpace(line)
if line != "" {
parts := strings.Fields(line)
if len(parts) >= 2 {
// 获取显存大小并转换为 MB
memStr := parts[1]
mem, err := parseMemoryFromBytes(memStr)
if err != nil {
mem = 0 // 如果显存无法解析,则设置为 0
}
// 用正确的品牌名填充
gpus[gpuIndex] = message.GPU{
Brand: "AMD",
Model: parts[0],
Mem: mem,
}
gpuIndex++
}
// 跳过标题行和空行
if line == "" || line == "Name" {
continue
}
// 判断显卡品牌
brand := "Unknown"
if strings.Contains(line, "NVIDIA") {
brand = "NVIDIA"
} else if strings.Contains(line, "AMD") || strings.Contains(line, "Radeon") {
brand = "AMD"
} else if strings.Contains(line, "Intel") {
brand = "Intel"
}
// 将 GPU 信息存储到 map 中
gpus[gpuIndex] = message.GPU{
Brand: brand,
Model: line,
Mem: 0, // wmic 无法直接获取显存,需要其他方法
}
gpuIndex++
}
if len(gpus) == 0 {
@@ -102,48 +230,351 @@ func getAmdGPUInfo() (map[int]message.GPU, error) {
return gpus, nil
}
// 从字节转换显存为 MB
func parseMemoryFromBytes(memStr string) (float64, error) {
// 将字节转换为 MB
memBytes, err := strconv.Atoi(memStr)
if err != nil {
return 0, fmt.Errorf("字节转显存失败: %v", err)
}
// 显存从字节转为MB
memMB := float64(memBytes) / (1024 * 1024)
return memMB, nil
}
// 获取所有 GPU 信息
func GetGPUInfo() (map[int]message.GPU, error) {
func (w *WindowsClient) GetGPUInfo() (map[int]message.GPU, error) {
// 尝试获取 NVIDIA GPU 信息
gpus, err := getNvidiaGPUInfo()
if err == nil {
return gpus, nil
}
// 尝试获取 AMD GPU 信息
gpus, err = getAmdGPUInfo()
if err == nil {
return gpus, nil
// 如果没有 NVIDIA 显卡,尝试获取其他类型显卡信息
return getOtherGPUInfo()
}
// Windows 版本:使用主机 UUID 作为“机器码”返回给上层(仍然复用 GetMACAddress 接口名)
// 通过 PowerShell 命令Get-WmiObject -Class Win32_ComputerSystemProduct | Select-Object -ExpandProperty UUID
func (w *WindowsClient) GetMACAddress() (string, error) {
// 注意:在 Go 中调用 PowerShell
cmd := exec.Command("powershell", "-Command", "Get-WmiObject -Class Win32_ComputerSystemProduct | Select-Object -ExpandProperty UUID")
out, err := cmd.Output()
if err != nil {
return "", fmt.Errorf("获取主机 UUID 失败: %v", err)
}
return nil, fmt.Errorf("未找到任何显卡信息")
uuid := strings.TrimSpace(string(out))
if uuid == "" || strings.EqualFold(uuid, "FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF") {
return "", fmt.Errorf("获取到的主机 UUID 无效: %q", uuid)
}
// 统一转换为大写,去掉花括号
uuid = strings.Trim(uuid, "{}")
uuid = strings.ToUpper(uuid)
return uuid, nil
}
/*
{
"coin-algo": {
"lolminer": {
"pool_name": "url"
},
"bzminer": {
"pool_name": "url"
},
"rigel": {
"pool_name": "url"
}
}
}
配置lolminer
Windows 下使用 lolMiner.exe
*/
func (w *WindowsClient) lolminer(cfg message.ConfigurationMiningMsg) {
w.mu.Lock()
if w.Status != 2 {
log.Fatalf("当前还有挖矿任务正在进行中:币=%s, 算法=%s, 矿池=%s, 截止时间=%d", cfg.Coin, cfg.Algo, cfg.Pool, cfg.EndTimestamp)
return
}
w.Status = 1
w.mu.Unlock()
var address string
if cfg.WalletMining {
address = cfg.WalletAddress
} else {
address = cfg.PoolUser
}
dir := w.MiningConfig.LolMinerPath
name := filepath.Join(dir, "lolMiner.exe")
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
}
// 添加执行记录
go func() {
err := w.db.InsertMiningTask(cfg)
if err != nil {
log.Fatalf("本次挖矿任务记录失败:%v", err)
}
}()
// 获取 lolMiner 的进程 ID
fmt.Printf("lolMiner started with PID: %d\n", cmd.Process.Pid)
// 记录当前挖矿进程
w.mu.Lock()
w.currentProcess = &currentProcess{
process: cmd,
miner: "lolminer",
}
w.mu.Unlock()
// 获取当前时间戳(秒级)
currentTimestamp := time.Now().Unix()
endTimestamp := int64(cfg.EndTimestamp)
// 计算目标时间戳和当前时间戳之间的差值
if endTimestamp <= currentTimestamp {
// 如果目标时间已经到达,立即结束挖矿进程
fmt.Println("目标时间已经到达,立即结束 lolMiner 挖矿进程")
w.StopMining()
// 修改执行记录
go func() {
err := w.db.FinishMiningTask(cfg)
if err != nil {
log.Fatalf("修改执行记录失败:%v", err)
}
}()
} else {
// 计算需要等待的秒数
waitDuration := time.Second * time.Duration(endTimestamp-currentTimestamp)
fmt.Printf("当前时间戳:%d目标时间戳%d剩余时间%v\n", currentTimestamp, endTimestamp, waitDuration)
// 使用 time.Sleep 等待直到目标时间戳
time.Sleep(waitDuration)
fmt.Println("目标时间到达,开始执行操作,结束 lolMiner 挖矿进程")
// 通过 StopMining 统一停止挖矿进程
w.StopMining()
// 修改执行记录
go func() {
err := w.db.FinishMiningTask(cfg)
if err != nil {
log.Fatalf("修改执行记录失败:%v", err)
}
}()
}
log.Printf("当前挖矿任务已执行完毕:币=%s, 算法=%s, 矿池=%s, 截止时间=%d", cfg.Coin, cfg.Algo, cfg.Pool, cfg.EndTimestamp)
w.mu.Lock()
w.Status = 2
w.mu.Unlock()
}
/*
配置bzminer
Windows 下使用 bzminer.exe
*/
func (w *WindowsClient) bzminer(cfg message.ConfigurationMiningMsg) {
w.mu.Lock()
if w.Status != 2 {
log.Fatalf("当前还有挖矿任务正在进行中:币=%s, 算法=%s, 矿池=%s, 截止时间=%d", cfg.Coin, cfg.Algo, cfg.Pool, cfg.EndTimestamp)
return
}
w.Status = 1
w.mu.Unlock()
var address string
if cfg.WalletMining {
address = cfg.WalletAddress
} else {
address = cfg.PoolUser
}
dir := w.MiningConfig.BzMinerPath
name := filepath.Join(dir, "bzminer.exe")
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
}
// 添加执行记录
go func() {
err := w.db.InsertMiningTask(cfg)
if err != nil {
log.Fatalf("本次挖矿任务记录失败:%v", err)
}
}()
// 获取 bzminer 的进程 ID
fmt.Printf("bzminer started with PID: %d\n", cmd.Process.Pid)
// 记录当前挖矿进程
w.mu.Lock()
w.currentProcess = &currentProcess{
process: cmd,
miner: "bzminer",
}
w.mu.Unlock()
// 获取当前时间戳(秒级)
currentTimestamp := time.Now().Unix()
endTimestamp := int64(cfg.EndTimestamp)
// 计算目标时间戳和当前时间戳之间的差值
if endTimestamp <= currentTimestamp {
// 如果目标时间已经到达,立即结束挖矿进程
fmt.Println("目标时间已经到达,立即结束 bzminer 挖矿进程")
w.StopMining()
fmt.Println("bzminer process killed.")
} else {
// 计算需要等待的秒数
waitDuration := time.Second * time.Duration(endTimestamp-currentTimestamp)
fmt.Printf("当前时间戳:%d目标时间戳%d剩余时间%v\n", currentTimestamp, endTimestamp, waitDuration)
// 使用 time.Sleep 等待直到目标时间戳
time.Sleep(waitDuration)
fmt.Println("目标时间到达,开始执行操作,结束 bzminer 挖矿进程")
// 通过 StopMining 统一停止挖矿进程
w.StopMining()
// 修改执行记录
go func() {
err := w.db.FinishMiningTask(cfg)
if err != nil {
log.Fatalf("修改执行记录失败:%v", err)
}
}()
// 输出进程被结束的信息
fmt.Println("bzminer process killed.")
}
log.Printf("当前挖矿任务已执行完毕:币=%s, 算法=%s, 矿池=%s, 截止时间=%d", cfg.Coin, cfg.Algo, cfg.Pool, cfg.EndTimestamp)
w.mu.Lock()
w.Status = 2
w.mu.Unlock()
}
/*
配置rigel
Windows 下使用 rigel.exe
*/
func (w *WindowsClient) rigel(cfg message.ConfigurationMiningMsg) {
w.mu.Lock()
if w.Status != 2 {
log.Fatalf("当前还有挖矿任务正在进行中:币=%s, 算法=%s, 矿池=%s, 截止时间=%d", cfg.Coin, cfg.Algo, cfg.Pool, cfg.EndTimestamp)
return
}
w.Status = 1
w.mu.Unlock()
var address string
if cfg.WalletMining {
address = cfg.WalletAddress
} else {
address = cfg.PoolUser
}
dir := w.MiningConfig.RigelPath
name := filepath.Join(dir, "rigel.exe")
// 禁用 rigel 内置 watchdog避免自动拉起子进程
args := []string{"--no-watchdog", "-a", strings.ToLower(cfg.Algo), "-o", cfg.PoolUrl, "-u", address, "-w", cfg.WorkerID, "--log-file", "logs/miner.log"}
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
}
// 添加执行记录
go func() {
err := w.db.InsertMiningTask(cfg)
if err != nil {
log.Fatalf("本次挖矿任务记录失败:%v", err)
}
}()
// 获取 rigel 的进程 ID
fmt.Printf("rigel started with PID: %d\n", cmd.Process.Pid)
// 记录当前挖矿进程
w.mu.Lock()
w.currentProcess = &currentProcess{
process: cmd,
miner: "rigel",
}
w.mu.Unlock()
// 获取当前时间戳(秒级)
currentTimestamp := time.Now().Unix()
endTimestamp := int64(cfg.EndTimestamp)
// 计算目标时间戳和当前时间戳之间的差值
if endTimestamp <= currentTimestamp {
// 如果目标时间已经到达,立即结束挖矿进程
fmt.Println("目标时间已经到达,立即结束 rigel 挖矿进程")
w.StopMining()
fmt.Println("rigel process killed.")
} else {
// 计算需要等待的秒数
waitDuration := time.Second * time.Duration(endTimestamp-currentTimestamp)
fmt.Printf("当前时间戳:%d目标时间戳%d剩余时间%v\n", currentTimestamp, endTimestamp, waitDuration)
// 使用 time.Sleep 等待直到目标时间戳
time.Sleep(waitDuration)
fmt.Println("目标时间到达,开始执行操作,结束 rigel 挖矿进程")
// 通过 StopMining 统一停止挖矿进程
w.StopMining()
// 修改执行记录
go func() {
err := w.db.FinishMiningTask(cfg)
if err != nil {
log.Fatalf("修改执行记录失败:%v", err)
}
}()
// 输出进程被结束的信息
fmt.Println("rigel process killed.")
}
log.Printf("当前挖矿任务已执行完毕:币=%s, 算法=%s, 矿池=%s, 截止时间=%d", cfg.Coin, cfg.Algo, cfg.Pool, cfg.EndTimestamp)
w.mu.Lock()
w.Status = 2
w.mu.Unlock()
}
// StopMining 主动终止当前挖矿进程
func (w *WindowsClient) StopMining() {
w.mu.Lock()
cp := w.currentProcess
w.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)
// Windows 下统一使用 Kill 结束进程
if err := cp.process.Process.Kill(); err != nil {
log.Printf("停止挖矿进程失败:%v", err)
}
// 清理状态
w.mu.Lock()
w.currentProcess = nil
w.Status = 2
w.mu.Unlock()
log.Println("当前挖矿任务已被手动停止")
}
// 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 (w *WindowsClient) Mining(cfg message.ConfigurationMiningMsg) error {
info := cfg.Coin + "-" + cfg.Algo
switch info {
case "SHA3X-SHA3X":
go w.lolminer(cfg) // SHA3X
case "XNA-KawPow":
go w.bzminer(cfg) // xna
case "CLORE-KawPow":
go w.bzminer(cfg) // clore
case "CFX-Octopus":
go w.rigel(cfg) // octopus
case "IRON-IronFish":
go w.lolminer(cfg)
case "NEXA-NexaPow":
go w.lolminer(cfg)
case "KLS-KarlsenHash":
go w.lolminer(cfg)
case "RVN-KawPow":
go w.bzminer(cfg) // rvn
case "ERG-Autolykos":
go w.bzminer(cfg) // ergo
case "XEL-Xelishashv2":
go w.rigel(cfg)
default:
return fmt.Errorf("不支持%s算法", info)
}
return nil
}