doc(linux): Add linux client install.sh & uninstall.sh

This commit is contained in:
yuanyuanxiang
2026-05-22 22:02:38 +02:00
parent 740ec8baf3
commit 62e962f216
3 changed files with 277 additions and 0 deletions

4
.gitattributes vendored
View File

@@ -1,6 +1,10 @@
# Auto detect text files and perform LF normalization
* text=auto
# Shell scripts must keep LF line endings even when checked out on Windows,
# otherwise Linux refuses them with "bad interpreter: /usr/bin/env^M".
*.sh text eol=lf
# Custom for Visual Studio
*.cs diff=csharp

152
linux/install.sh Executable file
View File

@@ -0,0 +1,152 @@
#!/usr/bin/env bash
# YAMA Ghost (Linux client) — install + autostart deployment
#
# 用法(在解压/克隆后的 linux/ 目录下):
# ./install.sh # 默认安装到 ~/.local/bin/ghost
# ./install.sh /opt/yama # 安装到 /opt/yama/ghost如需要会自动 sudo
#
# 行为:
# 1. 复制 ghost 二进制到目标位置并加可执行权
# 2. 注册 XDG Autostart~/.config/autostart/ghost.desktop
# 3. 可选立即启动一次(继承当前桌面会话的 X 环境)
set -euo pipefail
# ---- 防止以 root 直接运行 ----
# 用 sudo 跑会让 $HOME 变成 /root或 sudo 配置决定的值),
# autostart 写到 /root/.config/autostart/,桌面用户的 session 看不见,
# 自启动完全失效。需要 sudo 的地方(如装到 /opt/...),脚本会按需自调用 sudo。
if [[ "${EUID:-$(id -u)}" -eq 0 ]]; then
echo "请用普通用户身份运行此脚本,不要 sudo。" >&2
echo "如目标目录需要 root 权限,脚本会按需自动调用 sudo。" >&2
exit 1
fi
# ---- 颜色 ----
if [[ -t 1 ]]; then
C_RED=$'\033[31m'; C_GREEN=$'\033[32m'; C_YELLOW=$'\033[33m'
C_BLUE=$'\033[34m'; C_BOLD=$'\033[1m'; C_RESET=$'\033[0m'
else
C_RED=''; C_GREEN=''; C_YELLOW=''; C_BLUE=''; C_BOLD=''; C_RESET=''
fi
info() { echo "${C_BLUE}[INFO]${C_RESET} $*"; }
ok() { echo "${C_GREEN}[ OK ]${C_RESET} $*"; }
warn() { echo "${C_YELLOW}[WARN]${C_RESET} $*"; }
error() { echo "${C_RED}[FAIL]${C_RESET} $*" >&2; }
# ---- 路径解析 ----
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SRC_BIN="${SCRIPT_DIR}/ghost"
# 安装目标目录(参数 $1默认 ~/.local/bin
INSTALL_DIR="${1:-${HOME}/.local/bin}"
DEST_BIN="${INSTALL_DIR}/ghost"
AUTOSTART_DIR="${XDG_CONFIG_HOME:-${HOME}/.config}/autostart"
AUTOSTART_FILE="${AUTOSTART_DIR}/ghost.desktop"
echo "${C_BOLD}YAMA Ghost Linux 安装${C_RESET}"
echo " 源: ${SRC_BIN}"
echo " 目标: ${DEST_BIN}"
echo " 自启动: ${AUTOSTART_FILE}"
echo ""
# ---- 前置检查 ----
if [[ ! -f "${SRC_BIN}" ]]; then
error "找不到 ghost 二进制 ${SRC_BIN}"
error "请把 install.sh 放在 ghost 同目录后再运行"
exit 1
fi
if ! file "${SRC_BIN}" 2>/dev/null | grep -q "ELF.*executable"; then
error "${SRC_BIN} 不是有效的 ELF 可执行文件"
exit 1
fi
# 判断目标目录是否需要 sudo
# 三种情况都要走 sudo 分支:
# a) 目录不存在且父目录无写权(如 /opt/yama 父是 /opt root-owned
# b) 目录已存在但当前用户无写权(如已存在的 /usr/local/bin root-owned
# c) 介于两者之间的情况由 mkdir 的退出码决定
NEED_SUDO=""
if [[ -d "${INSTALL_DIR}" ]]; then
[[ -w "${INSTALL_DIR}" ]] || NEED_SUDO="sudo"
else
if ! mkdir -p "${INSTALL_DIR}" 2>/dev/null; then
NEED_SUDO="sudo"
fi
fi
if [[ -n "${NEED_SUDO}" ]]; then
info "目标目录需要 root 权限,将使用 sudo可能需要输入密码"
${NEED_SUDO} mkdir -p "${INSTALL_DIR}"
fi
# ---- 1. 如已运行则先停止 ----
if pgrep -x ghost > /dev/null; then
warn "检测到 ghost 进程正在运行,先停止以替换二进制"
pkill -x ghost || true
sleep 1
if pgrep -x ghost > /dev/null; then
warn "进程未优雅退出,强制 kill"
pkill -9 -x ghost || true
sleep 1
fi
ok "旧进程已停止"
fi
# ---- 2. 复制二进制 ----
info "复制 ghost 到 ${DEST_BIN}"
${NEED_SUDO} install -m 0755 "${SRC_BIN}" "${DEST_BIN}"
ok "二进制已部署 (mode 0755)"
# ---- 3. 写 XDG Autostart 文件 ----
mkdir -p "${AUTOSTART_DIR}"
cat > "${AUTOSTART_FILE}" <<EOF
[Desktop Entry]
Type=Application
Name=YAMA Ghost
Comment=YAMA remote control client
Exec=${DEST_BIN}
Terminal=false
X-GNOME-Autostart-enabled=true
NoDisplay=true
StartupNotify=false
EOF
ok "Autostart 已注册"
# 验证 .desktop 格式(如果系统装了 desktop-file-validate
if command -v desktop-file-validate >/dev/null 2>&1; then
if desktop-file-validate "${AUTOSTART_FILE}" >/dev/null 2>&1; then
ok "Autostart 文件格式验证通过"
else
warn "desktop-file-validate 报告了警告,但通常不影响功能"
fi
fi
# ---- 4. 可选:立即启动 ----
echo ""
echo -n "${C_BOLD}是否立即启动 ghost验证 X 环境)?[Y/n]${C_RESET} "
read -r ans
if [[ -z "${ans}" || "${ans}" =~ ^[Yy]$ ]]; then
if [[ -z "${DISPLAY:-}" ]]; then
warn "当前 shell 没有 DISPLAY 变量,可能不在桌面会话内 — 启动后远控仍可能 0x0"
warn "建议在 GNOME 终端/桌面环境的终端里运行此脚本"
fi
nohup "${DEST_BIN}" >/dev/null 2>&1 &
sleep 1
if pgrep -x ghost > /dev/null; then
ok "ghost 已启动 (PID=$(pgrep -x ghost | head -1))"
else
error "启动失败,请手动跑 ${DEST_BIN} 看错误输出"
exit 1
fi
fi
echo ""
echo "${C_GREEN}${C_BOLD}✓ 安装完成${C_RESET}"
echo ""
echo "下次开机将自动启动;如需立即测试,重启或在桌面终端跑:"
echo " ${DEST_BIN}"
echo ""
echo "卸载请运行同目录的 ./uninstall.sh"

