Files
xxxig/script/install.sh
T
wangdefa 99a17f1852
Build and Release / build-and-test (arm64) (push) Successful in 23s
Build and Release / build-and-test (amd64) (push) Successful in 34s
Build and Release / release (push) Skipped
完善安装脚本,增加对 1GB Huge Pages 的支持,优化服务管理与权限设置
2026-08-05 12:33:33 +08:00

535 lines
15 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
#
# XXXig 安装脚本 - Gitea 版本
# 从 Gitea Generic Package Registry 下载并安装 XXXig
#
set -e
declare -r BLUE="\033[0;34m"
declare -r RED="\033[0;31m"
declare -r GREEN="\033[0;32m"
declare -r YELLOW="\033[0;33m"
declare -r RESET="\033[0m"
# 基本配置
INSTALL_DIR="/etc/miner/xxxig"
CONFIG_FILE="$INSTALL_DIR/config.json"
LOG_FILE="$INSTALL_DIR/xxxig.log"
HUGEPAGES_HELPER="/usr/local/libexec/xxxig-enable-1gb-pages"
HUGEPAGES_SERVICE="/etc/systemd/system/xxxig-hugepages.service"
SYSTEMD_DROPIN_DIR="/etc/systemd/system/xxxig.service.d"
SYSTEMD_DROPIN="$SYSTEMD_DROPIN_DIR/hugepages.conf"
# Gitea 配置
GITEA_SERVER="gitea.bcde.io"
GITEA_OWNER="wangdefa"
GITEA_REPO="xxxig"
PACKAGE_NAME="xxxig"
# 默认参数
DEFAULT_VERSION="latest"
DEFAULT_THREADS=0
DEFAULT_POOL_ALGO="rx/0"
DEFAULT_POOL_TLS="false"
DEFAULT_POOL_KEEPALIVE="false"
DEFAULT_POOL_DAEMON="false"
DEFAULT_POOL_SUBMIT_TO_ORIGIN="false"
DEFAULT_POOL_NICEHASH="true"
DEFAULT_ONE_GB_PAGES="false"
DEFAULT_VERBOSE="true"
DEFAULT_CC_ENABLED="false"
DEFAULT_CC_TLS="false"
DEFAULT_KEEP_NAME="false"
# 辅助函数
log() { echo -e "${1}${2}${RESET}"; }
error() { log "$RED" "错误: $1"; exit 1; }
get_arch() {
case $(uname -m) in
x86_64) echo "amd64" ;;
aarch64) echo "arm64" ;;
*) error "不支持的架构: $(uname -m)" ;;
esac
}
get_os() {
grep -qi alpine /etc/os-release && echo "alpine" && return
grep -qiE 'debian|ubuntu' /etc/os-release && echo "ubuntu" && return
error "不支持的操作系统"
}
get_gitea_latest_version() {
local version
version=$(curl -s "https://${GITEA_SERVER}/api/v1/repos/${GITEA_OWNER}/${GITEA_REPO}/releases" | jq -r '.[0].tag_name')
[[ -z "$version" || "$version" == "null" ]] && error "获取版本失败"
echo "$version"
}
# 卸载旧版本
uninstall_xxxig() {
if [[ $os_type == "alpine" ]]; then
rc-service xxxig stop 2>/dev/null || true
rc-service xxxig-hugepages stop 2>/dev/null || true
rc-update del xxxig default 2>/dev/null || true
rc-update del xxxig-hugepages default 2>/dev/null || true
rm -rf "$INSTALL_DIR" /etc/init.d/xxxig /etc/init.d/xxxig-hugepages
rm -f "$HUGEPAGES_HELPER" "$HUGEPAGES_SERVICE"
else
systemctl stop xxxig xxxig-hugepages 2>/dev/null || true
systemctl disable xxxig xxxig-hugepages 2>/dev/null || true
rm -rf "$INSTALL_DIR" /etc/systemd/system/xxxig.service
rm -f "$SYSTEMD_DROPIN" "$HUGEPAGES_HELPER" "$HUGEPAGES_SERVICE"
rmdir "$SYSTEMD_DROPIN_DIR" 2>/dev/null || true
systemctl daemon-reload 2>/dev/null || true
fi
}
# 安装软件包
install_xxxig() {
local temp_dir=$(mktemp -d)
trap "rm -rf '$temp_dir'" EXIT
log "$BLUE" "下载 XXXig $version..."
curl -fsSL -o "$temp_dir/xxxig.tar.gz" "$download_url" || error "下载失败"
mkdir -p "$INSTALL_DIR"
tar -xzf "$temp_dir/xxxig.tar.gz" -C "$INSTALL_DIR" --strip-components=1 || error "解压失败"
chmod +x "$INSTALL_DIR/xxxigDaemon" "$INSTALL_DIR/xxxig"
}
create_xxxig_user() {
if [[ "$os_type" == "alpine" ]]; then
getent group xxxig >/dev/null || addgroup -S xxxig
getent passwd xxxig >/dev/null || \
adduser -S -D -H -h "$INSTALL_DIR" -s /sbin/nologin -G xxxig xxxig
else
getent group xxxig >/dev/null || addgroup --system xxxig
getent passwd xxxig >/dev/null || \
adduser --system --ingroup xxxig --no-create-home \
--home "$INSTALL_DIR" --shell /usr/sbin/nologin xxxig
fi
}
set_runtime_permissions() {
touch "$LOG_FILE"
chown -R root:root "$INSTALL_DIR"
chown xxxig:xxxig "$CONFIG_FILE" "$LOG_FILE"
chmod 640 "$CONFIG_FILE"
chmod 640 "$LOG_FILE"
chmod 755 "$INSTALL_DIR" "$INSTALL_DIR/xxxigDaemon" "$INSTALL_DIR/xxxig"
}
write_hugepages_helper() {
mkdir -p "$(dirname "$HUGEPAGES_HELPER")"
cat >"$HUGEPAGES_HELPER" <<'EOF'
#!/bin/sh
set -eu
sysctl -w vm.nr_hugepages="$(nproc)"
found=0
for node in /sys/devices/system/node/node*; do
file="$node/hugepages/hugepages-1048576kB/nr_hugepages"
[ -e "$file" ] || continue
printf '3\n' >"$file"
actual=$(cat "$file")
[ "$actual" -ge 3 ] || exit 1
found=1
done
[ "$found" -eq 1 ]
EOF
chmod 755 "$HUGEPAGES_HELPER"
}
write_hugepages_service() {
cat >"$HUGEPAGES_SERVICE" <<EOF
[Unit]
Description=Reserve Huge Pages for XXXig
Before=xxxig.service
ConditionPathExists=/sys/kernel/mm/hugepages/hugepages-1048576kB/nr_hugepages
[Service]
Type=oneshot
ExecStart=$HUGEPAGES_HELPER
[Install]
WantedBy=multi-user.target
EOF
}
configure_systemd_hugepages() {
[[ "$one_gb_pages" == "true" ]] || return 0
log "$BLUE" "配置 1GB Huge Pages 持久池..."
write_hugepages_helper
write_hugepages_service
mkdir -p "$SYSTEMD_DROPIN_DIR"
cat >"$SYSTEMD_DROPIN" <<'EOF'
[Unit]
Requires=xxxig-hugepages.service
After=xxxig-hugepages.service
EOF
}
configure_alpine_hugepages() {
[[ "$one_gb_pages" == "true" ]] || return 0
log "$BLUE" "配置 1GB Huge Pages 持久池..."
write_hugepages_helper
cat >/etc/init.d/xxxig-hugepages <<EOF
#!/sbin/openrc-run
description="Reserve Huge Pages for XXXig"
start() {
ebegin "Reserving Huge Pages for XXXig"
$HUGEPAGES_HELPER
eend \$?
}
EOF
chmod 755 /etc/init.d/xxxig-hugepages
rc-update add xxxig-hugepages default
}
# 配置 CPU 亲和性
configure_cpu_rx() {
local cpu_cores=$(nproc)
local rx_array="" step=1
[[ "$threads" -le $((cpu_cores / 2)) ]] && step=2
for ((i = 0; i < threads; i++)); do
[[ $i -gt 0 ]] && rx_array+=", "
rx_array+="$((i * step))"
done
local field="rx"
[[ "$pool_algo" == "panthera" ]] && field="panthera"
jq ".cpu.$field = [ $rx_array ]" "$CONFIG_FILE" >"$CONFIG_FILE.tmp" && mv "$CONFIG_FILE.tmp" "$CONFIG_FILE"
}
# 更新配置文件
append_pool_options() {
[[ "$one_gb_pages" == "true" ]] && jq_cmd+=' | .randomx."1gb-pages" = true'
[[ "$pool_keepalive" == "true" ]] && jq_cmd+=' | .pools[0].keepalive = true'
[[ -n "$pool_tls_fingerprint" ]] && \
jq_cmd+=" | .pools[0].\"tls-fingerprint\" = \"$pool_tls_fingerprint\""
[[ "$pool_daemon" == "true" ]] && jq_cmd+=' | .pools[0].daemon = true'
[[ "$pool_submit_to_origin" == "true" ]] && \
jq_cmd+=" | .pools[0].\"submit-to-origin\" = true | .pools[0].\"self-select\" = \"$pool_self_select\""
return 0
}
append_cc_options() {
if [[ "$cc_enabled" == "true" ]]; then
jq_cmd+=" | .\"cc-client\".enabled = true
| .\"cc-client\".servers[0].url = \"$cc_url\"
| .\"cc-client\".servers[0].\"access-token\" = \"$cc_token\"
| .\"cc-client\".servers[0].\"use-tls\" = $cc_tls
| .\"cc-client\".\"worker-id\" = \"$cc_work_id\""
else
jq_cmd+=' | ."cc-client".enabled = false'
fi
}
replace_config() {
[[ ! -f "$CONFIG_FILE" ]] && error "配置文件缺失"
local jq_cmd=".\"log-file\" = \"$LOG_FILE\"
| .\"donate-level\" = 0
| .\"donate-over-proxy\" = 0
| .pools[0].algo = \"$pool_algo\"
| .pools[0].url = \"$pool_address\"
| .pools[0].user = \"$wallet_address\"
| .pools[0].pass = \"$pool_password\"
| .pools[0].tls = $pool_tls
| .pools[0].nicehash = $pool_nicehash
| .\"verbose\" = $verbose"
append_pool_options
append_cc_options
jq "$jq_cmd" "$CONFIG_FILE" >"$CONFIG_FILE.tmp" && mv "$CONFIG_FILE.tmp" "$CONFIG_FILE"
configure_cpu_rx
}
# 创建并启动服务
create_alpine_service() {
local hugepages_dependency=""
[[ "$one_gb_pages" == "true" ]] && hugepages_dependency=" need xxxig-hugepages"
cat >/etc/init.d/xxxig <<EOF
#!/sbin/openrc-run
name="XXXig Miner"
description="XXXig Miner Service"
command="/etc/miner/xxxig/xxxigDaemon"
command_args="-c /etc/miner/xxxig/config.json"
command_user="xxxig:xxxig"
pidfile="/run/\${RC_SVCNAME}.pid"
command_background="yes"
rc_ulimit="-n 65535 -l unlimited"
depend() {
need net
$hugepages_dependency
after network
}
EOF
chmod 755 /etc/init.d/xxxig
rc-update add xxxig default
rc-service xxxig start
}
write_systemd_service() {
cat >/etc/systemd/system/xxxig.service <<EOF
[Unit]
Description=XXXig Miner
After=network.target
[Service]
Type=simple
User=xxxig
Group=xxxig
WorkingDirectory=$INSTALL_DIR
ExecStart=$INSTALL_DIR/xxxigDaemon -c $CONFIG_FILE
Restart=always
RestartSec=10
LimitNOFILE=65535
LimitMEMLOCK=infinity
AmbientCapabilities=CAP_IPC_LOCK
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=$INSTALL_DIR
[Install]
WantedBy=multi-user.target
EOF
}
start_systemd_service() {
systemctl daemon-reload
if [[ "$one_gb_pages" == "true" ]]; then
systemctl enable xxxig-hugepages.service
fi
systemctl enable --now xxxig.service
}
create_service() {
if [[ "$os_type" == "alpine" ]]; then
create_alpine_service
else
write_systemd_service
start_systemd_service
fi
}
show_usage() {
cat <<EOF
用法: $0 [选项]
选项:
-v, --version <版本> XXXig 版本 (默认: $DEFAULT_VERSION)
-t, --threads <数字> 线程数 (默认: 自动)
-a, --algo <算法> 矿池算法 (默认: $DEFAULT_POOL_ALGO)
-o, --pool <地址> 矿池地址 (必需)
-w, --wallet <地址> 钱包地址
-p, --password <密码> 矿池密码 (默认自动生成)
--1gb-pages 启用 1GB 大页
--tls 启用 TLS
--keepalive 启用 KeepAlive
--daemon 启用 SOLO 挖矿
--nicehash 启用 NiceHash 模式 (默认: 启用)
--submit-to-origin 提交到原始矿池
--self-select <地址> 自选矿池地址
--verbose 启用详细输出 (默认: 启用)
--cc 启用 CC
--cc-url <地址> CC 服务器地址
--cc-work-id <ID> CC 工作 ID
--cc-token <令牌> CC 访问令牌
--cc-tls 启用 CC TLS
--keep-name 保留已有矿池密码和 CC worker-id
-h, --help 显示帮助信息
$0 -o pool.example.com:3333 -w YOUR_WALLET_ADDRESS
EOF
exit 1
}
init_args() {
version=$DEFAULT_VERSION
threads=$DEFAULT_THREADS
pool_algo=$DEFAULT_POOL_ALGO
pool_tls=$DEFAULT_POOL_TLS
pool_keepalive=$DEFAULT_POOL_KEEPALIVE
pool_submit_to_origin=$DEFAULT_POOL_SUBMIT_TO_ORIGIN
pool_daemon=$DEFAULT_POOL_DAEMON
pool_nicehash=$DEFAULT_POOL_NICEHASH
pool_self_select=""
pool_address=""
wallet_address=""
pool_password=""
pool_tls_fingerprint=""
verbose=$DEFAULT_VERBOSE
cc_enabled=$DEFAULT_CC_ENABLED
cc_url=""
cc_work_id=""
cc_token=""
cc_tls=$DEFAULT_CC_TLS
one_gb_pages=$DEFAULT_ONE_GB_PAGES
keep_name=$DEFAULT_KEEP_NAME
}
read_args() {
while [[ $# -gt 0 ]]; do
case "$1" in
-v | --version) version="$2"; shift 2 ;;
-t | --threads) threads="$2"; shift 2 ;;
-a | --algo) pool_algo="$2"; shift 2 ;;
-o | --pool) pool_address="$2"; shift 2 ;;
-w | --wallet) wallet_address="$2"; shift 2 ;;
-p | --password) pool_password="$2"; shift 2 ;;
--1gb-pages) one_gb_pages="true"; shift ;;
--cc-url) cc_url="$2"; shift 2 ;;
--cc-work-id) cc_work_id="$2"; shift 2 ;;
--cc-token) cc_token="$2"; shift 2 ;;
--tls-fingerprint) pool_tls_fingerprint="$2"; shift 2 ;;
--tls) pool_tls="true"; shift ;;
--keepalive) pool_keepalive="true"; shift ;;
--daemon) pool_daemon="true"; shift ;;
--nicehash) pool_nicehash="true"; shift ;;
--submit-to-origin) pool_submit_to_origin="true"; shift ;;
--self-select) pool_self_select="$2"; shift 2 ;;
--verbose) verbose="true"; shift ;;
--cc) cc_enabled="true"; shift ;;
--cc-tls) cc_tls="true"; shift ;;
--keep-name | --preserve-name) keep_name="true"; shift ;;
-h | --help) show_usage ;;
*) error "无效选项: $1" ;;
esac
done
}
validate_args() {
[[ -z "$pool_address" ]] && error "矿池地址不能为空"
[[ ! "$threads" =~ ^[0-9]+$ ]] && error "无效线程数"
[[ "$pool_submit_to_origin" == "true" && -z "$pool_self_select" ]] && \
error "启用 submit_to_origin 必须设置 self_select"
return 0
}
resolve_defaults() {
local ip random_str
if [[ -z "$pool_password" ]]; then
ip=$(curl -4s --retry 3 --connect-timeout 10 ifconfig.me 2>/dev/null || echo "0.0.0.0")
random_str=$(tr -dc '0-9a-zA-Z' </dev/urandom | head -c 5)
pool_password=$(echo "$ip" | awk -F. '{print $3"_"$4}')"_$random_str"
fi
[[ -z "$cc_work_id" ]] && cc_work_id="$(get_arch)_$pool_password"
[[ -z "$wallet_address" ]] && wallet_address="empty_wallet_$(date +%s)"
if [[ "$threads" -le 0 ]]; then
threads=$(nproc)
elif [[ "$threads" -gt $(nproc) ]]; then
threads=$(nproc)
fi
return 0
}
load_preserved_name() {
local old_password old_work_id preserved="false"
[[ "$keep_name" == "true" && -f "$CONFIG_FILE" ]] || return 0
old_password=$(jq -r '.pools[0].pass // empty' "$CONFIG_FILE" 2>/dev/null || true)
old_work_id=$(jq -r '."cc-client"."worker-id" // empty' "$CONFIG_FILE" 2>/dev/null || true)
if [[ -z "$pool_password" && -n "$old_password" ]]; then
pool_password=$old_password
preserved="true"
fi
if [[ -z "$cc_work_id" && -n "$old_work_id" ]]; then
cc_work_id=$old_work_id
preserved="true"
fi
[[ "$preserved" == "true" ]] && log "$GREEN" "已保留原矿工名称"
return 0
}
parse_args() {
init_args
read_args "$@"
validate_args
}
install_dependencies() {
local deps=(curl jq file)
if command -v apt-get &>/dev/null; then
for pkg in "${deps[@]}"; do
command -v "$pkg" &>/dev/null || apt-get install -y "$pkg"
done
elif command -v apk &>/dev/null; then
for pkg in "${deps[@]}"; do
command -v "$pkg" &>/dev/null || apk add "$pkg"
done
fi
}
prepare_download() {
local pkg_version
os_type=$(get_os)
[[ "$version" == "latest" ]] && version=$(get_gitea_latest_version)
pkg_version="${version#v}"
# 产物统一为 musl 全静态(linux-static),不再区分 distro;os_type 仅用于下方服务管理(OpenRC/systemd)
download_url="https://${GITEA_SERVER}/api/packages/${GITEA_OWNER}/generic/${PACKAGE_NAME}/${pkg_version}/${PACKAGE_NAME}-$(get_arch)-linux-static-${pkg_version}.tar.gz"
}
show_config() {
log "$BLUE" "配置信息:"
echo " 版本: $version"
echo " 架构: $(get_arch)"
echo " 系统: $os_type"
echo " 线程: $threads"
echo " 算法: $pool_algo"
echo " 矿池: $pool_address"
echo " 钱包: $wallet_address"
}
install_runtime() {
uninstall_xxxig
install_xxxig
replace_config
create_xxxig_user
set_runtime_permissions
if [[ "$os_type" == "alpine" ]]; then
configure_alpine_hugepages
else
configure_systemd_hugepages
fi
create_service
}
show_management_commands() {
echo "管理命令:"
if [[ "$os_type" == "alpine" ]]; then
echo " rc-service xxxig status"
echo " tail -f $LOG_FILE"
echo " rc-service xxxig restart"
else
echo " systemctl status xxxig"
echo " journalctl -u xxxig -f"
echo " systemctl restart xxxig"
fi
}
main() {
parse_args "$@"
install_dependencies
load_preserved_name
resolve_defaults
prepare_download
show_config
install_runtime
show_management_commands
}
main "$@"