diff --git a/README.md b/README.md index f7e682d..5e7ab15 100644 --- a/README.md +++ b/README.md @@ -1,113 +1,180 @@ -# RustDesk-Server 一键安装脚本 +# RustDesk Server Tool -## 示例 -```sh -# RustDesk-Server x86 一键安装脚本 -# Rep -# You Server:ubuntu -服务状态: [运行中] +一个交互式的 RustDesk Server 安装脚本,现在同时支持: ->~~~~~~~~~~~~~~ rustdesk-server tool ~~~~~~~~~~~~< v: 1.3 +- Linux:使用官方发布的 Linux 预编译包安装 `hbbs` / `hbbr` +- macOS:由于官方发布页没有提供 macOS Server 二进制,本脚本会拉取官方源码并在本机编译,然后用 `launchd` 托管服务 -1: 安装 2: 卸载 +仓库地址: +[https://github.com/signyf/rustdesktool](https://github.com/signyf/rustdesktool) -3: 查看状态 4: 查看key +## 支持情况 -5: 启动服务 6: 停止服务 +### Linux -7: 升级脚本 +- 发行版:Ubuntu、Debian、CentOS、Rocky、AlmaLinux、Alpine +- 架构:`amd64`、`arm64` +- 服务管理:`systemd` -q: 退出 +### macOS -请输入命令号: +- 架构:Intel(`amd64`) / Apple Silicon(`arm64`) +- 服务管理:`launchd` +- 安装方式:从官方 `rustdesk/rustdesk-server` 源码编译 + +## macOS 说明 + +截至官方 `rustdesk-server` `1.1.15` 版本,发布页提供的是 Linux 资产,没有 macOS Server 预编译包。 + +因此这个脚本在 macOS 上会: + +1. 下载 `rustdesk/rustdesk-server` 对应版本源码 +2. 本机执行 `cargo build --release` +3. 安装生成的 `hbbs` / `hbbr` +4. 写入 `launchd` plist 到 `/Library/LaunchDaemons` + +### macOS 前置依赖 + +先安装这些依赖: + +```bash +xcode-select --install +brew install rust git ``` -## 安装&卸载 +说明: + +- 建议用普通用户运行脚本,脚本在需要写系统目录时会自动调用 `sudo` +- 首次编译 RustDesk Server 可能需要几分钟 -### 一键安装 -> root 用户 +## 使用方法 -```sh -wget -N https://raw.githubusercontent.com/sshpc/rustdesktool/main/rustdesktool.sh && chmod +x ./rustdesktool.sh && ./rustdesktool.sh +### 1. 克隆仓库 + +```bash +git clone https://github.com/signyf/rustdesktool.git +cd rustdesktool +git checkout codex/macos-support +chmod +x rustdesktool.sh ``` -> 再次执行 +### 2. 启动脚本 -```sh +```bash ./rustdesktool.sh ``` + +如果你希望直接以 root 方式运行 Linux 安装,也可以: + +```bash +sudo ./rustdesktool.sh +``` + +## 一键使用链接 + +当前可直接使用的分支是 `codex/macos-support`,可以直接运行: + +### 标准链接 + +```bash +curl -fsSL https://raw.githubusercontent.com/signyf/rustdesktool/codex/macos-support/rustdesktool.sh -o rustdesktool.sh && chmod +x ./rustdesktool.sh && ./rustdesktool.sh +``` + ### 国内加速链接 -```sh -wget -N https://gh.ddlc.top/https://raw.githubusercontent.com/sshpc/rustdesktool/main/rustdesktool.sh && chmod +x ./rustdesktool.sh && ./rustdesktool.sh +```bash +curl -fsSL https://gh.ddlc.top/https://raw.githubusercontent.com/signyf/rustdesktool/codex/macos-support/rustdesktool.sh -o rustdesktool.sh && chmod +x ./rustdesktool.sh && ./rustdesktool.sh ``` -```sh -wget -N https://gh-proxy.com/https://raw.githubusercontent.com/sshpc/rustdesktool/main/rustdesktool.sh && chmod +x ./rustdesktool.sh && ./rustdesktool.sh + +```bash +curl -fsSL https://gh-proxy.com/https://raw.githubusercontent.com/signyf/rustdesktool/codex/macos-support/rustdesktool.sh -o rustdesktool.sh && chmod +x ./rustdesktool.sh && ./rustdesktool.sh ``` -## 客户端下载 -官方地址 -https://rustdesk.com/download +说明: -支持 (Windows、Ubuntu、Mac、Android..) +- 这里用 `curl` 而不是 `wget`,因为 macOS 默认自带 `curl` +- 当前命令使用的是 `codex/macos-support` 分支 +- 如果你后面把这些修改合并进 `main`,只需要把链接里的 `codex/macos-support` 改成 `main` -## 客户端配置 +## 菜单功能 ->默认安装后使用的是官方的服务器,需要换成自己的 +- `安装` +- `卸载` +- `查看状态` +- `查看公钥` +- `启动服务` +- `停止服务` +- `更新脚本` - 设置 → 网络 -* ID服务器地址:<你的服务器IP> -* 中继服务器地址:<你的服务器IP> -* API服务器地址:留空 -* 秘钥:你的服务器公钥(安装后获得) +说明: +- 这个分支里 `更新脚本` 已禁用,避免把本地的 macOS 适配改回上游原版脚本 -## 其他信息 +## 安装目录 -### 默认安装目录 -/usr/local/rustdesk-sever +默认安装目录: -### 服务安装目录 +```text +/usr/local/rustdesk-server +``` -/usr/lib/systemd/system/RustDeskHbbr.service -/usr/lib/systemd/system/RustDeskHbbs.service +公钥位置: +```text +/usr/local/rustdesk-server/id_ed25519.pub +``` -### 秘钥位置 -默认安装目录下的 id_ed25519.pub +日志目录: ->自定义秘钥:安装后手动替换安装目录下的 id_ed25519.pub 和 id_ed25519 +```text +/usr/local/rustdesk-server/logs +``` -### 端口 +## 服务文件位置 ->若连接失败,请检查防火墙端口是否打开 +### Linux -hbbs 监听21115(tcp), 21116(tcp/udp), 21118(tcp) +```text +/usr/lib/systemd/system/RustDeskHbbs.service +/usr/lib/systemd/system/RustDeskHbbr.service +``` -hbbr 监听21117(tcp), 21119(tcp) +### macOS -* 21115 -> hbbs用作NAT类型测试 -* 21116/UDP -> hbbs用作ID注册与心跳服务 -* 21116/TCP -> hbbs用作TCP打洞与连接服务 -* 21117 -> hbbr中继服务 -* 21118、21119 -> 网页客户端 +```text +/Library/LaunchDaemons/com.rustdesk.hbbs.plist +/Library/LaunchDaemons/com.rustdesk.hbbr.plist +``` +## 客户端配置 -### 官方 rep: +RustDesk 客户端中设置: -https://github.com/rustdesk/rustdesk-server +- ID Server:你的服务器 IP 或域名 +- Relay Server:你的服务器 IP 或域名 +- API Server:留空 +- Key:服务端生成的公钥 -## Star History +## 端口 - - - - - Star History Chart - - +请确保这些端口已放行: +- `21115/tcp` +- `21116/tcp` +- `21116/udp` +- `21117/tcp` +- `21118/tcp` +- `21119/tcp` +作用说明: +- `21115`:NAT 测试 +- `21116/tcp`:TCP 打洞与连接 +- `21116/udp`:ID 注册与心跳 +- `21117`:Relay 服务 +- `21118` / `21119`:Web 相关端口 +## 官方项目 +- RustDesk Server: [https://github.com/rustdesk/rustdesk-server](https://github.com/rustdesk/rustdesk-server) +- RustDesk Client: [https://rustdesk.com/download](https://rustdesk.com/download) diff --git a/rustdesktool.sh b/rustdesktool.sh index 30ec825..65045db 100755 --- a/rustdesktool.sh +++ b/rustdesktool.sh @@ -1,333 +1,363 @@ -#!/bin/bash -export LANG=en_US.UTF-8 - - -#安装目录 -installdirectory='/usr/local/rustdesk-server' -#官方版本号 -rustdeskserverversion='1.1.15' -# 配置文件下载代理主机列表(github加速) -proxyhost=( +#!/usr/bin/env bash + +set -u + +export LANG="${LANG:-en_US.UTF-8}" + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +RUSTDESK_SERVER_VERSION="${RUSTDESK_SERVER_VERSION:-1.1.15}" +INSTALL_DIRECTORY="${INSTALL_DIRECTORY:-/usr/local/rustdesk-server}" +MACOS_BUILD_DIRECTORY="${MACOS_BUILD_DIRECTORY:-${TMPDIR:-/tmp}/rustdesktool-build}" +LINUX_SERVICE_DIRECTORY="${LINUX_SERVICE_DIRECTORY:-/usr/lib/systemd/system}" +MACOS_SERVICE_DIRECTORY="${MACOS_SERVICE_DIRECTORY:-/Library/LaunchDaemons}" +HBBS_SERVICE_NAME="RustDeskHbbs" +HBBR_SERVICE_NAME="RustDeskHbbr" +HBBS_LAUNCHD_LABEL="com.rustdesk.hbbs" +HBBR_LAUNCHD_LABEL="com.rustdesk.hbbr" +HBBS_LAUNCHD_PLIST="${MACOS_SERVICE_DIRECTORY}/${HBBS_LAUNCHD_LABEL}.plist" +HBBR_LAUNCHD_PLIST="${MACOS_SERVICE_DIRECTORY}/${HBBR_LAUNCHD_LABEL}.plist" +LOG_DIRECTORY="${INSTALL_DIRECTORY}/logs" +TIMEOUT_SECONDS=10 +RELEASE="unknown" +ARCH_LABEL="unknown" +DOWNLOAD_ARCHIVE_NAME="" +DOWNLOAD_EXTRACTED_DIRECTORY="" +INSTALL_TYPE="" +REMOVE_TYPE="" +UPDATE_TYPE="" +SUDO="" +PLATFORM_SUPPORTS_NATIVE_BINARIES="false" + +PROXY_HOSTS=( "https://gh.ddlc.top" "https://gh-proxy.com" "https://edgeone.gh-proxy.com" "https://cdn.gh-proxy.com" "https://hk.gh-proxy.com" ) -#下载超时时间 -timeout=3 -installType='' -removeType='' -upgrade='' -release='linux' +color_red() { + printf '\033[0;31m%b\033[0m\n' "$1" +} -#字体颜色定义 -_red() { - printf '\033[0;31;31m%b\033[0m' "$1" - echo +color_green() { + printf '\033[0;32m%b\033[0m\n' "$1" } -_green() { - printf '\033[0;31;32m%b\033[0m' "$1" - echo + +color_yellow() { + printf '\033[0;33m%b\033[0m\n' "$1" } -_yellow() { - printf '\033[0;31;33m%b\033[0m' "$1" - echo + +color_blue() { + printf '\033[0;36m%b\033[0m\n' "$1" } -_blue() { - printf '\033[0;31;36m%b\033[0m' "$1" + +wait_input() { echo -} -waitinput() { + read -r -n1 -p "按任意键继续...(Ctrl+C 退出)" echo - read -n1 -r -p "按任意键继续...(退出 Ctrl+C)" } -# 加载动画 + loading() { - local pids=("$@") + local pid="$1" local delay=0.1 - local spinstr='|/-\' - tput civis # 隐藏光标 - - while :; do - local all_done=true - for pid in "${pids[@]}"; do - if kill -0 "$pid" 2>/dev/null; then - all_done=false - local temp=${spinstr#?} - printf "\r\033[0;31;36m[ %c ] loading ...\033[0m" "$spinstr" - local spinstr=$temp${spinstr%"$temp"} - sleep $delay - fi - done - [[ $all_done == true ]] && break + local spin='|/-\' + + if command -v tput >/dev/null 2>&1; then + tput civis 2>/dev/null || true + fi + + while kill -0 "$pid" 2>/dev/null; do + local first_char="${spin%${spin#?}}" + printf "\r\033[0;36m[ %s ] 处理中...\033[0m" "$first_char" + spin="${spin#?}${first_char}" + sleep "$delay" done - - tput cnorm # 恢复光标 - printf "\r\033[K" # 清除行 + + if command -v tput >/dev/null 2>&1; then + tput cnorm 2>/dev/null || true + fi + + printf "\r\033[K" +} + +run_privileged() { + if [[ -n "$SUDO" ]]; then + "$SUDO" "$@" + else + "$@" + fi +} + +require_command() { + local command_name="$1" + local hint="$2" + if ! command -v "$command_name" >/dev/null 2>&1; then + color_red "缺少依赖:${command_name}" + color_yellow "$hint" + exit 1 + fi } -# 通用下载函数:从镜像列表中依次尝试下载文件 download_file() { local url="$1" - local path="$2" - local output="" - local dest="" - if [[ -n "$path" ]]; then - dest="$path" - else - dest="." - fi - for base in "${proxyhost[@]}"; do - output="${dest}/$(basename "$url")" - ( - wget -q --timeout="$timeout" "${base}/$url" -O "$output" > /dev/null 2>&1 - ) & - local pid=$! - loading $pid - wait $pid - if [[ -s "$output" ]]; then - return 0 - fi + local output="$2" + local candidates=("$url") + local candidate="" + + for base in "${PROXY_HOSTS[@]}"; do + candidates+=("${base}/${url}") done - return 1 -} + rm -f "$output" -#菜单渲染 -menu() { - printf "\033[H\033[2J" - _green '# RustDesk-Server x86 一键安装脚本' - _green '# Rep ' - _blue '# You Server:'${release} - _blue "服务状态: [$(check_rustdesk_status)]" - echo - _blue ">~~~~~~~~~~~~~~ rustdesk-server tool ~~~~~~~~~~~~< v: $rustdeskserverversion" - echo - options=("$@") - num_options=${#options[@]} - # 计算数组中的字符最大长度 - max_len=0 - for ((i = 0; i < num_options; i++)); do - # 获取当前字符串的长度 - str_len=${#options[i]} - - # 更新最大长度 - if ((str_len > max_len)); then - max_len=$str_len + for candidate in "${candidates[@]}"; do + if command -v curl >/dev/null 2>&1; then + if curl -fsL --connect-timeout "$TIMEOUT_SECONDS" "$candidate" -o "$output" >/dev/null 2>&1; then + return 0 + fi + elif command -v wget >/dev/null 2>&1; then + if wget -q --timeout="$TIMEOUT_SECONDS" "$candidate" -O "$output" >/dev/null 2>&1; then + return 0 + fi + else + color_red "下载文件需要 curl 或 wget。" + return 1 fi done - # 渲染菜单 - for ((i = 0; i < num_options; i += 4)); do - printf "%s%*s " "$((i / 2 + 1)): ${options[i]}" "$((max_len - ${#options[i]}))" - if [[ "${options[i + 2]}" != "" ]]; then printf "$((i / 2 + 2)): ${options[i + 2]}"; fi - echo - echo - done - printf '\033[0;31;31m%b\033[0m' "q: 退出 " - echo - echo - # 获取用户输入 - read -ep "请输入命令号: " number - if [[ $number -ge 1 && $number -le $((num_options / 2)) ]]; then - #找到函数名索引 - action_index=$((2 * (number - 1) + 1)) - #函数名赋值 - parentfun=${options[action_index]} - #函数执行 - ${options[action_index]} - waitinput - main - elif [[ $number == 'q' ]]; then - echo - exit - else - echo - _red '输入有误 回车返回首页' - waitinput - main - fi + + rm -f "$output" + return 1 } -#检查系统 -checkSystem() { - - if grep -qi "centos\|red hat" /etc/os-release; then - release="centos" - installType='yum -y install' - removeType='yum -y remove' - upgrade="yum update -y --skip-broken" - elif grep -qi "ubuntu" /etc/os-release; then - release="ubuntu" - installType='apt -y install' - removeType='apt -y autoremove' - upgrade="apt update" - elif grep -qi "debian" /etc/os-release; then - release="debian" - installType='apt -y install' - removeType='apt -y autoremove' - upgrade="apt update" - elif grep -qi "alpine" /etc/os-release; then - release="alpine" - installType='apk add' - upgrade="apk update" - removeType='apk del' - else - _red "不支持此系统" - _red "$(cat /etc/issue)" - _red "$(cat /proc/version)" +ensure_linux_package_tools() { + if [[ "$RELEASE" == "linux" && -z "$INSTALL_TYPE" ]]; then + color_red "暂不支持当前 Linux 发行版。" exit 1 fi } +ensure_linux_dependency() { + local binary_name="$1" + local package_name="$2" + if command -v "$binary_name" >/dev/null 2>&1; then + return 0 + fi -#脚本升级 -updateself() { + ensure_linux_package_tools + color_yellow "正在安装缺少的依赖:${package_name}" + run_privileged bash -lc "${INSTALL_TYPE} ${package_name}" || { + color_red "${package_name} 安装失败。" + exit 1 + } +} - _blue '下载最新版脚本' - if ! download_file "https://raw.githubusercontent.com/sshpc/rustdesktool/main/rustdesktool.sh"; then - _red "rustdesktool.sh 下载失败!" - return 1 +ensure_macos_prerequisites() { + require_command "git" "请先安装 Xcode Command Line Tools:xcode-select --install" + require_command "cargo" "请先安装 Rust,例如:brew install rust" + + if ! xcode-select -p >/dev/null 2>&1; then + color_red "尚未安装 Xcode Command Line Tools。" + color_yellow "请先执行:xcode-select --install" + exit 1 fi - chmod +x ./rustdesktool.sh - _green "脚本更新成功" - exec bash ./rustdesktool.sh } -check_rustdesk_status() { - local hbbs_service="/usr/lib/systemd/system/RustDeskHbbs.service" - local hbbr_service="/usr/lib/systemd/system/RustDeskHbbr.service" +detect_architecture() { + local machine_arch + machine_arch="$(uname -m)" + + case "$machine_arch" in + x86_64|amd64) + ARCH_LABEL="amd64" + DOWNLOAD_ARCHIVE_NAME="rustdesk-server-linux-amd64.zip" + DOWNLOAD_EXTRACTED_DIRECTORY="amd64" + ;; + arm64|aarch64) + ARCH_LABEL="arm64" + DOWNLOAD_ARCHIVE_NAME="rustdesk-server-linux-arm64v8.zip" + DOWNLOAD_EXTRACTED_DIRECTORY="arm64v8" + ;; + *) + ARCH_LABEL="$machine_arch" + DOWNLOAD_ARCHIVE_NAME="" + DOWNLOAD_EXTRACTED_DIRECTORY="" + ;; + esac +} - # 判断是否安装 - if [[ ! -f "$hbbs_service" || ! -f "$hbbr_service" ]]; then - echo "未安装" - return +check_system() { + local uname_out + uname_out="$(uname -s)" + + case "$uname_out" in + Linux) + RELEASE="linux" + PLATFORM_SUPPORTS_NATIVE_BINARIES="true" + if [[ -f /etc/os-release ]]; then + if grep -qi "centos\|red hat\|rocky\|almalinux" /etc/os-release; then + INSTALL_TYPE='yum -y install' + REMOVE_TYPE='yum -y remove' + UPDATE_TYPE='yum -y update' + elif grep -qi "ubuntu\|debian" /etc/os-release; then + INSTALL_TYPE='apt -y install' + REMOVE_TYPE='apt -y autoremove' + UPDATE_TYPE='apt update' + elif grep -qi "alpine" /etc/os-release; then + INSTALL_TYPE='apk add' + REMOVE_TYPE='apk del' + UPDATE_TYPE='apk update' + fi + fi + ;; + Darwin) + RELEASE="macos" + PLATFORM_SUPPORTS_NATIVE_BINARIES="false" + ;; + *) + color_red "暂不支持当前操作系统:${uname_out}" + exit 1 + ;; + esac + + detect_architecture + + if [[ "$RELEASE" == "linux" && -z "$INSTALL_TYPE" ]]; then + color_red "这个脚本暂不支持当前 Linux 发行版。" + exit 1 fi - # 判断是否运行 - if systemctl is-active --quiet RustDeskHbbs && systemctl is-active --quiet RustDeskHbbr; then - echo "运行中" - else - echo "未运行" + if [[ "$RELEASE" == "macos" && "$ARCH_LABEL" != "amd64" && "$ARCH_LABEL" != "arm64" ]]; then + color_red "macOS 目前仅支持 amd64 和 arm64。" + exit 1 fi -} - + if [[ "$RELEASE" == "linux" && "$ARCH_LABEL" != "amd64" && "$ARCH_LABEL" != "arm64" ]]; then + color_red "Linux 原生安装目前仅支持 amd64 和 arm64。" + exit 1 + fi -#查看状态 -viewstatus() { - echo - _blue 'RustDeskHbbs status:' - systemctl status RustDeskHbbs | awk '/Active/' - _blue 'RustDeskHbbr status:' - systemctl status RustDeskHbbr | awk '/Active/' - _blue 'net status:' - netstat -tuln | grep -E ":(21115|21116|21117|21118|21119)\b" + if [[ "$(id -u)" -eq 0 ]]; then + SUDO="" + elif command -v sudo >/dev/null 2>&1; then + SUDO="sudo" + else + color_red "这个脚本需要 root 权限或 sudo。" + exit 1 + fi } -startservice() { - _blue "启动服务" - ( - systemctl start RustDeskHbbs > /dev/null 2>&1 - systemctl start RustDeskHbbr > /dev/null 2>&1 - ) & - local pid=$! - loading $pid - wait $pid - _green "服务已启动" +linux_hbbs_service_file() { + printf '%s/%s.service' "$LINUX_SERVICE_DIRECTORY" "$HBBS_SERVICE_NAME" } -stopservice() { - _blue "停止服务" - ( - systemctl stop RustDeskHbbs > /dev/null 2>&1 - systemctl stop RustDeskHbbr > /dev/null 2>&1 - ) & - local pid=$! - loading $pid - wait $pid - _yellow "服务已停止" +linux_hbbr_service_file() { + printf '%s/%s.service' "$LINUX_SERVICE_DIRECTORY" "$HBBR_SERVICE_NAME" } -viewkey() { - echo - _blue '公钥:' - cat $installdirectory/id_ed25519.pub - echo - echo +linux_service_installed() { + [[ -f "$(linux_hbbs_service_file)" && -f "$(linux_hbbr_service_file)" ]] } -#卸载 -uninstall() { - read -rp "确认卸载?(y/N): " c - [[ $c == y ]] || return - stopservice - - systemctl disable RustDeskHbbs - systemctl disable RustDeskHbbr - - rm -rf $installdirectory - rm -rf /usr/lib/systemd/system/RustDeskHbbs.service - rm -rf /usr/lib/systemd/system/RustDeskHbbr.service +macos_service_installed() { + [[ -f "$HBBS_LAUNCHD_PLIST" && -f "$HBBR_LAUNCHD_PLIST" ]] +} - _blue '已卸载' - echo +linux_services_running() { + systemctl is-active --quiet "$HBBS_SERVICE_NAME" && systemctl is-active --quiet "$HBBR_SERVICE_NAME" } -#安装 -install() { +macos_services_running() { + pgrep -f "${INSTALL_DIRECTORY}/hbbs" >/dev/null 2>&1 && pgrep -f "${INSTALL_DIRECTORY}/hbbr" >/dev/null 2>&1 +} - #检查是否已安装 - if [ -f "/usr/lib/systemd/system/RustDeskHbbr.service" ]; then - _yellow '检测到文件存在 卸载旧版...' - uninstall +check_rustdesk_status() { + if [[ "$RELEASE" == "linux" ]]; then + if ! linux_service_installed; then + echo "未安装" + return + fi + if linux_services_running; then + echo "运行中" + else + echo "已停止" + fi + return fi - _blue "开始安装" - mkdir -p $installdirectory - - # 检查并安装 wget - if ! command -v wget >/dev/null 2>&1; then - _yellow "未检测到 wget,正在安装..." - ${installType} wget || { _red "wget 安装失败"; exit 1; } + if ! macos_service_installed; then + echo "未安装" + return fi - # 检查并安装 unzip - if ! command -v unzip >/dev/null 2>&1; then - _yellow "未检测到 unzip,正在安装..." - ${installType} unzip || { _red "unzip 安装失败"; exit 1; } + if macos_services_running; then + echo "运行中" + else + echo "已停止" fi +} - _blue "下载安装文件" - if ! download_file "https://github.com/rustdesk/rustdesk-server/releases/download/$rustdeskserverversion/rustdesk-server-linux-amd64.zip" "$installdirectory"; then - _red "rustdesk-server-linux-amd64.zip下载失败!" - exit 1 - fi +menu() { + local options=("$@") + local num_options="${#options[@]}" + local max_len=0 + local i=0 + local label="" + local number="" + local action_index=0 - _blue "解压文件" - ( - unzip $installdirectory/rustdesk-server-linux-amd64.zip -d $installdirectory > /dev/null 2>&1 - ) & - local pid=$! - loading $pid - wait $pid + printf "\033[H\033[2J" + color_green "# RustDesk Server 一键安装脚本" + color_green "# 项目地址 " + color_blue "# 平台:$(if [[ "$RELEASE" == "linux" ]]; then echo "Linux"; else echo "macOS"; fi) (${ARCH_LABEL})" + color_blue "# 安装方式:$(if [[ "$RELEASE" == "linux" ]]; then echo "官方 Linux 二进制"; else echo "macOS 本地源码编译"; fi)" + color_blue "# 服务状态:[$(check_rustdesk_status)]" + echo + color_blue ">~~~~~~~~~~~~~~ rustdesk-server 工具 ~~~~~~~~~~~~< 版本:${RUSTDESK_SERVER_VERSION}" + echo + + for ((i = 0; i < num_options; i += 2)); do + label="${options[i]}" + if (( ${#label} > max_len )); then + max_len="${#label}" + fi + done - mv $installdirectory/amd64/* $installdirectory/ + for ((i = 0; i < num_options; i += 4)); do + printf "%s%*s " "$((i / 2 + 1)): ${options[i]}" "$((max_len - ${#options[i]}))" "" + if [[ -n "${options[i + 2]:-}" ]]; then + printf "%s" "$((i / 2 + 2)): ${options[i + 2]}" + fi + echo + echo + done - rm $installdirectory/rustdesk-server-linux-amd64.zip - rm -r $installdirectory/amd64 + color_red "q: 退出" + echo + read -r -p "请输入操作编号: " number - chmod +x $installdirectory/hbbr - chmod +x $installdirectory/hbbs + if [[ "$number" == "q" ]]; then + exit 0 + fi - if [ ! -f "/usr/lib/systemd/system/RustDeskHbbr.service" ]; then - #文件不存在 - touch /usr/lib/systemd/system/RustDeskHbbr.service + if [[ "$number" =~ ^[0-9]+$ ]] && (( number >= 1 && number <= num_options / 2 )); then + action_index=$((2 * (number - 1) + 1)) + "${options[action_index]}" + wait_input + main + return fi - cat </usr/lib/systemd/system/RustDeskHbbr.service + color_red "输入有误,请重新选择。" + wait_input + main +} + +write_linux_service_files() { + run_privileged mkdir -p "$LINUX_SERVICE_DIRECTORY" + + run_privileged tee "$(linux_hbbr_service_file)" >/dev/null </usr/lib/systemd/system/RustDeskHbbs.service + run_privileged tee "$(linux_hbbs_service_file)" >/dev/null </dev/null < + + + + Label + ${HBBR_LAUNCHD_LABEL} + ProgramArguments + + ${INSTALL_DIRECTORY}/hbbr + + WorkingDirectory + ${INSTALL_DIRECTORY} + RunAtLoad + + KeepAlive + + StandardOutPath + ${LOG_DIRECTORY}/hbbr.log + StandardErrorPath + ${LOG_DIRECTORY}/hbbr.err.log + + +EOF + + run_privileged tee "$HBBS_LAUNCHD_PLIST" >/dev/null < + + + + Label + ${HBBS_LAUNCHD_LABEL} + ProgramArguments + + ${INSTALL_DIRECTORY}/hbbs + + WorkingDirectory + ${INSTALL_DIRECTORY} + RunAtLoad + + KeepAlive + + StandardOutPath + ${LOG_DIRECTORY}/hbbs.log + StandardErrorPath + ${LOG_DIRECTORY}/hbbs.err.log + + +EOF + + run_privileged chmod 644 "$HBBS_LAUNCHD_PLIST" "$HBBR_LAUNCHD_PLIST" +} + +launchd_bootout_if_loaded() { + local label="$1" + if run_privileged launchctl print "system/${label}" >/dev/null 2>&1; then + run_privileged launchctl bootout "system/${label}" >/dev/null 2>&1 || true + fi +} + +start_linux_services() { + color_blue "正在启动 RustDesk 服务..." + run_privileged systemctl start "$HBBS_SERVICE_NAME" + run_privileged systemctl start "$HBBR_SERVICE_NAME" + color_green "RustDesk 服务已启动。" +} + +start_macos_services() { + color_blue "正在启动 launchd 服务..." + launchd_bootout_if_loaded "$HBBR_LAUNCHD_LABEL" + launchd_bootout_if_loaded "$HBBS_LAUNCHD_LABEL" + run_privileged launchctl bootstrap system "$HBBR_LAUNCHD_PLIST" + run_privileged launchctl bootstrap system "$HBBS_LAUNCHD_PLIST" + run_privileged launchctl enable "system/${HBBR_LAUNCHD_LABEL}" >/dev/null 2>&1 || true + run_privileged launchctl enable "system/${HBBS_LAUNCHD_LABEL}" >/dev/null 2>&1 || true + run_privileged launchctl kickstart -k "system/${HBBR_LAUNCHD_LABEL}" >/dev/null 2>&1 || true + run_privileged launchctl kickstart -k "system/${HBBS_LAUNCHD_LABEL}" >/dev/null 2>&1 || true + color_green "launchd 服务已启动。" +} + +stop_linux_services() { + color_blue "正在停止 RustDesk 服务..." + run_privileged systemctl stop "$HBBS_SERVICE_NAME" >/dev/null 2>&1 || true + run_privileged systemctl stop "$HBBR_SERVICE_NAME" >/dev/null 2>&1 || true + color_yellow "RustDesk 服务已停止。" +} + +stop_macos_services() { + color_blue "正在停止 launchd 服务..." + launchd_bootout_if_loaded "$HBBS_LAUNCHD_LABEL" + launchd_bootout_if_loaded "$HBBR_LAUNCHD_LABEL" + color_yellow "launchd 服务已停止。" +} + +start_service() { + if [[ "$RELEASE" == "linux" ]]; then + start_linux_services + else + start_macos_services + fi +} + +stop_service() { + if [[ "$RELEASE" == "linux" ]]; then + stop_linux_services + else + stop_macos_services + fi +} + +view_status() { echo - #启动服务 - startservice + if [[ "$RELEASE" == "linux" ]]; then + color_blue "RustDeskHbbs 状态:" + systemctl status "$HBBS_SERVICE_NAME" --no-pager 2>/dev/null | awk '/Active:|Loaded:/' + color_blue "RustDeskHbbr 状态:" + systemctl status "$HBBR_SERVICE_NAME" --no-pager 2>/dev/null | awk '/Active:|Loaded:/' + color_blue "监听端口:" + if command -v ss >/dev/null 2>&1; then + ss -lntu | grep -E ':(21115|21116|21117|21118|21119)\b' || true + else + netstat -tuln 2>/dev/null | grep -E ':(21115|21116|21117|21118|21119)\b' || true + fi + return + fi + + color_blue "launchd 服务:" + if run_privileged launchctl print "system/${HBBS_LAUNCHD_LABEL}" >/dev/null 2>&1; then + run_privileged launchctl print "system/${HBBS_LAUNCHD_LABEL}" 2>/dev/null | awk -F'= ' '/state =/ {print "hbbs 状态: " $2; exit}' + else + echo "hbbs 状态: 未加载" + fi + + if run_privileged launchctl print "system/${HBBR_LAUNCHD_LABEL}" >/dev/null 2>&1; then + run_privileged launchctl print "system/${HBBR_LAUNCHD_LABEL}" 2>/dev/null | awk -F'= ' '/state =/ {print "hbbr 状态: " $2; exit}' + else + echo "hbbr 状态: 未加载" + fi + + color_blue "运行中的进程:" + pgrep -fal "${INSTALL_DIRECTORY}/hbbs" || true + pgrep -fal "${INSTALL_DIRECTORY}/hbbr" || true + + color_blue "监听端口:" + lsof -nP -iTCP -sTCP:LISTEN | grep -E ':(21115|21116|21117|21118|21119)' || true + lsof -nP -iUDP | grep -E ':(21115|21116|21117|21118|21119)' || true +} - #printf "\033[H\033[2J" +view_key() { + local key_file="${INSTALL_DIRECTORY}/id_ed25519.pub" echo - _green '安装完成' - viewkey + if [[ -f "$key_file" ]]; then + color_blue "公钥:" + cat "$key_file" + else + color_yellow "暂未找到公钥文件。" + color_yellow "请先启动服务,等待几秒后再试。" + fi + echo +} + +print_public_ip() { + local public_ip="" + if command -v curl >/dev/null 2>&1; then + public_ip="$(curl -fsL --connect-timeout "$TIMEOUT_SECONDS" https://ipinfo.io/ip 2>/dev/null || true)" + elif command -v wget >/dev/null 2>&1; then + public_ip="$(wget -q -T "$TIMEOUT_SECONDS" -O- https://ipinfo.io/ip 2>/dev/null || true)" + fi + + if [[ -n "$public_ip" ]]; then + color_green "公网 IP:" + echo "$public_ip" + echo + fi +} + +install_linux() { + local archive_path="${TMPDIR:-/tmp}/${DOWNLOAD_ARCHIVE_NAME}.$$" + local download_url="https://github.com/rustdesk/rustdesk-server/releases/download/${RUSTDESK_SERVER_VERSION}/${DOWNLOAD_ARCHIVE_NAME}" + + ensure_linux_dependency "unzip" "unzip" + + if linux_service_installed; then + color_yellow "检测到已存在的 Linux 服务,正在重新安装..." + perform_uninstall "force" + fi + + color_blue "正在准备安装目录..." + run_privileged mkdir -p "$INSTALL_DIRECTORY" + run_privileged mkdir -p "$LOG_DIRECTORY" + + color_blue "正在下载官方 Linux 版 RustDesk Server..." + if ! download_file "$download_url" "$archive_path"; then + color_red "${DOWNLOAD_ARCHIVE_NAME} 下载失败。" + exit 1 + fi + + color_blue "正在解压安装包..." + run_privileged unzip -oq "$archive_path" -d "$INSTALL_DIRECTORY" + run_privileged install -m 755 "${INSTALL_DIRECTORY}/${DOWNLOAD_EXTRACTED_DIRECTORY}/hbbs" "${INSTALL_DIRECTORY}/hbbs" + run_privileged install -m 755 "${INSTALL_DIRECTORY}/${DOWNLOAD_EXTRACTED_DIRECTORY}/hbbr" "${INSTALL_DIRECTORY}/hbbr" + run_privileged rm -rf "${INSTALL_DIRECTORY}/${DOWNLOAD_EXTRACTED_DIRECTORY}" + rm -f "$archive_path" + + color_blue "正在写入 systemd 服务文件..." + write_linux_service_files + + start_linux_services + sleep 2 - local ip="$(wget -q -T10 -O- ipinfo.io/ip)" - _green '公网 IP:' - echo $ip echo - _yellow "请手动放行防火墙 TCP & UDP端口 21115-21119" + color_green "Linux 安装完成。" + view_key + print_public_ip + color_yellow "请在防火墙中放行 TCP/UDP 端口 21115-21119。" +} + +install_macos() { + local source_archive="${MACOS_BUILD_DIRECTORY}/rustdesk-server-${RUSTDESK_SERVER_VERSION}.tar.gz" + local source_url="https://github.com/rustdesk/rustdesk-server/archive/refs/tags/${RUSTDESK_SERVER_VERSION}.tar.gz" + local extracted_source_dir="${MACOS_BUILD_DIRECTORY}/rustdesk-server-${RUSTDESK_SERVER_VERSION}" + local macos_source_directory="${MACOS_BUILD_DIRECTORY}/rustdesk-server" + + ensure_macos_prerequisites + + if macos_service_installed; then + color_yellow "检测到已存在的 macOS 服务,正在重新安装..." + perform_uninstall "force" + fi + + color_blue "正在准备安装目录..." + run_privileged mkdir -p "$INSTALL_DIRECTORY" + run_privileged mkdir -p "$LOG_DIRECTORY" + mkdir -p "$MACOS_BUILD_DIRECTORY" + + color_blue "正在下载官方 RustDesk Server 源码包..." + if ! download_file "$source_url" "$source_archive"; then + color_red "RustDesk Server ${RUSTDESK_SERVER_VERSION} 源码下载失败。" + exit 1 + fi + + rm -rf "$extracted_source_dir" "$macos_source_directory" + color_blue "正在解压源码包..." + tar -xzf "$source_archive" -C "$MACOS_BUILD_DIRECTORY" + mv "$extracted_source_dir" "$macos_source_directory" + rm -f "$source_archive" + + color_blue "正在为 macOS 编译 hbbs 和 hbbr..." + ( + cd "$macos_source_directory" || exit 1 + cargo build --release >/dev/null 2>&1 + ) & + local build_pid=$! + loading "$build_pid" + if ! wait "$build_pid"; then + color_red "cargo 编译失败。" + color_yellow "请确认 Rust 工具链和 Xcode Command Line Tools 已安装。" + exit 1 + fi + + run_privileged install -m 755 "${macos_source_directory}/target/release/hbbs" "${INSTALL_DIRECTORY}/hbbs" + run_privileged install -m 755 "${macos_source_directory}/target/release/hbbr" "${INSTALL_DIRECTORY}/hbbr" + + color_blue "正在写入 launchd 配置文件..." + write_macos_launchd_files + + start_macos_services + sleep 3 + echo + color_green "macOS 安装完成。" + view_key + print_public_ip + color_yellow "请在 macOS 防火墙或上游路由器中放行 TCP/UDP 端口 21115-21119。" +} + +install() { + if [[ "$RELEASE" == "linux" ]]; then + install_linux + else + install_macos + fi +} +uninstall_linux() { + stop_linux_services || true + run_privileged systemctl disable "$HBBS_SERVICE_NAME" >/dev/null 2>&1 || true + run_privileged systemctl disable "$HBBR_SERVICE_NAME" >/dev/null 2>&1 || true + run_privileged rm -rf "$INSTALL_DIRECTORY" + run_privileged rm -f "$(linux_hbbs_service_file)" "$(linux_hbbr_service_file)" + run_privileged systemctl daemon-reload >/dev/null 2>&1 || true } +uninstall_macos() { + stop_macos_services || true + run_privileged rm -rf "$INSTALL_DIRECTORY" + run_privileged rm -f "$HBBS_LAUNCHD_PLIST" "$HBBR_LAUNCHD_PLIST" + rm -rf "$MACOS_BUILD_DIRECTORY" +} + +perform_uninstall() { + local mode="${1:-ask}" + local confirm="" + if [[ "$mode" != "force" ]]; then + read -r -p "确认卸载吗?(y/N): " confirm + [[ "$confirm" == "y" || "$confirm" == "Y" ]] || return 0 + fi + + if [[ "$RELEASE" == "linux" ]]; then + uninstall_linux + else + uninstall_macos + fi + + color_blue "RustDesk Server 已卸载。" +} + +uninstall() { + perform_uninstall "ask" +} + +update_self() { + color_yellow "这个支持 macOS 的分支已禁用脚本自更新。" + color_yellow "如果需要同步上游更新,请通过 git 合并,并保留当前的 macOS 适配修改。" +} -#主函数 main() { - options=("安装" install "卸载" uninstall "查看状态" viewstatus "查看key" viewkey "启动服务" startservice "停止服务" stopservice "升级脚本" updateself) + local options=( + "安装" install + "卸载" uninstall + "查看状态" view_status + "查看公钥" view_key + "启动服务" start_service + "停止服务" stop_service + "更新脚本" update_self + ) menu "${options[@]}" } -checkSystem +check_system main