121
linux/uninstall.sh Executable file
View File

@@ -0,0 +1,121 @@
#!/usr/bin/env bash
# YAMA Ghost (Linux client) — uninstall
#
# 用法:
# ./uninstall.sh # 默认从 ~/.local/bin/ghost 卸载
# ./uninstall.sh /opt/yama # 从指定目录卸载
# ./uninstall.sh --yes # 跳过确认(自动化场景)
#
# 行为(幂等 — 重复运行不会报错):
# 1. 停止运行中的 ghost 进程
# 2. 删除 XDG Autostart 文件
# 3. 删除已安装的二进制
# 4. 询问是否清理用户配置(~/.config/ghost
set -euo pipefail
# ---- 颜色 ----
if [[ -t 1 ]]; then
C_RED=$'\033[31m'; C_GREEN=$'\033[32m'; C_YELLOW=$'\033[33m'
C_BLUE=$'\033[34m'; C_BOLD=$'\033[1m'; C_RESET=$'\033[0m'
else
C_RED=''; C_GREEN=''; C_YELLOW=''; C_BLUE=''; C_BOLD=''; C_RESET=''
fi
info() { echo "${C_BLUE}[INFO]${C_RESET} $*"; }
ok() { echo "${C_GREEN}[ OK ]${C_RESET} $*"; }
warn() { echo "${C_YELLOW}[WARN]${C_RESET} $*"; }
error() { echo "${C_RED}[FAIL]${C_RESET} $*" >&2; }
# ---- 参数解析 ----
ASSUME_YES=0
INSTALL_DIR="${HOME}/.local/bin"
for arg in "$@"; do
case "${arg}" in
--yes|-y) ASSUME_YES=1 ;;
--help|-h)
# 头部注释覆盖标题/用法/行为 4 步,对应源文件第 2-13 行
sed -n '2,13p' "$0" | sed 's/^# \?//'
exit 0
;;
*) INSTALL_DIR="${arg}" ;;
esac
done
DEST_BIN="${INSTALL_DIR}/ghost"
AUTOSTART_FILE="${XDG_CONFIG_HOME:-${HOME}/.config}/autostart/ghost.desktop"
CONFIG_DIR="${XDG_CONFIG_HOME:-${HOME}/.config}/ghost"
confirm() {
[[ "${ASSUME_YES}" -eq 1 ]] && return 0
local prompt="$1"
local ans=""
echo -n "${prompt} [y/N] "
read -r ans || true # EOF on stdin: ans stays empty, 返回 no
[[ "${ans}" =~ ^[Yy]$ ]]
}
echo "${C_BOLD}YAMA Ghost Linux 卸载${C_RESET}"
echo " 二进制: ${DEST_BIN}"
echo " 自启动: ${AUTOSTART_FILE}"
echo " 配置: ${CONFIG_DIR}"
echo ""
if ! confirm "确认卸载?"; then
info "已取消"
exit 0
fi
# ---- 1. 停止进程 ----
if pgrep -x ghost > /dev/null; then
info "停止运行中的 ghost 进程"
pkill -x ghost || true
sleep 1
if pgrep -x ghost > /dev/null; then
warn "进程未优雅退出,强制 kill"
pkill -9 -x ghost || true
sleep 1
fi
ok "ghost 进程已停止"
else
info "没有运行中的 ghost 进程"
fi
# ---- 2. 删除 Autostart 文件 ----
if [[ -f "${AUTOSTART_FILE}" ]]; then
rm -f "${AUTOSTART_FILE}"
ok "已删除 ${AUTOSTART_FILE}"
else
info "Autostart 文件不存在(已卸载或未安装过)"
fi
# ---- 3. 删除二进制 ----
if [[ -f "${DEST_BIN}" ]]; then
if [[ -w "${DEST_BIN}" ]] || [[ -w "$(dirname "${DEST_BIN}")" ]]; then
rm -f "${DEST_BIN}"
ok "已删除 ${DEST_BIN}"
else
info "需要 sudo 才能删除 ${DEST_BIN}"
sudo rm -f "${DEST_BIN}"
ok "已删除 ${DEST_BIN}"
fi
# 如果安装目录是 ~/.local/bin 且现在空了,不删除(可能用户还有其它东西)
else
info "二进制不存在(已卸载或不在 ${INSTALL_DIR}"
fi
# ---- 4. 用户配置目录(询问,不主动删)----
if [[ -d "${CONFIG_DIR}" ]]; then
echo ""
warn "用户配置目录仍存在:${CONFIG_DIR}"
warn "其中可能包含 PID 文件、日志等。删除后无法恢复。"
if confirm " 一并删除配置目录?"; then
rm -rf "${CONFIG_DIR}"
ok "已删除 ${CONFIG_DIR}"
else
info "保留配置目录"
fi
fi
echo ""
echo "${C_GREEN}${C_BOLD}✓ 卸载完成${C_RESET}"