safesight/edge/scripts/setup-overlayroot.sh
tian 29d9ef5d0d refactor: 项目结构梳理 - 设备端/管理端对称布局
仓库结构:
  edge/     设备端(原根目录设备端代码整体移入)
  control/  管理端(清理后)
  docs/     文档(PRD 移入 design/)
  README.md 根导航(新增)

清理:
- control/.brainstorm 临时草稿删除
- control 根级重复文档(API表/PRD_04)并入 docs/design/
- control/plan.md -> docs/implementation/control-plan.md
- control/safesightd-linux-arm64 二进制取消版本控制(.gitignore)
- edge/transform 模型转换产物归入 models/,onnx/pt 大源文件取消跟踪(.gitignore)
- Readme.md(PRD) -> docs/design/PRD_Product_v1.2.md(避开 README 大小写冲突)

更新:
- 根 README.md 导航、docs/README.md 文档索引
- deployment.md/检查表路径加 edge/ 前缀
- .gitignore 重写(edge/control 分区规则)
2026-08-02 11:39:54 +08:00

555 lines
22 KiB
Bash
Raw Permalink 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.

#!/bin/bash
# =============================================================================
# setup-overlayroot.sh — RK3588 Edge 断电保护方案一overlayroot + /data 分区)
# -----------------------------------------------------------------------------
# 解决场景SD 卡断电重启导致根文件系统损坏、无法启动。
#
# 原理:
# 1) 用 Ubuntu 官方 overlayroot 把根文件系统置于 tmpfs overlay 之上,
# 系统对根分区的一切写入进入内存,重启即丢,根分区永久只读,
# 断电不再损坏根文件系统。
# 2) 在 SD 卡剩余空间划分独立读写分区 /dataext4存放日志、
# 告警图片/视频、设备 ID 等需要持久化的数据,不受 overlay 影响。
#
# 用法(在设备上执行):
# sudo ./setup-overlayroot.sh install 一键启用(推荐)
# sudo ./setup-overlayroot.sh install --yes 跳过确认(无人值守)
# sudo ./setup-overlayroot.sh install --hls-ram HLS 分片放内存(tmpfs)
# 减小 SD 写入(按需)
# sudo ./setup-overlayroot.sh status 查看当前状态
# sudo ./setup-overlayroot.sh verify 重启后验证是否生效
# sudo ./setup-overlayroot.sh disable 临时禁用(维护模式,可写)
# sudo ./setup-overlayroot.sh enable 重新启用(退出维护模式)
#
# 可选环境变量:
# HLS_MODE=ram|data HLS 分片存放位置(默认 data--hls-ram 等价 ram
# JOURNAL_MODE=ram|data 系统 journal 日志(默认 ram
# SKIP_PARTITION=1 跳过分区创建(镜像已含 /data 分区时使用)
# =============================================================================
set -euo pipefail
# ---------------------------------------------------------------------------
# 颜色与输出
# ---------------------------------------------------------------------------
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
BLUE='\033[0;34m'; CYAN='\033[0;36m'; NC='\033[0m'
info() { echo -e "${CYAN}[INFO]${NC} $*"; }
ok() { echo -e "${GREEN}${NC} $*"; }
warn() { echo -e "${YELLOW}${NC} $*"; }
die() { echo -e "${RED}${NC} $*" >&2; exit 1; }
# ---------------------------------------------------------------------------
# 全局配置
# ---------------------------------------------------------------------------
DATA_MOUNT="/data"
DATA_LABEL="safesight-data"
DATA_SUBDIRS="hls logs alarms clips agent"
RUNTIME_ROOT="/var/lib/safesight-edge-server"
AGENT_LIB="/var/lib/safesight-edge-agent"
FSTAB="/etc/fstab"
OVERLAY_CONF="/etc/overlayroot.conf"
MARKER_BEGIN="# safesight-overlayroot (自动生成,勿手动修改)"
MARKER_END="# end safesight-overlayroot"
HLS_MODE="${HLS_MODE:-data}" # data | ram
JOURNAL_MODE="${JOURNAL_MODE:-ram}" # ram | data
SKIP_PARTITION="${SKIP_PARTITION:-0}"
ASSUME_YES=0
# ---------------------------------------------------------------------------
# 参数解析
# ---------------------------------------------------------------------------
CMD="${1:-install}"
shift || true
for arg in "$@"; do
case "$arg" in
--yes) ASSUME_YES=1 ;;
--hls-ram) HLS_MODE=ram ;;
--journal-data) JOURNAL_MODE=data ;;
*) warn "未知参数: $arg" ;;
esac
done
# ---------------------------------------------------------------------------
# 基础检查
# ---------------------------------------------------------------------------
require_root() {
[ "$(id -u)" -eq 0 ] || die "请使用 sudo 运行此脚本"
}
detect_distro() {
if [ -r /etc/os-release ]; then
# shellcheck disable=SC1091
. /etc/os-release
case "${ID:-}" in
ubuntu|debian) return 0 ;;
esac
fi
warn "非 Ubuntu/Debian 系统overlayroot 可能不可用,继续尝试..."
}
overlay_active() {
grep -qs 'overlay' /proc/mounts
}
# ---------------------------------------------------------------------------
# 磁盘/分区检测
# ---------------------------------------------------------------------------
detect_root_disk() {
ROOT_DEV="$(findmnt -no SOURCE / 2>/dev/null || true)"
[ -n "${ROOT_DEV:-}" ] || die "无法定位根分区"
case "$ROOT_DEV" in
/dev/mmcblk*) ROOT_DISK="${ROOT_DEV%p*}"; ROOT_PART="${ROOT_DEV##*p}" ;;
/dev/nvme*) ROOT_DISK="${ROOT_DEV%p*}"; ROOT_PART="${ROOT_DEV##*p}" ;;
/dev/sd*) ROOT_DISK="${ROOT_DEV%[0-9]*}"; ROOT_PART="${ROOT_DEV##*[a-z]}" ;;
*) die "无法识别的根设备: $ROOT_DEV(本脚本仅支持 SD 卡/eMMC/NVMe 直连分区)" ;;
esac
info "根分区: $ROOT_DEV → 磁盘: $ROOT_DISK"
}
# /data 是否已存在(已挂载,或磁盘上有数据分区)
data_partition_exists() {
# 已挂载
if mountpoint -q "$DATA_MOUNT" 2>/dev/null; then
DATA_PART="$(findmnt -no SOURCE "$DATA_MOUNT" 2>/dev/null || true)"
return 0
fi
# 磁盘上已有标签 safesight-data 的分区(未挂载)
for p in $(lsblk -ln -o NAME,TYPE "$ROOT_DISK" 2>/dev/null | awk '$2=="part"{print $1}'); do
d="/dev/$p"
if [ "$(blkid -s LABEL -o value "$d" 2>/dev/null)" = "$DATA_LABEL" ]; then
DATA_PART="$d"
return 0
fi
done
return 1
}
find_free_space() {
# 返回 "起始MiB 大小MiB"(取最后一个 >=2048MiB 的 Free Space否则返回空
LANG=C parted -s "$ROOT_DISK" unit MiB print free 2>/dev/null | \
awk '$4=="Free" && $3>=2048 {start=$1; size=$3} END {if (size>=2048) print start, size}'
}
create_data_partition() {
info "$ROOT_DISK 空闲空间中创建 /data 分区($DATA_LABEL..."
FREE_INFO="$(find_free_space || true)"
[ -n "$FREE_INFO" ] || die "未找到足够空闲空间(至少 2GiB。请先扩容 SD 卡镜像或参考文档手动划分 /data 分区。"
FREE_START="$(echo "$FREE_INFO" | awk '{print $1}' | tr -d 'MiB')"
FREE_SIZE="$(echo "$FREE_INFO" | awk '{print $2}')"
echo ""
echo "即将执行:"
echo "$ROOT_DISK 偏移 ${FREE_START}MiB 处创建 ext4 分区(约 ${FREE_SIZE}"
echo " 分区标签: $DATA_LABEL"
echo " 该分区将挂载到 $DATA_MOUNT,保存日志/告警/设备ID等持久数据"
echo ""
if [ "$ASSUME_YES" -ne 1 ]; then
read -r -p "继续? [y/N]: " yn
[ "${yn:-n}" = "y" ] || [ "${yn:-n}" = "Y" ] || die "已取消"
fi
# 分区表类型gpt 用名称参数msdos 用 primary
TABLE_TYPE="$(parted -s "$ROOT_DISK" print 2>/dev/null | awk '/Partition Table/{print $3}')"
if [ "$TABLE_TYPE" = "gpt" ]; then
parted -s "$ROOT_DISK" mkpart "$DATA_LABEL" ext4 "${FREE_START}MiB" 100%
else
parted -s "$ROOT_DISK" mkpart primary ext4 "${FREE_START}MiB" 100%
fi
# 刷新分区表
partprobe "$ROOT_DISK" 2>/dev/null || partx -u "$ROOT_DISK" 2>/dev/null || true
udevadm settle 2>/dev/null || sleep 3
# 新分区 = 磁盘上最后一个 part
DATA_PART="$(lsblk -ln -o NAME,TYPE "$ROOT_DISK" | awk '$2=="part"{p=$1} END{print "/dev/"p}')"
[ -n "${DATA_PART:-}" ] || die "分区创建失败"
mkfs.ext4 -F -L "$DATA_LABEL" "$DATA_PART"
ok "数据分区已创建: $DATA_PART (标签 $DATA_LABEL)"
}
# ---------------------------------------------------------------------------
# fstab 写入(先于挂载执行)
# ---------------------------------------------------------------------------
write_fstab() {
[ -n "${DATA_UUID:-}" ] || die "未获取到数据分区 UUID"
local bak="$FSTAB.bak-$(date +%Y%m%d%H%M%S)"
cp -a "$FSTAB" "$bak"
info "fstab 已备份: $bak"
# 移除旧的管理块(幂等)
sed -i "/^${MARKER_BEGIN}/,/^${MARKER_END}/d" "$FSTAB"
{
echo "$MARKER_BEGIN"
echo "# 独立读写数据分区断电保护核心rootfs 只读后唯一持久化区域)"
echo "UUID=$DATA_UUID $DATA_MOUNT ext4 defaults,noatime,nofail,x-systemd.device-timeout=10s,commit=60 0 2"
# safesight 运行数据 bind 到 /data路径保持 /var/lib/... 不变,兼容现有部署)
echo "# safesight 运行数据 → /data日志/告警/视频/设备ID 断电不丢)"
if [ "$HLS_MODE" = "ram" ]; then
echo "# HLS 分片 → 内存 tmpfs重启即丢减小 SD 写入;容量按需调整)"
echo "tmpfs $RUNTIME_ROOT/hls tmpfs defaults,noatime,size=512m,mode=755 0 0"
else
echo "$DATA_MOUNT/safesight/hls $RUNTIME_ROOT/hls none bind,nofail 0 0"
fi
echo "$DATA_MOUNT/safesight/logs $RUNTIME_ROOT/logs none bind,nofail 0 0"
echo "$DATA_MOUNT/safesight/alarms $RUNTIME_ROOT/alarms none bind,nofail 0 0"
echo "$DATA_MOUNT/safesight/clips $RUNTIME_ROOT/clips none bind,nofail 0 0"
echo "$DATA_MOUNT/safesight/agent $AGENT_LIB none bind,nofail 0 0"
if [ "$JOURNAL_MODE" = "data" ]; then
echo "# 系统 journal → /data断电不丢便于事后排查"
echo "$DATA_MOUNT/safesight/journal /var/log/journal none bind,nofail 0 0"
fi
echo "$MARKER_END"
} >> "$FSTAB"
ok "fstab 已更新"
}
# ---------------------------------------------------------------------------
# /data 目录初始化 + 存量数据迁移 + 挂载
# ---------------------------------------------------------------------------
setup_data_dirs() {
local sub
for sub in $DATA_SUBDIRS; do
mkdir -p "$DATA_MOUNT/safesight/$sub"
done
[ "$JOURNAL_MODE" = "data" ] && mkdir -p "$DATA_MOUNT/safesight/journal"
# 迁移已有数据bind 挂载前执行,避免覆盖)
migrate_dir() {
local src="$1" dst="$2" name="$3"
# bind 已挂载时 src 与 dst 是同一目录,跳过
[ "$(readlink -f "$src")" != "$(readlink -f "$dst")" ] || return 0
if [ -d "$src" ] && [ -n "$(ls -A "$src" 2>/dev/null)" ]; then
cp -a "$src"/. "$dst"/ && ok "已迁移 $name 存量数据 → $dst"
fi
}
for sub in hls logs alarms clips; do
migrate_dir "$RUNTIME_ROOT/$sub" "$DATA_MOUNT/safesight/$sub" "$sub"
done
migrate_dir "$AGENT_LIB" "$DATA_MOUNT/safesight/agent" "agent"
# 确保 bind 目标目录存在
for sub in hls logs alarms clips; do
mkdir -p "$RUNTIME_ROOT/$sub"
done
mkdir -p "$AGENT_LIB"
mkdir -p /var/log/journal
}
mount_all() {
# 挂载 /data 与全部 bind依赖上一步 fstab/目录已就绪)
mount -a 2>/dev/null || true
if ! mountpoint -q "$DATA_MOUNT" 2>/dev/null; then
die "$DATA_MOUNT 挂载失败,请检查 /etc/fstab"
fi
ok "$DATA_MOUNT 已挂载 rw (UUID=$DATA_UUID)"
}
# ---------------------------------------------------------------------------
# journald 配置(控制系统日志内存占用 / 持久化)
# ---------------------------------------------------------------------------
setup_journald() {
mkdir -p /etc/systemd/journald.conf.d
if [ "$JOURNAL_MODE" = "data" ]; then
cat > /etc/systemd/journald.conf.d/safesight.conf << 'EOF'
[Journal]
Storage=persistent
SystemMaxUse=256M
SystemMaxFile=32M
MaxRetentionSec=14d
SyncIntervalSec=1h
EOF
ok "journald → /data持久化上限 256M / 保留14天"
else
cat > /etc/systemd/journald.conf.d/safesight.conf << 'EOF'
[Journal]
Storage=volatile
RuntimeMaxUse=64M
EOF
ok "journald → 内存volatile上限 64M重启即丢"
fi
systemctl restart systemd-journald 2>/dev/null || true
}
# ---------------------------------------------------------------------------
# overlayroot 安装与配置
# ---------------------------------------------------------------------------
install_overlayroot() {
if ! dpkg -s overlayroot >/dev/null 2>&1; then
info "安装 overlayroot ..."
apt-get update -qq 2>/dev/null || warn "apt update 失败(设备可能离线),尝试直接安装..."
apt-get install -y overlayroot || die "overlayroot 安装失败。若设备离线,请将 overlayroot 预装进母卡镜像sudo apt install overlayroot后重新打包。"
else
ok "overlayroot 已安装"
fi
# 备份旧配置
if [ -f "$OVERLAY_CONF" ]; then
cp -a "$OVERLAY_CONF" "$OVERLAY_CONF.bak-$(date +%Y%m%d%H%M%S)"
fi
cat > "$OVERLAY_CONF" << 'EOF'
# /etc/overlayroot.conf — 由 setup-overlayroot.sh 生成
# 启用后根文件系统只读,一切写入进入内存(tmpfs),重启即丢,断电不损坏根分区。
# 维护模式: 运行 sudo /opt/safesight-edge-server/scripts/setup-overlayroot.sh disable
overlayroot="tmpfs"
overlayroot_tmpfs_size="50%"
overlayroot_quiet="n"
# /data 是独立分区fstab 挂载,不经过 overlay保持可写无需排除。
# 若个别镜像出现其它分区被意外只读,可在此追加排除,例如:
# overlayroot_exclude_partitions="/var/lib/docker"
EOF
ok "/etc/overlayroot.conf 已配置 (overlayroot=\"tmpfs\")"
info "重建 initramfs ..."
update-initramfs -u || die "update-initramfs 失败"
if [ -d /boot/extlinux ]; then
warn "检测到 extlinux 引导U-Boot/香橙派。overlayroot 由 initramfs 加载,无需改 extlinux.conf。"
fi
ok "initramfs 已更新"
}
# ---------------------------------------------------------------------------
# 命令实现
# ---------------------------------------------------------------------------
cmd_install() {
require_root
detect_distro
detect_root_disk
if overlay_active; then
warn "当前系统已处于 overlayroot 保护模式。如需调整配置,请先运行: $0 disable"
fi
# 1) 数据分区
if data_partition_exists; then
ok "检测到数据分区: ${DATA_PART:-/data 已挂载)}"
else
if [ "${SKIP_PARTITION:-0}" = "1" ]; then
die "SKIP_PARTITION=1 但未检测到 $DATA_LABEL 分区,请确认镜像包含数据分区"
fi
create_data_partition
fi
DATA_UUID="$(blkid -s UUID -o value "$DATA_PART" 2>/dev/null || true)"
[ -n "$DATA_UUID" ] || die "未获取到数据分区 UUID ($DATA_PART)"
# 2) fstab + 目录 + 挂载fstab 先写,再按新 fstab 挂载)
write_fstab
setup_data_dirs
mount_all
# 3) journald
setup_journald
# 4) overlayroot
install_overlayroot
echo ""
echo -e "${GREEN}════════════════════════════════════════════════════════════════${NC}"
echo -e "${GREEN} 配置完成!重启后根文件系统将进入只读保护模式${NC}"
echo -e "${GREEN}════════════════════════════════════════════════════════════════${NC}"
echo ""
echo "下一步:"
echo " sudo reboot # 重启生效"
echo ""
echo "重启后验证:"
echo " sudo /opt/safesight-edge-server/scripts/setup-overlayroot.sh verify"
echo ""
echo "数据分区(断电不丢):"
echo " $DATA_MOUNT/safesight/{logs,alarms,clips,agent}"
echo " $DATA_MOUNT/safesight/hls # HLS 分片 (${HLS_MODE})"
echo ""
echo "日常维护(装软件/改系统配置,需临时可写):"
echo " sudo /opt/safesight-edge-server/scripts/setup-overlayroot.sh disable"
echo " sudo reboot # 进入可写维护模式"
echo " sudo /opt/safesight-edge-server/scripts/setup-overlayroot.sh enable"
echo " sudo reboot # 恢复只读保护"
echo ""
echo "注意overlayroot 启用后apt 安装、系统配置修改都会在重启后丢失,"
echo " 必须进入维护模式操作!"
}
cmd_status() {
require_root
echo -e "${BLUE}══════════ overlayroot 状态 ══════════${NC}"
if overlay_active; then
echo -e " 根文件系统: ${GREEN}● 保护模式overlay, 只读+tmpfs${NC}"
mount | grep ' / ' | sed 's/^/ /'
echo -e " 上层(tmpfs)容量: $(df -h / | awk 'NR==2{print $2}') 已用: $(df -h / | awk 'NR==2{print $3}')"
else
echo -e " 根文件系统: ${YELLOW}○ 可写模式overlay 未启用)${NC}"
fi
echo ""
if mountpoint -q "$DATA_MOUNT" 2>/dev/null; then
echo -e " $DATA_MOUNT: ${GREEN}已挂载 rw${NC} $(findmnt -no SOURCE,FSTYPE "$DATA_MOUNT")"
echo " 磁盘占用: $(df -h "$DATA_MOUNT" | awk 'NR==2{print "总"$2" 已用"$3" 可用"$4}')"
else
echo -e " $DATA_MOUNT: ${RED}未挂载${NC}"
fi
echo ""
echo " bind 挂载:"
for b in logs alarms clips hls agent; do
m="$RUNTIME_ROOT/$b"
[ "$b" = "agent" ] && m="$AGENT_LIB"
if mountpoint -q "$m" 2>/dev/null; then
echo -e " ${GREEN}${NC} $m$(findmnt -no SOURCE "$m")"
else
echo -e " ${RED}${NC} $m 未挂载"
fi
done
echo ""
if [ -f "$OVERLAY_CONF" ]; then
echo " /etc/overlayroot.conf:"
grep -E '^overlayroot' "$OVERLAY_CONF" | sed 's/^/ /'
else
echo -e " /etc/overlayroot.conf: ${RED}不存在${NC}"
fi
if [ -f /etc/systemd/journald.conf.d/safesight.conf ]; then
echo ""
echo " journald: $(grep -E '^\s*(Storage|RuntimeMaxUse|SystemMaxUse)=' /etc/systemd/journald.conf.d/safesight.conf | tr '\n' ' ')"
fi
echo ""
if [ -f "$AGENT_LIB/device_id" ]; then
echo " 设备 ID: $(cat "$AGENT_LIB/device_id")(持久化于 /data"
else
echo -e " 设备 ID: ${YELLOW}尚未生成${NC}"
fi
}
cmd_verify() {
require_root
echo -e "${BLUE}══════════ 断电保护验证 ══════════${NC}"
FAIL=0
if overlay_active; then
ok "overlay 已启用,根分区写入受保护"
else
warn "overlay 未启用(仍处于可写模式)"
FAIL=1
fi
# /data 可写测试
if mountpoint -q "$DATA_MOUNT"; then
TEST_FILE="$DATA_MOUNT/.write_test_$(date +%s)"
if echo ok > "$TEST_FILE" 2>/dev/null && rm -f "$TEST_FILE"; then
ok "$DATA_MOUNT 可读写"
else
warn "$DATA_MOUNT 不可写!"
FAIL=1
fi
else
warn "$DATA_MOUNT 未挂载"
FAIL=1
fi
# bind 挂载
for b in logs alarms clips agent; do
m="$RUNTIME_ROOT/$b"
[ "$b" = "agent" ] && m="$AGENT_LIB"
if mountpoint -q "$m" 2>/dev/null; then
ok "$m$(findmnt -no SOURCE "$m")"
else
warn "$m 未挂载(若为首次启用,服务运行后会自动创建)"
fi
done
# 根分区是否真的只读(写测试)
ROTEST="/.write_test_root_$$"
if touch "$ROTEST" 2>/dev/null; then
rm -f "$ROTEST"
warn "警告: 根分区仍可写!请检查 overlayroot 配置"
FAIL=1
else
ok "根分区只读"
fi
# 服务状态
for s in safesight-edge-server safesight-edge-agent; do
if systemctl is-active --quiet "$s" 2>/dev/null; then
ok "$s 运行中"
else
warn "$s 未运行"
fi
done
echo ""
if [ "$FAIL" -eq 0 ]; then
echo -e "${GREEN}✓ 断电保护配置正常,可放心断电${NC}"
else
echo -e "${YELLOW}存在未通过项,请按上方提示处理${NC}"
fi
exit "$FAIL"
}
# 切换 overlayroot 开关disable=可写维护 / enable=恢复保护)
toggle_overlay() {
local mode="$1"
require_root
[ -f "$OVERLAY_CONF" ] || cp /dev/null "$OVERLAY_CONF"
cp -a "$OVERLAY_CONF" "$OVERLAY_CONF.bak-$(date +%Y%m%d%H%M%S)"
if [ "$mode" = "disable" ]; then
sed -i 's/^overlayroot=.*/overlayroot=""/' "$OVERLAY_CONF"
# 若文件里没有可识别的行则追加
grep -q '^overlayroot=' "$OVERLAY_CONF" || echo 'overlayroot=""' >> "$OVERLAY_CONF"
echo ""
echo -e "${YELLOW}overlayroot 已设为空(可写模式)。重启后根分区恢复可写,用于安装软件/修改配置。${NC}"
else
sed -i 's/^overlayroot=.*/overlayroot="tmpfs"/' "$OVERLAY_CONF"
grep -q '^overlayroot=' "$OVERLAY_CONF" || echo 'overlayroot="tmpfs"' >> "$OVERLAY_CONF"
echo ""
echo -e "${GREEN}overlayroot 已重新启用(保护模式)。重启后根分区只读。${NC}"
fi
info "重建 initramfs ..."
update-initramfs -u
echo ""
echo "请执行: sudo reboot"
}
# ---------------------------------------------------------------------------
# 主入口
# ---------------------------------------------------------------------------
case "$CMD" in
install) cmd_install ;;
status) cmd_status ;;
verify) cmd_verify ;;
disable) toggle_overlay disable ;;
enable) toggle_overlay enable ;;
*)
echo "RK3588 Edge 断电保护脚本overlayroot + /data"
echo ""
echo "用法: sudo $(basename "$0") [命令] [选项]"
echo ""
echo "命令:"
echo " install 一键启用断电保护(创建/挂载 /data + 启用 overlayroot"
echo " status 查看当前保护状态"
echo " verify 重启后验证保护是否生效"
echo " disable 临时禁用(维护模式:装软件/改系统配置)"
echo " enable 重新启用(恢复保护)"
echo ""
echo "选项:"
echo " --yes 跳过确认"
echo " --hls-ram HLS 分片放内存(减小 SD 写入,重启即丢)"
echo " --journal-data 系统日志持久化到 /data默认内存"
echo ""
echo "示例:"
echo " sudo $(basename "$0") install"
echo " sudo $(basename "$0") install --hls-ram"
echo " sudo $(basename "$0") status"
;;
esac