#!/usr/bin/env bash set -eu if [ "$(id -u)" -ne 0 ]; then echo "必须使用 root 用户安装 Smail" >&2 exit 1 fi root_dir="$(realpath -e -- "${1:-$(dirname "$0")/..}")" runtime_dir=/opt/smail case "$(uname -m)" in x86_64) arch=amd64 ;; aarch64|arm64) arch=arm64 ;; *) echo "不支持的系统架构" >&2; exit 1 ;; esac # 安装前先检查所有输入和运行状态,不能覆盖仍在运行的程序。 for file in "$root_dir/out/backend/smail_linux_$arch" "$root_dir/out/backend/smail-admin_linux_$arch" \ "$root_dir/deploy/systemd/smail.service" "$root_dir/deploy/systemd/smail-admin.service"; do if [ ! -f "$file" ] || [ -L "$file" ] || [ ! -s "$file" ]; then echo "安装输入不存在或不是普通文件:$file" >&2 exit 1 fi done if systemctl is-active --quiet smail.service || systemctl is-active --quiet smail-admin.service; then echo "请先停止 Smail 主服务和管理员重置服务,安装不会自动中断现有业务。" >&2 exit 1 fi for process in /proc/[0-9]*/exe; do executable="$(readlink "$process" 2>/dev/null || true)" case "$executable" in "$runtime_dir/smail"|"$runtime_dir/smail (deleted)"|"$runtime_dir/smail-admin"|"$runtime_dir/smail-admin (deleted)") echo "部署目录中的程序仍在运行,请确认并停止后再安装。" >&2 exit 1 ;; esac done if [ -L /opt ] || [ -L "$runtime_dir" ] || { [ -e "$runtime_dir" ] && [ ! -d "$runtime_dir" ]; }; then echo "部署路径不能是链接或普通文件。" >&2 exit 1 fi for directory in config data ssl log plugins; do target="$runtime_dir/$directory" if [ -L "$target" ] || { [ -e "$target" ] && [ ! -d "$target" ]; }; then echo "运行目录不能是链接或普通文件:$target" >&2 exit 1 fi done for target in "$runtime_dir/smail" "$runtime_dir/smail-admin" \ /etc/systemd/system/smail.service /etc/systemd/system/smail-admin.service; do if [ -L "$target" ] || { [ -e "$target" ] && [ ! -f "$target" ]; }; then echo "程序或服务文件不能是链接或特殊文件:$target" >&2 exit 1 fi done if [ -L /run/smail ] || { [ -e /run/smail ] && [ ! -d /run/smail ]; }; then echo "本机控制目录不能是链接或普通文件。" >&2 exit 1 fi if [ -L /run/smail/admin-control.sock ] || { [ -e /run/smail/admin-control.sock ] && [ ! -S /run/smail/admin-control.sock ]; }; then echo "本机控制路径不是安全套接字。" >&2 exit 1 fi # 安装备份存放在系统备份目录,不在部署根目录遗留 .bak 文件。 backup_dir="$(mktemp -d /var/backups/smail-deploy-XXXXXXXX)" for name in smail smail-admin; do if [ -f "$runtime_dir/$name" ]; then cp -p -- "$runtime_dir/$name" "$backup_dir/$name"; fi done for name in smail.service smail-admin.service; do if [ -f "/etc/systemd/system/$name" ]; then cp -p -- "/etc/systemd/system/$name" "$backup_dir/$name"; fi done if ! getent group smail >/dev/null; then groupadd --system smail; fi if ! getent passwd smail >/dev/null; then useradd --system --gid smail --home-dir "$runtime_dir" --shell /usr/sbin/nologin smail; fi install -d -o root -g root -m 0755 "$runtime_dir" for directory in config data ssl log plugins; do target="$runtime_dir/$directory" # 内置插件在临时沙箱中展开,只兼容已存在的历史插件目录。 if [ "$directory" = plugins ] && [ ! -d "$target" ]; then continue; fi install -d -o smail -g smail -m 0700 "$target" # 不跟随链接,也不越过挂载点;仅为本部署的存量运行数据调整属主。 find -P "$target" -xdev \( -type d -o -type f \) -exec chown --no-dereference smail:smail {} + done install -d -o smail -g smail -m 0750 /run/smail if [ -S /run/smail/admin-control.sock ]; then # 从旧版 root 主服务迁移时保留残留套接字,由主程序验证失活后自行处理。 chown --no-dereference smail:smail /run/smail/admin-control.sock fi install -d -o root -g root -m 0755 /run/systemd/ask-password install -o root -g root -m 0755 "$root_dir/out/backend/smail_linux_$arch" "$runtime_dir/smail" install -o root -g root -m 0700 "$root_dir/out/backend/smail-admin_linux_$arch" "$runtime_dir/smail-admin" install -o root -g root -m 0644 "$root_dir/deploy/systemd/smail.service" /etc/systemd/system/smail.service install -o root -g root -m 0644 "$root_dir/deploy/systemd/smail-admin.service" /etc/systemd/system/smail-admin.service systemctl daemon-reload systemctl enable smail # 清理旧版本可能留下的开机启动关系。smail-admin 单元没有 [Install],不能再次 enable。 systemctl disable smail-admin >/dev/null 2>&1 || true echo "Smail 已安装到 $runtime_dir,旧程序和服务文件备份在 $backup_dir。" echo "配置、数据库和证书未覆盖;请使用 systemctl start smail 启动并检查运行状态。" echo "smail-admin 仅允许按需执行 start/stop,不支持开机自启动。"