version-1

This commit is contained in:
lzx
2025-11-26 11:39:20 +08:00
commit 789be70a53
16 changed files with 1182 additions and 0 deletions

447
internal/src/linux/linux.go Normal file
View File

@@ -0,0 +1,447 @@
package linux
// lspci | grep -i vga
import (
message "client/internal/msg"
"client/internal/utils"
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
"strings"
"sync"
"time"
"gopkg.in/ini.v1"
)
type LinuxClient struct {
mu sync.Mutex
Auth string
MachineCode string
ID string
MiningConfig message.MiningConfig
Status int // 当前client状态1正在工作2空闲
}
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
mac, err := client.GetMACAddress()
if err != nil {
log.Fatalln(err)
panic("获取当前主机信息失败,程序已退出,请检查网络后重新启动本客户端。")
}
client.MachineCode = mac
client.ID = auth + "." + mac
return client
}
// 获取 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 地址
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
}
}
return "", fmt.Errorf("MAC address not found")
}
/*
配置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
}
// 获取 lolMiner 的进程 ID
fmt.Printf("lolMiner started with PID: %d\n", cmd.Process.Pid)
// 获取当前时间戳(秒级)
currentTimestamp := time.Now().Unix()
endTimestamp := int64(cfg.EndTimestamp)
// 计算目标时间戳和当前时间戳之间的差值
if endTimestamp <= currentTimestamp {
fmt.Println("目标时间已经到达,直接执行操作")
} 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 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()
}
/*
配置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
}
// 获取 bzminer 的进程 ID
fmt.Printf("bzminer started with PID: %d\n", cmd.Process.Pid)
// 获取当前时间戳(秒级)
currentTimestamp := time.Now().Unix()
endTimestamp := int64(cfg.EndTimestamp)
// 计算目标时间戳和当前时间戳之间的差值
if endTimestamp <= currentTimestamp {
fmt.Println("目标时间已经到达,直接执行操作")
} 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 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")
args := []string{"-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
}
// 获取 rigel 的进程 ID
fmt.Printf("rigel started with PID: %d\n", cmd.Process.Pid)
// 获取当前时间戳(秒级)
currentTimestamp := time.Now().Unix()
endTimestamp := int64(cfg.EndTimestamp)
// 计算目标时间戳和当前时间戳之间的差值
if endTimestamp <= currentTimestamp {
fmt.Println("目标时间已经到达,直接执行操作")
} 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 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
}