Files
gitlab-instance-0a899031_do…/scripts/linux/service_ctl.sh
eric 92ecc14e43 s'
2026-03-28 14:16:42 -05:00

619 lines
18 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 -euo pipefail
SERVICE_ID="${1:-}"
ACTION="${2:-status}"
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
CONFIG_DIR="$ROOT_DIR/config"
STACK_ENV="$CONFIG_DIR/system-stack.env"
HW_ENV="$CONFIG_DIR/hardware.env"
SRS_COMPOSE="$ROOT_DIR/services/srs/docker-compose.yml"
HOMEPAGE_COMPOSE="$ROOT_DIR/services/homepage/docker-compose.yml"
FILEBROWSER_COMPOSE="$ROOT_DIR/services/filebrowser/docker-compose.yml"
NEKO_COMPOSE="$ROOT_DIR/services/neko/docker-compose.yml"
BROWSER_LAUNCHER="$ROOT_DIR/scripts/linux/launch_browser.sh"
[ -f "$STACK_ENV" ] && set -a && . "$STACK_ENV" && set +a
[ -f "$HW_ENV" ] && set -a && . "$HW_ENV" && set +a
HOSTNAME_ALIAS="${HOSTNAME_ALIAS:-live}"
PORT="${PORT:-8001}"
WEBTTY_PORT="${WEBTTY_PORT:-7681}"
HOMEPAGE_PORT="${HOMEPAGE_PORT:-80}"
FILEBROWSER_PORT="${FILEBROWSER_PORT:-8082}"
NETDATA_PORT="${NETDATA_PORT:-19999}"
SRS_HTTP_PORT="${SRS_HTTP_PORT:-8080}"
SRS_RTMP_PORT="${SRS_RTMP_PORT:-1935}"
SRS_API_PORT="${SRS_API_PORT:-1985}"
SRS_SRT_PORT="${SRS_SRT_PORT:-10080}"
SRS_WEBRTC_PORT="${SRS_WEBRTC_PORT:-8088}"
SHELLCRASH_DIR="${SHELLCRASH_DIR:-/etc/ShellCrash}"
WEBTTY_USER="${WEBTTY_USER:-live}"
WEBTTY_PASS="${WEBTTY_PASS:-12345678}"
FILEBROWSER_USER="${FILEBROWSER_USER:-live}"
FILEBROWSER_PASS="${FILEBROWSER_PASS:-12345678}"
WIFI_CONNECTION_NAME="${WIFI_CONNECTION_NAME:-live-wifi}"
WIFI_SSID="${WIFI_SSID:-live}"
ANDROID_GATEWAY_NAME="${ANDROID_GATEWAY_NAME:-live-android-gateway}"
ANDROID_UPLINK_IF="${ANDROID_UPLINK_IF:-eth0}"
ANDROID_DOWNLINK_IF="${ANDROID_DOWNLINK_IF:-}"
ANDROID_GATEWAY_CIDR="${ANDROID_GATEWAY_CIDR:-192.168.51.1/24}"
ANDROID_PANEL_PORT="${ANDROID_PANEL_PORT:-5000}"
NEKO_HTTP_PORT="${NEKO_HTTP_PORT:-9200}"
CONSOLE_BASE_URL="http://${HOSTNAME_ALIAS}.local:${PORT}"
kv() {
printf '%s=%s\n' "$1" "$2"
}
have() {
command -v "$1" >/dev/null 2>&1
}
filtered_pgrep() {
local pattern="$1"
pgrep -fa "$pattern" 2>/dev/null | grep -v -F "$0" | grep -v -F "pgrep -fa" || true
}
unit_exists() {
systemctl list-unit-files "$1" --no-legend 2>/dev/null | grep -q "^$1"
}
unit_active() {
systemctl is-active --quiet "$1"
}
compose_bin() {
if have docker && docker compose version >/dev/null 2>&1; then
echo "docker compose"
return 0
fi
if have docker-compose; then
echo "docker-compose"
return 0
fi
return 1
}
compose_run() {
local compose_file="$1"
shift
local cmd
cmd="$(compose_bin)" || return 127
if [ "$cmd" = "docker compose" ]; then
docker compose -f "$compose_file" "$@"
else
docker-compose -f "$compose_file" "$@"
fi
}
docker_container_status() {
local name="$1"
if ! have docker; then
return 1
fi
docker ps -a --filter "name=^/${name}$" --format '{{.State}}|{{.Status}}' | head -n 1
}
service_mdns_status() {
local detail hostname ipv6
hostname="$(hostname)"
ipv6="unknown"
if grep -Eq '^[[:space:]]*use-ipv6=no' /etc/avahi/avahi-daemon.conf 2>/dev/null; then
ipv6="disabled"
fi
detail="hostname=${hostname}; avahi_ipv6=${ipv6}"
kv available 1
kv running "$(unit_active avahi-daemon && echo 1 || echo 0)"
kv status "$(unit_active avahi-daemon && echo online || echo stopped)"
kv host "${HOSTNAME_ALIAS}.local"
kv port 22
kv url "ssh ${WEBTTY_USER}@${HOSTNAME_ALIAS}.local"
kv detail "$detail"
}
service_mdns_start() {
hostnamectl set-hostname "$HOSTNAME_ALIAS"
systemctl restart avahi-daemon
kv message "mDNS hostname refreshed"
}
service_mdns_stop() {
systemctl stop avahi-daemon
kv message "avahi-daemon stopped"
}
service_wifi_status() {
if ! have nmcli || [ ! -d /sys/class/net/wlan0 ]; then
kv available 0
kv running 0
kv status unavailable
kv detail "NetworkManager or wlan0 is not available"
return 0
fi
local active detail
active="$(nmcli -t -f NAME,DEVICE connection show --active 2>/dev/null | grep "^${WIFI_CONNECTION_NAME}:" || true)"
detail="$(nmcli -t -f NAME,UUID,TYPE,AUTOCONNECT connection show "$WIFI_CONNECTION_NAME" 2>/dev/null | paste -sd ';' - || true)"
kv available 1
kv running "$([ -n "$active" ] && echo 1 || echo 0)"
kv status "$([ -n "$active" ] && echo online || echo configured)"
kv host "${HOSTNAME_ALIAS}.local"
kv port ""
kv url ""
kv detail "${detail:-ssid=${WIFI_SSID}}"
}
service_wifi_start() {
if ! have nmcli || [ ! -d /sys/class/net/wlan0 ]; then
kv message "Wi-Fi interface is not available"
exit 1
fi
if ! nmcli connection show "$WIFI_CONNECTION_NAME" >/dev/null 2>&1; then
nmcli connection add type wifi ifname wlan0 con-name "$WIFI_CONNECTION_NAME" ssid "$WIFI_SSID" \
wifi-sec.key-mgmt wpa-psk wifi-sec.psk "${WIFI_PSK:-12345678}" ipv4.method auto connection.autoconnect yes
fi
nmcli connection up "$WIFI_CONNECTION_NAME" >/dev/null 2>&1 || true
kv message "Wi-Fi profile ensured"
}
service_wifi_stop() {
nmcli connection down "$WIFI_CONNECTION_NAME" >/dev/null 2>&1 || true
kv message "Wi-Fi profile disconnected"
}
service_shellcrash_status() {
local running detail
if [ ! -x "$SHELLCRASH_DIR/start.sh" ] && ! unit_exists shellcrash.service; then
kv available 0
kv running 0
kv status unavailable
kv detail "ShellCrash is not installed"
return 0
fi
running=0
detail="$(filtered_pgrep 'CrashCore|shellcrash' | tr '\n' ';' || true)"
if unit_active shellcrash.service || [ -n "$detail" ]; then
running=1
fi
[ -z "$detail" ] && detail="dir=$SHELLCRASH_DIR"
kv available 1
kv running "$running"
kv status "$([ "$running" = 1 ] && echo online || echo stopped)"
kv url "${CONSOLE_BASE_URL}/shellcrash"
kv host "${HOSTNAME_ALIAS}.local"
kv port ""
kv detail "$detail"
}
service_shellcrash_start() {
if unit_exists shellcrash.service; then
systemctl enable --now shellcrash.service
elif [ -x "$SHELLCRASH_DIR/start.sh" ]; then
"$SHELLCRASH_DIR/start.sh" start
else
kv message "ShellCrash is not installed"
exit 1
fi
kv message "ShellCrash started"
}
service_shellcrash_stop() {
if unit_exists shellcrash.service; then
systemctl stop shellcrash.service
elif [ -x "$SHELLCRASH_DIR/start.sh" ]; then
"$SHELLCRASH_DIR/start.sh" stop
fi
kv message "ShellCrash stopped"
}
service_srs_status() {
if ! have docker || [ ! -f "$SRS_COMPOSE" ]; then
kv available 0
kv running 0
kv status unavailable
kv detail "Docker or SRS compose file is missing"
return 0
fi
local state
state="$(docker_container_status live-srs || true)"
kv available 1
kv running "$([[ "$state" == running* ]] && echo 1 || echo 0)"
kv status "$([[ "$state" == running* ]] && echo online || echo stopped)"
kv host "${HOSTNAME_ALIAS}.local"
kv port "$SRS_HTTP_PORT"
kv url "http://${HOSTNAME_ALIAS}.local:${SRS_HTTP_PORT}"
kv detail "${state:-container=missing};rtmp=${SRS_RTMP_PORT};api=${SRS_API_PORT};srt=${SRS_SRT_PORT};rtc=${SRS_WEBRTC_PORT}"
}
service_srs_start() {
compose_run "$SRS_COMPOSE" up -d
kv message "SRS started"
}
service_srs_stop() {
compose_run "$SRS_COMPOSE" down
kv message "SRS stopped"
}
service_webtty_status() {
if ! unit_exists live-webtty.service && ! have ttyd; then
kv available 0
kv running 0
kv status unavailable
kv detail "ttyd is not installed"
return 0
fi
kv available 1
kv running "$(unit_active live-webtty.service && echo 1 || echo 0)"
kv status "$(unit_active live-webtty.service && echo online || echo stopped)"
kv host "${HOSTNAME_ALIAS}.local"
kv port "$WEBTTY_PORT"
kv url "http://${HOSTNAME_ALIAS}.local:${WEBTTY_PORT}"
kv detail "web user=${WEBTTY_USER}"
}
service_webtty_start() {
systemctl enable --now live-webtty.service
kv message "WebTTY started"
}
service_webtty_stop() {
systemctl stop live-webtty.service
kv message "WebTTY stopped"
}
service_cockpit_status() {
if ! unit_exists cockpit.socket; then
kv available 0
kv running 0
kv status unavailable
kv detail "Cockpit is not installed"
return 0
fi
kv available 1
kv running "$(unit_active cockpit.socket && echo 1 || echo 0)"
kv status "$(unit_active cockpit.socket && echo online || echo stopped)"
kv host "${HOSTNAME_ALIAS}.local"
kv port 9090
kv url "https://${HOSTNAME_ALIAS}.local:9090"
kv detail "login with live or any enabled system account"
}
service_cockpit_start() {
systemctl enable --now cockpit.socket
kv message "Cockpit started"
}
service_cockpit_stop() {
systemctl stop cockpit.socket
kv message "Cockpit stopped"
}
service_filebrowser_status() {
if ! have docker || [ ! -f "$FILEBROWSER_COMPOSE" ]; then
kv available 0
kv running 0
kv status unavailable
kv detail "Docker or File Browser compose file is missing"
return 0
fi
local state
state="$(docker_container_status live-filebrowser || true)"
kv available 1
kv running "$([[ "$state" == running* ]] && echo 1 || echo 0)"
kv status "$([[ "$state" == running* ]] && echo online || echo stopped)"
kv host "${HOSTNAME_ALIAS}.local"
kv port "$FILEBROWSER_PORT"
kv url "http://${HOSTNAME_ALIAS}.local:${FILEBROWSER_PORT}"
kv detail "${state:-container=missing};user=${FILEBROWSER_USER:-live}"
}
service_filebrowser_start() {
compose_run "$FILEBROWSER_COMPOSE" up -d
kv message "File Browser started"
}
service_filebrowser_stop() {
compose_run "$FILEBROWSER_COMPOSE" down
kv message "File Browser stopped"
}
service_homepage_status() {
if ! have docker || [ ! -f "$HOMEPAGE_COMPOSE" ]; then
kv available 0
kv running 0
kv status unavailable
kv detail "Docker or Homepage compose file is missing"
return 0
fi
local state
state="$(docker_container_status live-homepage || true)"
kv available 1
kv running "$([[ "$state" == running* ]] && echo 1 || echo 0)"
kv status "$([[ "$state" == running* ]] && echo online || echo stopped)"
kv host "${HOSTNAME_ALIAS}.local"
kv port "$HOMEPAGE_PORT"
kv url "$([ "$HOMEPAGE_PORT" = 80 ] && echo "http://${HOSTNAME_ALIAS}.local" || echo "http://${HOSTNAME_ALIAS}.local:${HOMEPAGE_PORT}")"
kv detail "${state:-container=missing}"
}
service_homepage_start() {
compose_run "$HOMEPAGE_COMPOSE" up -d
kv message "Homepage started"
}
service_homepage_stop() {
compose_run "$HOMEPAGE_COMPOSE" down
kv message "Homepage stopped"
}
service_netdata_status() {
if ! unit_exists netdata.service; then
kv available 0
kv running 0
kv status unavailable
kv detail "Netdata is not installed"
return 0
fi
kv available 1
kv running "$(unit_active netdata.service && echo 1 || echo 0)"
kv status "$(unit_active netdata.service && echo online || echo stopped)"
kv host "${HOSTNAME_ALIAS}.local"
kv port "$NETDATA_PORT"
kv url "http://${HOSTNAME_ALIAS}.local:${NETDATA_PORT}"
kv detail "metrics and alarms API available at /api/v1"
}
service_netdata_start() {
systemctl enable --now netdata
kv message "Netdata started"
}
service_netdata_stop() {
systemctl stop netdata
kv message "Netdata stopped"
}
service_adb_status() {
if ! have adb; then
kv available 0
kv running 0
kv status unavailable
kv detail "adb is not installed"
return 0
fi
local details
details="$(adb devices -l 2>/dev/null | tr '\n' ';' || true)"
kv available 1
kv running "$(filtered_pgrep 'adb -L tcp:5037|[ /]adb([ ]|$)' >/dev/null 2>&1 && echo 1 || echo 0)"
kv status "$(filtered_pgrep 'adb -L tcp:5037|[ /]adb([ ]|$)' >/dev/null 2>&1 && echo online || echo idle)"
kv host "${HOSTNAME_ALIAS}.local"
kv port ""
kv url ""
kv detail "${details:-adb server ready}"
}
service_adb_start() {
adb start-server >/dev/null
kv message "ADB server started"
}
service_adb_stop() {
adb kill-server >/dev/null 2>&1 || true
kv message "ADB server stopped"
}
service_android_panel_status() {
if ! unit_exists live-android-panel.service; then
kv available 0
kv running 0
kv status unavailable
kv detail "Android web panel is not installed"
return 0
fi
kv available 1
kv running "$(unit_active live-android-panel.service && echo 1 || echo 0)"
kv status "$(unit_active live-android-panel.service && echo online || echo stopped)"
kv host "${HOSTNAME_ALIAS}.local"
kv port "$ANDROID_PANEL_PORT"
kv url "${CONSOLE_BASE_URL}/android"
kv detail "android-center=${CONSOLE_BASE_URL}/android;ws-scrcpy=http://${HOSTNAME_ALIAS}.local:${ANDROID_PANEL_PORT};git=ANDROID_PANEL_GIT_URL"
}
service_android_panel_start() {
systemctl enable --now live-android-panel.service
kv message "Android web panel started"
}
service_android_panel_stop() {
systemctl stop live-android-panel.service
kv message "Android web panel stopped"
}
service_chromium_status() {
if ! have chromium && ! have chromium-browser && ! have google-chrome; then
kv available 0
kv running 0
kv status unavailable
kv detail "Chromium or Google Chrome is not installed"
return 0
fi
local details
details="$(filtered_pgrep 'chromium|chrome' | tr '\n' ';' || true)"
kv available 1
kv running "$([ -n "$details" ] && echo 1 || echo 0)"
kv status "$([ -n "$details" ] && echo online || echo stopped)"
kv host "${HOSTNAME_ALIAS}.local"
kv port "${BROWSER_REMOTE_DEBUG_PORT:-9222}"
kv url ""
kv detail "${details:-profile=${BROWSER_PROFILE_DIR:-$HOME/.local/share/live-browser/profile}}"
}
service_chromium_start() {
nohup "$BROWSER_LAUNCHER" >/tmp/live-browser.log 2>&1 &
disown || true
kv message "Chromium launched"
}
service_chromium_stop() {
pkill -f 'chromium|chrome' || true
kv message "Chromium stopped"
}
service_neko_status() {
if [ "${ENABLE_NEKO:-0}" != "1" ]; then
kv available 0
kv running 0
kv status unavailable
kv detail "在 config/system-stack.env 设置 ENABLE_NEKO=1 后执行安装脚本或在此 Start 拉取容器"
return 0
fi
if [ ! -f "$NEKO_COMPOSE" ]; then
kv available 0
kv running 0
kv status unavailable
kv detail "services/neko/docker-compose.yml missing"
return 0
fi
if ! have docker; then
kv available 0
kv running 0
kv status unavailable
kv detail "docker not installed"
return 0
fi
kv available 1
local state
state="$(docker_container_status live-neko-chromium || true)"
kv running "$(echo "$state" | grep -qi running && echo 1 || echo 0)"
kv status "$(echo "$state" | grep -qi running && echo online || echo stopped)"
kv host "${HOSTNAME_ALIAS}.local"
kv port "$NEKO_HTTP_PORT"
kv url "http://${HOSTNAME_ALIAS}.local:${NEKO_HTTP_PORT}"
kv detail "m1k1o/neko Chromium in Docker; ENABLE_NEKO=1 + install_neko_browser or Modules start"
}
service_neko_start() {
if [ "${ENABLE_NEKO:-0}" != "1" ]; then
kv message "请先在 config/system-stack.env 设置 ENABLE_NEKO=1"
exit 1
fi
if [ ! -f "$NEKO_COMPOSE" ]; then
kv message "Neko compose missing"
return 0
fi
local neko_dir
neko_dir="$(cd "$(dirname "$NEKO_COMPOSE")" && pwd)"
local prof="${NEKO_PROFILE_HOST_DIR:-$ROOT_DIR/services/neko/data/chromium-profile}"
mkdir -p "$prof"
chmod 777 "$prof" 2>/dev/null || true
export NEKO_HTTP_PORT="${NEKO_HTTP_PORT:-9200}"
export NEKO_USER_PASS="${NEKO_USER_PASS:-live}"
export NEKO_ADMIN_PASS="${NEKO_ADMIN_PASS:-admin}"
export NEKO_PROFILE_HOST_DIR="$prof"
export NEKO_DESKTOP_SCREEN="${NEKO_DESKTOP_SCREEN:-1920x1080@30}"
export NEKO_WEBRTC_EPR="${NEKO_WEBRTC_EPR:-52000-52100}"
export NEKO_WEBRTC_UDP_RANGE="${NEKO_WEBRTC_UDP_RANGE:-52000-52100}"
export NEKO_WEBRTC_ICELITE="${NEKO_WEBRTC_ICELITE:-1}"
if [ -z "${NEKO_WEBRTC_NAT1TO1:-}" ]; then
export NEKO_WEBRTC_NAT1TO1="$(hostname -I 2>/dev/null | awk '{print $1}')"
fi
export NEKO_PLUGINS_ENABLED="${NEKO_PLUGINS_ENABLED:-true}"
cmd="$(compose_bin)" || {
kv message "docker compose unavailable"
return 0
}
(
cd "$neko_dir" || exit 1
if [ "$cmd" = "docker compose" ]; then
docker compose -f docker-compose.yml up -d
else
docker-compose -f docker-compose.yml up -d
fi
)
kv message "Neko stack started"
}
service_neko_stop() {
if [ ! -f "$NEKO_COMPOSE" ]; then
kv message "Neko compose missing"
return 0
fi
local neko_dir
neko_dir="$(cd "$(dirname "$NEKO_COMPOSE")" && pwd)"
cmd="$(compose_bin)" || {
kv message "docker compose unavailable"
return 0
}
(
cd "$neko_dir" || exit 1
if [ "$cmd" = "docker compose" ]; then
docker compose -f docker-compose.yml down
else
docker-compose -f docker-compose.yml down
fi
)
kv message "Neko stack stopped"
}
service_android_gateway_status() {
if ! have nmcli || [ -z "$ANDROID_DOWNLINK_IF" ]; then
kv available 0
kv running 0
kv status unavailable
kv detail "透明网关需第二块网卡:在 system-stack.env 设置 ANDROID_DOWNLINK_IF如 eth1并安装 NetworkManager(nmcli)"
return 0
fi
if [ ! -d "/sys/class/net/$ANDROID_DOWNLINK_IF" ]; then
kv available 0
kv running 0
kv status unavailable
kv detail "Downlink interface $ANDROID_DOWNLINK_IF is missing"
return 0
fi
local active
active="$(nmcli -t -f NAME connection show --active 2>/dev/null | grep "^${ANDROID_GATEWAY_NAME}$" || true)"
kv available 1
kv running "$([ -n "$active" ] && echo 1 || echo 0)"
kv status "$([ -n "$active" ] && echo online || echo configured)"
kv host "${HOSTNAME_ALIAS}.local"
kv port ""
kv url ""
kv detail "uplink=${ANDROID_UPLINK_IF};downlink=${ANDROID_DOWNLINK_IF};gateway=${ANDROID_GATEWAY_CIDR}"
}
service_android_gateway_start() {
if [ -z "$ANDROID_DOWNLINK_IF" ]; then
kv message "ANDROID_DOWNLINK_IF is not configured"
exit 1
fi
if ! nmcli connection show "$ANDROID_GATEWAY_NAME" >/dev/null 2>&1; then
nmcli connection add type ethernet ifname "$ANDROID_DOWNLINK_IF" con-name "$ANDROID_GATEWAY_NAME" \
ipv4.method shared ipv4.addresses "$ANDROID_GATEWAY_CIDR" connection.autoconnect yes
fi
nmcli connection up "$ANDROID_GATEWAY_NAME"
kv message "Android gateway started"
}
service_android_gateway_stop() {
nmcli connection down "$ANDROID_GATEWAY_NAME" >/dev/null 2>&1 || true
kv message "Android gateway stopped"
}
dispatch() {
local fn="service_${SERVICE_ID}_${ACTION}"
if ! declare -F "$fn" >/dev/null 2>&1; then
echo "MESSAGE=Unsupported service/action: ${SERVICE_ID}/${ACTION}"
exit 1
fi
"$fn"
}
if [ -z "$SERVICE_ID" ]; then
echo "MESSAGE=Missing service id"
exit 1
fi
dispatch