version-1
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/test/
|
||||||
111
README.md
Normal file
111
README.md
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
# 云算力平台卖方客户端
|
||||||
|
|
||||||
|
本程序为云算力平台卖方客户端,用于将卖方身份与GPU主机绑定,实现自动挖矿匹配功能。
|
||||||
|
|
||||||
|
## 功能特性
|
||||||
|
|
||||||
|
1. **身份绑定**:将云算力平台上的卖方身份和GPU主机进行绑定
|
||||||
|
2. **GPU信息上报**:自动获取并上报主机GPU详细参数(GPU型号、显存容量等)
|
||||||
|
3. **自动挖矿匹配**:自动匹配买方的挖矿需求,无需手动操作挖矿
|
||||||
|
|
||||||
|
## 系统要求
|
||||||
|
|
||||||
|
- Go 1.25.4 或更高版本
|
||||||
|
- Windows 或 Linux 操作系统
|
||||||
|
- 已配置好挖矿环境(显卡驱动、挖矿软件、执行权限等)
|
||||||
|
|
||||||
|
## 编译方法
|
||||||
|
|
||||||
|
### Windows 系统
|
||||||
|
|
||||||
|
在项目根目录下运行:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cmd\windows.bat
|
||||||
|
```
|
||||||
|
|
||||||
|
编译后的可执行文件将位于 `bin/client.exe`
|
||||||
|
|
||||||
|
### Linux 系统
|
||||||
|
|
||||||
|
在项目根目录下运行:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
chmod +x cmd/linux.sh
|
||||||
|
./cmd/linux.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
编译后的可执行文件将位于 `bin/client`
|
||||||
|
|
||||||
|
## 使用方法
|
||||||
|
|
||||||
|
1. **准备身份文件**:在 `bin` 目录下创建 `auth` 文件,包含你的卖方身份信息
|
||||||
|
|
||||||
|
2. **运行客户端**:
|
||||||
|
- Windows: 运行 `bin\client.exe`
|
||||||
|
- Linux: 运行 `bin/client`
|
||||||
|
|
||||||
|
3. 客户端将自动:
|
||||||
|
- 读取身份信息
|
||||||
|
- 获取主机MAC地址和GPU信息
|
||||||
|
- 连接到云算力平台服务器
|
||||||
|
- 等待并处理挖矿任务
|
||||||
|
|
||||||
|
## 重要注意事项
|
||||||
|
|
||||||
|
### 启动前准备
|
||||||
|
|
||||||
|
1. **挖矿环境配置**:确保客户端执行主机已配置好挖矿环境,包括:
|
||||||
|
- 显卡驱动已正确安装
|
||||||
|
- 挖矿软件已安装并配置
|
||||||
|
- 执行权限已设置
|
||||||
|
- 可以手动通过挖矿软件进行挖矿
|
||||||
|
|
||||||
|
### GPU 操作注意事项
|
||||||
|
|
||||||
|
#### 移除 GPU
|
||||||
|
|
||||||
|
- 如果要对本机GPU进行移除(拔出GPU)操作,云算力平台会同步移除对应的GPU
|
||||||
|
- **重要**:如果在相关GPU有租约且没有在平台申请故障处理的情况下直接移除GPU,会导致产生罚没
|
||||||
|
- **建议**:在有租约的情况下要移除故障GPU,请第一时间前往平台申请故障处理,在平台确认后再进行移除GPU的操作
|
||||||
|
|
||||||
|
#### 更换 GPU
|
||||||
|
|
||||||
|
- 如果要对本机GPU进行更换(拔出后又新插入GPU)操作,云算力平台会重新读取GPU数据
|
||||||
|
- 如果更换型号相同,则会按原有配置上架
|
||||||
|
- 如果更换的型号不同,则需在更换后前往卖家中心手动调整上架配置
|
||||||
|
- **重要**:如果在相关GPU有租约且没有在平台申请故障处理的情况下直接更换GPU,可能会导致产生罚没
|
||||||
|
- **建议**:在有租约的情况下要更换故障GPU,请第一时间前往平台申请故障处理,在平台确认后再进行更换GPU的操作
|
||||||
|
|
||||||
|
## 项目结构
|
||||||
|
|
||||||
|
```
|
||||||
|
cloud-client/
|
||||||
|
├── bin/ # 编译输出目录
|
||||||
|
│ ├── auth # 身份认证文件(需手动创建)
|
||||||
|
│ ├── mining.linux.conf # Linux 挖矿配置
|
||||||
|
│ └── mining.windows.conf # Windows 挖矿配置
|
||||||
|
├── cmd/ # 主程序目录
|
||||||
|
│ ├── main.go # 程序入口
|
||||||
|
│ ├── windows.bat # Windows 编译脚本
|
||||||
|
│ └── linux.sh # Linux 编译脚本
|
||||||
|
├── internal/ # 内部包
|
||||||
|
│ ├── client.go # 客户端主逻辑
|
||||||
|
│ ├── msg/ # 消息处理
|
||||||
|
│ ├── src/ # 系统相关实现
|
||||||
|
│ │ ├── linux/ # Linux 系统实现
|
||||||
|
│ │ └── windows/ # Windows 系统实现
|
||||||
|
│ └── utils/ # 工具函数
|
||||||
|
├── go.mod # Go 模块定义
|
||||||
|
└── README.md # 本文件
|
||||||
|
```
|
||||||
|
|
||||||
|
## 依赖项
|
||||||
|
|
||||||
|
- `github.com/google/uuid` v1.6.0
|
||||||
|
- `gopkg.in/ini.v1` v1.67.0
|
||||||
|
|
||||||
|
## 许可证
|
||||||
|
|
||||||
|
[根据实际情况填写]
|
||||||
|
|
||||||
12
bin/mining.linux.conf
Normal file
12
bin/mining.linux.conf
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#请确认您的主机上安装了下列挖矿软件,确认后可以打开注释,并修改其路径,如果没有安装,请勿打开注释
|
||||||
|
[bzminer]
|
||||||
|
# path=/path/bzminer
|
||||||
|
[lolminer]
|
||||||
|
# path=/home/a11/桌面/lolminer/1.98
|
||||||
|
[rigel]
|
||||||
|
# path=/path/rigel
|
||||||
|
|
||||||
|
#如果您的网络无法直接连通各个矿池,需要使用各大矿池专用网咯,请打开proxy的注释
|
||||||
|
#打开此注释后会使用各大矿池的专用网络,每笔订单额外增加1%的网络费用
|
||||||
|
[proxy]
|
||||||
|
# proxy=true
|
||||||
13
bin/mining.windows.conf
Normal file
13
bin/mining.windows.conf
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#请确认您的主机上安装了下列挖矿软件,确认后可以打开注释,并修改其路径,如果没有安装,请勿打开注释
|
||||||
|
#请使用双\\,否则可能无法解析出准确的路径
|
||||||
|
[bzminer]
|
||||||
|
# path=C:\\path\\bzminer
|
||||||
|
[lolminer]
|
||||||
|
# path=C:\\path\\lolminer
|
||||||
|
[rigel]
|
||||||
|
# path=C:\\path\\rigel
|
||||||
|
|
||||||
|
#如果您的网络无法直接连通各个矿池,需要使用各大矿池专用网咯,请打开proxy的注释
|
||||||
|
#打开此注释后会使用各大矿池的专用网络,每笔订单额外增加1%的网络费用
|
||||||
|
[proxy]
|
||||||
|
# proxy=true
|
||||||
10
cmd/linux.sh
Normal file
10
cmd/linux.sh
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
go build -o ../bin/client main.go
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
echo "编译成功!可执行文件位于: ../bin/client"
|
||||||
|
else
|
||||||
|
echo "编译失败!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
7
cmd/main.go
Normal file
7
cmd/main.go
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import client "client/internal"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
client.Star()
|
||||||
|
}
|
||||||
10
cmd/windows.bat
Normal file
10
cmd/windows.bat
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
@echo off
|
||||||
|
cd /d %~dp0
|
||||||
|
go build -o ../bin/client.exe main.go
|
||||||
|
if %errorlevel% equ 0 (
|
||||||
|
echo 编译成功!可执行文件位于: ..\bin\client.exe
|
||||||
|
) else (
|
||||||
|
echo 编译失败!
|
||||||
|
exit /b %errorlevel%
|
||||||
|
)
|
||||||
|
|
||||||
8
go.mod
Normal file
8
go.mod
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
module client
|
||||||
|
|
||||||
|
go 1.25.4
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/google/uuid v1.6.0
|
||||||
|
gopkg.in/ini.v1 v1.67.0
|
||||||
|
)
|
||||||
4
go.sum
Normal file
4
go.sum
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||||
|
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
207
internal/client.go
Normal file
207
internal/client.go
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
/*
|
||||||
|
本程序为云算力平台卖方客户端,主要提供以下功能:
|
||||||
|
1,将云算力平台上的卖方身份和GPU主机绑定
|
||||||
|
2,通过本客户端,可以使云算力平台获取到卖方具体每台机器的详细参数,包括GPU型号、显存容量等
|
||||||
|
3,卖方可通过本客户端自动匹配买方的挖矿需求,即卖方无需再买方下单后手动操作挖矿
|
||||||
|
|
||||||
|
卖家在启动客户端之前需要注意以下事项:
|
||||||
|
1,确定客户端执行主机已经配置好挖矿环境,包括显卡驱动、挖矿软件(指定挖矿软件)、执行权限等,即执行本客户端的用户可以手动通过挖矿软件进行挖矿
|
||||||
|
2,如果要对本机GPU进行移除(拔出GPU)操作,云算力平台会同步移除对应的GPU
|
||||||
|
3,如果在相关GPU有租约且没有在平台申请故障处理的情况下直接移除(拔出)GPU,会导致产生罚没,因此在有租约的情况下要移除故障GPU,请第一时间前往平台申请故障处理,在平台确认后再进行移除GPU的操作
|
||||||
|
4,如果要对本机GPU进行更换(拔出后又新插入GPU)操作,云算力平台会重新读取GPU数据,如果更换型号相同,则会按原有配置上架,如果更换的型号不同,则需在更换后前往卖家中心手动调整上架配置
|
||||||
|
5,如果在相关GPU有租约且没有在平台申请故障处理的情况下直接更换GPU,可能会导致产生罚没,因此在有租约的情况下要更换故障GPU,请第一时间前往平台申请故障处理,在平台确认后再进行更换GPU的操作
|
||||||
|
*/
|
||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
message "client/internal/msg"
|
||||||
|
"client/internal/src"
|
||||||
|
"client/internal/src/linux"
|
||||||
|
"client/internal/utils"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
Auth string
|
||||||
|
MachineCode string
|
||||||
|
ServerConn net.Conn // 服务连接
|
||||||
|
GPUs map[int]message.GPU // {"gpu编号": message.GPU{}, ...}
|
||||||
|
os *src.SystemServer
|
||||||
|
osName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newClient(url string) *Client {
|
||||||
|
var client = &Client{}
|
||||||
|
// 读取身份文件
|
||||||
|
auth, err := utils.ReadFile("./auth")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("获取客户端身份失败:%v", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
client.Auth = auth
|
||||||
|
|
||||||
|
os := src.NewSystemServer()
|
||||||
|
sys := runtime.GOOS
|
||||||
|
if sys == "linux" {
|
||||||
|
linux_ := linux.NewLinuxClient(auth)
|
||||||
|
os.ResiterSystem("linux", linux_)
|
||||||
|
} else {
|
||||||
|
|
||||||
|
}
|
||||||
|
client.os = os
|
||||||
|
client.osName = sys
|
||||||
|
|
||||||
|
// 读取主机MAC地址信息
|
||||||
|
var machine_code string
|
||||||
|
machine_code, err = os.GetMACAddress(sys)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
panic("获取当前主机信息失败,程序已退出,请检查网络后重新启动本客户端。")
|
||||||
|
}
|
||||||
|
utils.WirteFile("./machinecode", machine_code)
|
||||||
|
|
||||||
|
client.MachineCode = machine_code
|
||||||
|
|
||||||
|
gpus, err := os.GetGPUInfo(sys)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
panic("获取当前主机GPU数据失败,程序已退出,请检查GPU驱动等程序后重新启动本客户端。")
|
||||||
|
}
|
||||||
|
client.GPUs = gpus
|
||||||
|
server_conn, err := net.Dial("tcp", url)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("客户端连接到服务器失败:%v", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
defer server_conn.Close()
|
||||||
|
|
||||||
|
client.ServerConn = server_conn
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) sendMachineCode() {
|
||||||
|
var msg message.ServerMsg
|
||||||
|
msg.ID = c.Auth + "." + c.MachineCode
|
||||||
|
msg.Method = "auth.machineCode"
|
||||||
|
msg.Params = c.GPUs
|
||||||
|
msgByte, err := json.Marshal(msg)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("消息(%v)序列化失败:%v", msg, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.send(msgByte)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) receiveMsg() {
|
||||||
|
buffer := make([]byte, 1024)
|
||||||
|
|
||||||
|
for {
|
||||||
|
n, err := c.ServerConn.Read(buffer)
|
||||||
|
if err != nil {
|
||||||
|
if err.Error() == "EOF" {
|
||||||
|
// 服务端关闭连接时,退出接收循环
|
||||||
|
log.Println("服务端关闭了连接")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Println("接收数据失败:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
msgByte := buffer[:n]
|
||||||
|
go c.handleReceiveMsg(msgByte)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) send(msg []byte) {
|
||||||
|
_, err := c.ServerConn.Write(msg)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("发送消息失败,消息内容:%s", string(msg))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) handleReceiveMsg(msg []byte) {
|
||||||
|
var data message.ServerMsg
|
||||||
|
err := json.Unmarshal(msg, &data)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("解析接收到的消息失败:%v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
parts := strings.Split(data.ID, ".")
|
||||||
|
if len(parts) != 2 {
|
||||||
|
log.Fatalf("解析通信协议(server->client)失败")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
auth, machine_code := parts[0], parts[1]
|
||||||
|
if c.Auth != auth || c.MachineCode != machine_code {
|
||||||
|
log.Fatalf("客户端接收到错误的服务端消息")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch data.Method {
|
||||||
|
case "mining.req":
|
||||||
|
mining_msg, ok := data.Params.(message.ConfigurationMiningMsg)
|
||||||
|
if ok {
|
||||||
|
// 这里开始挖矿
|
||||||
|
err := c.os.Mining(c.osName, mining_msg)
|
||||||
|
if err != nil {
|
||||||
|
sendMsg_str := message.ServerMsgResp{
|
||||||
|
ID: c.Auth + "." + c.MachineCode,
|
||||||
|
Result: false,
|
||||||
|
Data: err,
|
||||||
|
}
|
||||||
|
sendMsg_byte, err := json.Marshal(sendMsg_str)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("序列化%v失败:%v", sendMsg_str, err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
c.send(sendMsg_byte) // 返回失败消息
|
||||||
|
}
|
||||||
|
// 挖矿开始
|
||||||
|
data := message.ConfigurationMiningResp{
|
||||||
|
Coin: mining_msg.Coin,
|
||||||
|
Algo: mining_msg.Algo,
|
||||||
|
Pool: mining_msg.Pool,
|
||||||
|
PoolUrl: mining_msg.PoolUrl,
|
||||||
|
WorkerID: mining_msg.WorkerID,
|
||||||
|
WalletAddress: mining_msg.WalletAddress,
|
||||||
|
WatchUrl: "", // 这里需要根据矿池自动生成
|
||||||
|
}
|
||||||
|
sendMsg_str := message.ServerMsgResp{
|
||||||
|
ID: c.Auth + "." + c.MachineCode,
|
||||||
|
Result: true,
|
||||||
|
Data: data,
|
||||||
|
}
|
||||||
|
sendMsg_byte, err := json.Marshal(sendMsg_str)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("序列化%v失败:%v", sendMsg_str, err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
c.send(sendMsg_byte) // 返回成功消息
|
||||||
|
} else {
|
||||||
|
sendMsg_str := message.ServerMsgResp{
|
||||||
|
ID: c.Auth + "." + c.MachineCode,
|
||||||
|
Result: false,
|
||||||
|
Data: fmt.Errorf("错误的params数据结构:%v", mining_msg),
|
||||||
|
}
|
||||||
|
sendMsg_byte, err := json.Marshal(sendMsg_str)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("序列化%v失败:%v", sendMsg_str, err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
c.send(sendMsg_byte) // 返回失败消息
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Star() {
|
||||||
|
url := "xxxx"
|
||||||
|
client := newClient(url)
|
||||||
|
client.sendMachineCode()
|
||||||
|
go client.receiveMsg() // 开始接收服务端消息
|
||||||
|
}
|
||||||
8
internal/msg/config.go
Normal file
8
internal/msg/config.go
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
package message
|
||||||
|
|
||||||
|
type MiningConfig struct {
|
||||||
|
BzMinerPath string
|
||||||
|
LolMinerPath string
|
||||||
|
RigelPath string
|
||||||
|
ProxyEnabled bool
|
||||||
|
}
|
||||||
43
internal/msg/msg.go
Normal file
43
internal/msg/msg.go
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
package message
|
||||||
|
|
||||||
|
type GPU struct {
|
||||||
|
Brand string `json:"brand"` // 品牌
|
||||||
|
Model string `json:"model"` // 型号
|
||||||
|
Mem float64 `json:"mem,omitempty"` // 显存容量(MB)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerMsg struct {
|
||||||
|
ID string `json:"id"` // 身份码.主机码
|
||||||
|
Method string `json:"method"`
|
||||||
|
Params any `json:"params"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerMsgResp struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Result bool `json:"result"`
|
||||||
|
Data any `json:"data,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// server -> client, method:"mining.req"
|
||||||
|
type ConfigurationMiningMsg struct {
|
||||||
|
Coin string `json:"coin"` // 币种
|
||||||
|
Algo string `json:"algo"` // 算法
|
||||||
|
Pool string `json:"pool"` // 矿池
|
||||||
|
WalletMining bool `json:"wallet_mining"` // 是否支持钱包挖矿
|
||||||
|
PoolUrl string `json:"pool_url"` // 挖矿地址
|
||||||
|
WalletAddress string `json:"wallet_address"` // 收款钱包
|
||||||
|
PoolUser string `json:"pool_user,omitempty"` // 矿池挖矿账号,仅针对不支持钱包挖矿的矿池,如f2pool、m2pool等,支持钱包挖矿的矿池不需该字段
|
||||||
|
WorkerID string `json:"worker_id"` // 矿工号
|
||||||
|
EndTimestamp uint64 `json:"end_timestamp"` // 合约结束时间戳,使用时统一转换成UTC+0时区
|
||||||
|
}
|
||||||
|
|
||||||
|
// client -> server, method:"mining.resp"
|
||||||
|
type ConfigurationMiningResp struct {
|
||||||
|
Coin string `json:"coin"` // 币种
|
||||||
|
Algo string `json:"algo"` // 算法
|
||||||
|
Pool string `json:"pool"` // 矿池
|
||||||
|
PoolUrl string `json:"pool_url"` // 挖矿地址
|
||||||
|
WorkerID string `json:"worker_id"` // 矿工号
|
||||||
|
WalletAddress string `json:"wallet_address"` // 收款钱包
|
||||||
|
WatchUrl string `json:"watch_url"` // 挖矿信息页面,只有在StartMining字段为true的时候才有,false时该字段为空字符串
|
||||||
|
}
|
||||||
447
internal/src/linux/linux.go
Normal file
447
internal/src/linux/linux.go
Normal 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
|
||||||
|
}
|
||||||
63
internal/src/src.go
Normal file
63
internal/src/src.go
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
package src
|
||||||
|
|
||||||
|
import (
|
||||||
|
message "client/internal/msg"
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type OS interface {
|
||||||
|
GetGPUInfo() (map[int]message.GPU, error)
|
||||||
|
Mining(cfg message.ConfigurationMiningMsg) error
|
||||||
|
GetMACAddress() (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type SystemServer struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
systems map[string]OS
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSystemServer() *SystemServer {
|
||||||
|
return &SystemServer{
|
||||||
|
systems: make(map[string]OS),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SystemServer) ResiterSystem(name string, os OS) {
|
||||||
|
s.mu.Lock()
|
||||||
|
s.systems[name] = os
|
||||||
|
s.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SystemServer) GetGPUInfo(osName string) (map[int]message.GPU, error) {
|
||||||
|
if srv, ok := s.systems[osName]; ok {
|
||||||
|
gpus, err := srv.GetGPUInfo()
|
||||||
|
if err != nil {
|
||||||
|
return map[int]message.GPU{}, fmt.Errorf("获取操作系统(%s)GPU信息失败:%v", osName, err)
|
||||||
|
}
|
||||||
|
return gpus, nil
|
||||||
|
}
|
||||||
|
return map[int]message.GPU{}, fmt.Errorf("错误的操作系统:%s", osName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SystemServer) Mining(osName string, cfg message.ConfigurationMiningMsg) error {
|
||||||
|
if srv, ok := s.systems[osName]; ok {
|
||||||
|
err := srv.Mining(cfg)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("开始挖矿失败:%v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("错误的操作系统:%s", osName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SystemServer) GetMACAddress(osName string) (string, error) {
|
||||||
|
if srv, ok := s.systems[osName]; ok {
|
||||||
|
mac, err := srv.GetMACAddress()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("获取当前机器MAC码失败")
|
||||||
|
}
|
||||||
|
return mac, nil
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("错误的操作系统:%s", osName)
|
||||||
|
}
|
||||||
149
internal/src/windows/windows.go
Normal file
149
internal/src/windows/windows.go
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
package windows
|
||||||
|
|
||||||
|
import (
|
||||||
|
message "client/internal/msg"
|
||||||
|
"fmt"
|
||||||
|
"os/exec"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 获取 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取 AMD 显卡信息
|
||||||
|
func getAmdGPUInfo() (map[int]message.GPU, error) {
|
||||||
|
// 使用 wmic 列出所有显卡信息
|
||||||
|
cmd := exec.Command("wmic", "path", "Win32_VideoController", "get", "Name,AdapterRAM")
|
||||||
|
output, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("无法执行 wmic 命令: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析输出
|
||||||
|
info := strings.TrimSpace(string(output))
|
||||||
|
lines := strings.Split(info, "\n")
|
||||||
|
|
||||||
|
gpus := make(map[int]message.GPU)
|
||||||
|
gpuIndex := 0
|
||||||
|
|
||||||
|
for _, line := range lines[1:] { // 跳过标题行
|
||||||
|
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 len(gpus) == 0 {
|
||||||
|
return nil, fmt.Errorf("未找到显卡信息")
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
// 尝试获取 NVIDIA GPU 信息
|
||||||
|
gpus, err := getNvidiaGPUInfo()
|
||||||
|
if err == nil {
|
||||||
|
return gpus, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尝试获取 AMD GPU 信息
|
||||||
|
gpus, err = getAmdGPUInfo()
|
||||||
|
if err == nil {
|
||||||
|
return gpus, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("未找到任何显卡信息")
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
{
|
||||||
|
"coin-algo": {
|
||||||
|
"lolminer": {
|
||||||
|
"pool_name": "url"
|
||||||
|
},
|
||||||
|
"bzminer": {
|
||||||
|
"pool_name": "url"
|
||||||
|
},
|
||||||
|
"rigel": {
|
||||||
|
"pool_name": "url"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
89
internal/utils/utils.go
Normal file
89
internal/utils/utils.go
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetYMDMHS() string {
|
||||||
|
// 获取当前时间
|
||||||
|
currentTime := time.Now()
|
||||||
|
|
||||||
|
// 格式化时间为 "年-月-日 时:分:秒"
|
||||||
|
formattedTime := currentTime.Format("2006-01-02 15:04:05")
|
||||||
|
|
||||||
|
return formattedTime
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadFile(filePath string) (string, error) {
|
||||||
|
data, err := ioutil.ReadFile(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("读取身份信息失败:%v", err)
|
||||||
|
}
|
||||||
|
return string(data), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func FileExists(path, filename string) (bool, error) {
|
||||||
|
// 构造完整的文件路径
|
||||||
|
filePath := filepath.Join(path, filename)
|
||||||
|
|
||||||
|
// 使用 os.Stat 检查文件是否存在
|
||||||
|
_, err := os.Stat(filePath)
|
||||||
|
if err != nil {
|
||||||
|
// 如果错误是 "文件不存在",则返回 false
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
// 如果其他错误,返回错误信息
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有错误,文件存在
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查文件路径是否存在
|
||||||
|
func CheckFileExists(path string) bool {
|
||||||
|
_, err := os.Stat(path)
|
||||||
|
return !os.IsNotExist(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func WirteFile(filePath, content string) {
|
||||||
|
// // 要写入的文件路径
|
||||||
|
// filePath := "example.txt"
|
||||||
|
// // 要写入的内容
|
||||||
|
// content := "Hello, Go! This is a test."
|
||||||
|
|
||||||
|
// 创建/打开文件,覆盖写入
|
||||||
|
file, err := os.Create(filePath) // 这里会覆盖原有内容,如果文件不存在会创建新文件
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("文件创建失败:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer file.Close() // 确保文件在写入后关闭
|
||||||
|
|
||||||
|
// 写入内容
|
||||||
|
_, err = file.WriteString(content)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("写入文件失败:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("文件写入成功")
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenerateUUID() string {
|
||||||
|
// 生成一个新的 UUID
|
||||||
|
id := uuid.New()
|
||||||
|
return id.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// func CheckMiningMsg(msg message.ConfigurationMiningMsg) bool{
|
||||||
|
// coin, algo, pool := msg.Coin, msg.Algo, msg.Pool
|
||||||
|
|
||||||
|
// }
|
||||||
Reference in New Issue
Block a user