doc(linux): Add linux client install.sh & uninstall.sh
This commit is contained in:
4
.gitattributes
vendored
4
.gitattributes
vendored
@@ -1,6 +1,10 @@
|
|||||||
# Auto detect text files and perform LF normalization
|
# Auto detect text files and perform LF normalization
|
||||||
* text=auto
|
* 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
|
# Custom for Visual Studio
|
||||||
*.cs diff=csharp
|
*.cs diff=csharp
|
||||||
|
|
||||||
|
|||||||
152
linux/install.sh
Executable file
152
linux/install.sh
Executable 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
121
linux/uninstall.sh
Executable 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}"
|
||||||
Reference in New Issue
Block a user