Smail/deploy/install-systemd.sh
smail 629445631f 修复 Linux 部署缺少配置工具
安装器现在校验并安装 smail-config,同时补充对应的防回归测试。
2026-10-15 23:20:02 +08:00

185 lines
7.8 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
set -eu
usage() {
echo "用法:$0 [发布目录]"
echo "每次部署都是全新部署,必须输入确认词后才会清理 Smail 专属运行数据。"
}
fresh=true
root_argument=""
while [ "$#" -gt 0 ]; do
case "$1" in
--fresh)
# 保留旧参数兼容性;部署始终按全新部署处理。
;;
--help|-h)
usage
exit 0
;;
-* )
echo "未知参数:$1" >&2
usage >&2
exit 1
;;
*)
if [ -n "$root_argument" ]; then
echo "发布目录只能指定一次。" >&2
exit 1
fi
root_argument="$1"
;;
esac
shift
done
if [ "$(id -u)" -ne 0 ]; then
echo "必须使用 root 用户安装 Smail" >&2
exit 1
fi
root_dir="$(realpath -e -- "${root_argument:-$(dirname "$0")/..}")"
runtime_dir=/opt/smail
fresh_confirmation="确认全新部署 Smail"
printf '这是不可逆的 Smail 专属数据清理操作。请输入“%s”继续: ' "$fresh_confirmation"
if ! IFS= read -r confirmation || [ "$confirmation" != "$fresh_confirmation" ]; then
echo "未获得有效确认,已取消全新部署。" >&2
exit 1
fi
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/out/backend/smail-config_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 \
/etc/smail/smail.env; do
if [ -L "$target" ] || { [ -e "$target" ] && [ ! -f "$target" ]; }; then
echo "程序或服务文件不能是链接或特殊文件:$target" >&2
exit 1
fi
done
if [ -L /etc/smail ] || { [ -e /etc/smail ] && [ ! -d /etc/smail ]; }; then
echo "服务环境目录不能是链接或普通文件:/etc/smail" >&2
exit 1
fi
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
# 仅保留程序和服务文件的短期备份,不保留配置、数据库、证书、日志、插件或其他运行数据。
for legacy_backup in /var/backups/smail-deploy-*; do
if [ ! -e "$legacy_backup" ]; then continue; fi
if [ -L "$legacy_backup" ] || [ ! -d "$legacy_backup" ]; then
echo "历史 Smail 备份不是安全目录,拒绝清理:$legacy_backup" >&2
exit 1
fi
rm -rf -- "$legacy_backup"
done
if [ "$fresh" = true ]; then
if [ -d "$runtime_dir" ] && [ -n "$(find "$runtime_dir" -mindepth 1 -maxdepth 1 -type l -print -quit)" ]; then
echo "全新部署已停止:/opt/smail 顶层存在符号链接,拒绝清理。" >&2
exit 1
fi
if [ -d "$runtime_dir" ]; then
find "$runtime_dir" -mindepth 1 -maxdepth 1 -exec rm -rf -- {} +
fi
if [ -d /etc/smail ]; then
find /etc/smail -mindepth 1 -maxdepth 1 -exec rm -rf -- {} +
fi
if [ -d /etc/systemd/system/smail.service.d ]; then
rm -rf -- /etc/systemd/system/smail.service.d
fi
if [ -d /run/smail ]; then
find /run/smail -mindepth 1 -maxdepth 1 -exec rm -rf -- {} +
fi
systemctl disable smail smail-admin >/dev/null 2>&1 || true
systemctl daemon-reload
fi
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
if ! getent group smail-plugin >/dev/null; then groupadd --system smail-plugin; fi
if ! getent passwd smail-plugin >/dev/null; then useradd --system --gid smail-plugin --home-dir /nonexistent --shell /usr/sbin/nologin smail-plugin; fi
install -d -o root -g root -m 0755 "$runtime_dir"
install -d -o root -g root -m 0750 /etc/smail
if [ ! -e /etc/smail/smail.env ]; then
# 每次部署都重新创建服务环境文件,不继承旧的凭据或代理配置。
umask 0077
printf '%s\n' 'SMAIL_ROOT=/opt/smail' 'GOMEMLIMIT=256MiB' > /etc/smail/smail.env
fi
if ! grep -q '^SMAIL_PLUGIN_UID=' /etc/smail/smail.env; then
printf 'SMAIL_PLUGIN_UID=%s\n' "$(id -u smail-plugin)" >> /etc/smail/smail.env
fi
if ! grep -q '^SMAIL_PLUGIN_GID=' /etc/smail/smail.env; then
printf 'SMAIL_PLUGIN_GID=%s\n' "$(id -g smail-plugin)" >> /etc/smail/smail.env
fi
chown root:root /etc/smail/smail.env
chmod 0600 /etc/smail/smail.env
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 0700 "$root_dir/out/backend/smail-config_linux_$arch" "$runtime_dir/smail-config"
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 "配置、数据库、证书、日志、插件和绑定信息均未继承;请先完成初始化配置,再使用 systemctl start smail 启动。"
echo "smail-admin 仅允许按需执行 start/stop,不支持开机自启动。"
echo "主服务默认内存高水位 384 MiB、硬上限 512 MiB;不是容量承诺,请先核对同机资源预算。"
echo "旧环境文件不会自动补写 GOMEMLIMIT;启动后请按《生产资源部署说明》执行只读资源预检。"