's'
This commit is contained in:
172
scripts/hardware_probe.py
Normal file
172
scripts/hardware_probe.py
Normal file
@@ -0,0 +1,172 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import platform
|
||||
import shutil
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
CONFIG_DIR = ROOT / "config"
|
||||
JSON_PATH = CONFIG_DIR / "hardware-profile.json"
|
||||
ENV_PATH = CONFIG_DIR / "hardware.env"
|
||||
|
||||
|
||||
def run_text(cmd: list[str]) -> str:
|
||||
try:
|
||||
proc = subprocess.run(
|
||||
cmd,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
encoding="utf-8",
|
||||
errors="ignore",
|
||||
timeout=8,
|
||||
check=False,
|
||||
)
|
||||
except (OSError, subprocess.TimeoutExpired):
|
||||
return ""
|
||||
return (proc.stdout or proc.stderr or "").strip()
|
||||
|
||||
|
||||
def detect_ffmpeg_hwaccels() -> list[str]:
|
||||
if not shutil.which("ffmpeg"):
|
||||
return []
|
||||
text = run_text(["ffmpeg", "-hide_banner", "-hwaccels"])
|
||||
accels: list[str] = []
|
||||
for line in text.splitlines():
|
||||
item = line.strip()
|
||||
if not item or item.lower().startswith("hardware acceleration methods"):
|
||||
continue
|
||||
if item not in accels:
|
||||
accels.append(item)
|
||||
return accels
|
||||
|
||||
|
||||
def detect_gpu() -> dict:
|
||||
dri_dir = Path("/dev/dri")
|
||||
render_nodes = sorted(str(path) for path in dri_dir.glob("renderD*")) if dri_dir.is_dir() else []
|
||||
cards = sorted(str(path) for path in dri_dir.glob("card*")) if dri_dir.is_dir() else []
|
||||
driver = ""
|
||||
if cards:
|
||||
card0 = Path(cards[0])
|
||||
driver_link = Path(f"/sys/class/drm/{card0.name}/device/driver")
|
||||
if driver_link.exists():
|
||||
try:
|
||||
driver = driver_link.resolve().name
|
||||
except OSError:
|
||||
driver = driver_link.name
|
||||
return {
|
||||
"present": bool(cards or render_nodes),
|
||||
"cards": cards,
|
||||
"render_nodes": render_nodes,
|
||||
"driver": driver,
|
||||
"vainfo": bool(shutil.which("vainfo")),
|
||||
}
|
||||
|
||||
|
||||
def detect_npu() -> dict:
|
||||
hints = []
|
||||
for path in (
|
||||
Path("/dev/rknpu"),
|
||||
Path("/dev/npu"),
|
||||
Path("/dev/galcore"),
|
||||
Path("/sys/class/misc/rknpu"),
|
||||
Path("/sys/class/misc/verisilicon-npu"),
|
||||
):
|
||||
if path.exists():
|
||||
hints.append(str(path))
|
||||
return {
|
||||
"present": bool(hints),
|
||||
"devices": hints,
|
||||
}
|
||||
|
||||
|
||||
def detect_kernel_features() -> dict:
|
||||
features = {
|
||||
"drm": Path("/dev/dri/card0").exists(),
|
||||
"render_node": any(Path("/dev/dri").glob("renderD*")) if Path("/dev/dri").is_dir() else False,
|
||||
"v4l2loopback": Path("/sys/module/v4l2loopback").exists(),
|
||||
"kvm": Path("/dev/kvm").exists(),
|
||||
"nvidia": shutil.which("nvidia-smi") is not None,
|
||||
}
|
||||
return features
|
||||
|
||||
|
||||
def recommend(profile: dict) -> dict:
|
||||
hwaccels = set(profile["ffmpeg_hwaccels"])
|
||||
gpu = profile["gpu"]
|
||||
browser_hw = "auto" if gpu["present"] else "disabled"
|
||||
decoder = "cpu"
|
||||
if "vaapi" in hwaccels and gpu["render_nodes"]:
|
||||
decoder = "vaapi"
|
||||
elif "v4l2m2m" in hwaccels:
|
||||
decoder = "v4l2m2m"
|
||||
elif "cuda" in hwaccels or "nvdec" in hwaccels:
|
||||
decoder = "cuda"
|
||||
|
||||
hdmi_player = "cpu"
|
||||
if shutil.which("mpv") and gpu["cards"]:
|
||||
hdmi_player = "mpv-drm"
|
||||
elif shutil.which("ffplay") and (os.environ.get("DISPLAY") or shutil.which("startx")):
|
||||
hdmi_player = "ffplay-x11"
|
||||
|
||||
risk = []
|
||||
if not gpu["present"]:
|
||||
risk.append("No DRM/VA render node detected, browser and HDMI playback will stay on conservative settings")
|
||||
if decoder == "cpu":
|
||||
risk.append("No safe hardware decoder detected, ffmpeg workloads will remain on CPU")
|
||||
if hdmi_player == "cpu":
|
||||
risk.append("No direct HDMI playback stack detected, install mpv or X11+ffplay for full-screen output")
|
||||
|
||||
return {
|
||||
"video_decoder": decoder,
|
||||
"browser_hwaccel": browser_hw,
|
||||
"hdmi_player": hdmi_player,
|
||||
"stability_mode": "safe" if risk else "balanced",
|
||||
"degradation_reasons": risk,
|
||||
}
|
||||
|
||||
|
||||
def build_profile() -> dict:
|
||||
profile = {
|
||||
"status": "ok",
|
||||
"arch": platform.machine(),
|
||||
"kernel": platform.release(),
|
||||
"platform": platform.platform(),
|
||||
"ffmpeg_hwaccels": detect_ffmpeg_hwaccels(),
|
||||
"gpu": detect_gpu(),
|
||||
"npu": detect_npu(),
|
||||
"kernel_features": detect_kernel_features(),
|
||||
}
|
||||
profile["recommendation"] = recommend(profile)
|
||||
return profile
|
||||
|
||||
|
||||
def write_outputs(profile: dict) -> None:
|
||||
CONFIG_DIR.mkdir(parents=True, exist_ok=True)
|
||||
JSON_PATH.write_text(json.dumps(profile, indent=2, ensure_ascii=False), encoding="utf-8")
|
||||
env_lines = [
|
||||
f"LIVE_HW_DECODER={profile['recommendation']['video_decoder']}",
|
||||
f"LIVE_HDMI_PLAYER={profile['recommendation']['hdmi_player']}",
|
||||
f"LIVE_BROWSER_HWACCEL={profile['recommendation']['browser_hwaccel']}",
|
||||
f"LIVE_STABILITY_MODE={profile['recommendation']['stability_mode']}",
|
||||
]
|
||||
ENV_PATH.write_text("\n".join(env_lines) + "\n", encoding="utf-8")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description="Probe Linux hardware capabilities for safe runtime defaults")
|
||||
parser.add_argument("--write", action="store_true", help="Write JSON and env files into config/")
|
||||
args = parser.parse_args()
|
||||
|
||||
profile = build_profile()
|
||||
if args.write:
|
||||
write_outputs(profile)
|
||||
print(json.dumps(profile, ensure_ascii=False))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
32
scripts/linux/install_business.sh
Normal file
32
scripts/linux/install_business.sh
Normal file
@@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SELF_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
INSTALL_TAG="business"
|
||||
. "$SELF_DIR/lib/common.sh"
|
||||
|
||||
ensure_root "$@"
|
||||
load_env
|
||||
|
||||
# Keep the business profile focused on the livestream stack.
|
||||
ENABLE_HOMEPAGE=0
|
||||
ENABLE_FILEBROWSER=0
|
||||
ENABLE_COCKPIT=0
|
||||
ENABLE_NETDATA=0
|
||||
|
||||
ensure_live_user
|
||||
install_base_packages
|
||||
install_media_packages
|
||||
configure_adb_udev
|
||||
install_nodejs
|
||||
install_docker_stack
|
||||
install_android_panel
|
||||
prepare_python_runtime
|
||||
prepare_web_console
|
||||
install_live_console_service
|
||||
configure_sudoers
|
||||
configure_mdns
|
||||
install_browser_stack
|
||||
prepare_srs
|
||||
run_hardware_probe
|
||||
print_summary "business"
|
||||
35
scripts/linux/install_hub.sh
Normal file
35
scripts/linux/install_hub.sh
Normal file
@@ -0,0 +1,35 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SELF_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
INSTALL_TAG="hub"
|
||||
. "$SELF_DIR/lib/common.sh"
|
||||
|
||||
ensure_root "$@"
|
||||
load_env
|
||||
|
||||
# Keep the hub profile independent from the livestream business stack.
|
||||
ENABLE_SRS=0
|
||||
|
||||
ensure_live_user
|
||||
install_base_packages
|
||||
install_media_packages
|
||||
configure_adb_udev
|
||||
install_nodejs
|
||||
install_docker_stack
|
||||
install_android_panel
|
||||
prepare_python_runtime
|
||||
prepare_web_console
|
||||
install_live_console_service
|
||||
configure_sudoers
|
||||
configure_mdns
|
||||
configure_wifi_profile
|
||||
install_shellcrash
|
||||
install_browser_stack
|
||||
install_webtty
|
||||
install_cockpit
|
||||
install_netdata
|
||||
prepare_homepage
|
||||
prepare_filebrowser
|
||||
run_hardware_probe
|
||||
print_summary "hub"
|
||||
27
scripts/linux/install_stack.sh
Normal file
27
scripts/linux/install_stack.sh
Normal file
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
PROFILE="${1:-all}"
|
||||
|
||||
case "$PROFILE" in
|
||||
hub|core|sh)
|
||||
exec bash "$ROOT_DIR/scripts/linux/install_hub.sh" "${@:2}"
|
||||
;;
|
||||
business|app|sh2)
|
||||
exec bash "$ROOT_DIR/scripts/linux/install_business.sh" "${@:2}"
|
||||
;;
|
||||
all|full)
|
||||
bash "$ROOT_DIR/scripts/linux/install_hub.sh" "${@:2}"
|
||||
bash "$ROOT_DIR/scripts/linux/install_business.sh" "${@:2}"
|
||||
;;
|
||||
*)
|
||||
cat <<USAGE
|
||||
Usage:
|
||||
scripts/linux/install_stack.sh hub
|
||||
scripts/linux/install_stack.sh business
|
||||
scripts/linux/install_stack.sh all
|
||||
USAGE
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
50
scripts/linux/launch_browser.sh
Normal file
50
scripts/linux/launch_browser.sh
Normal file
@@ -0,0 +1,50 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
CONFIG_DIR="$ROOT_DIR/config"
|
||||
|
||||
[ -f "$CONFIG_DIR/system-stack.env" ] && set -a && . "$CONFIG_DIR/system-stack.env" && set +a
|
||||
[ -f "$CONFIG_DIR/hardware.env" ] && set -a && . "$CONFIG_DIR/hardware.env" && set +a
|
||||
|
||||
browser_bin="$(
|
||||
command -v chromium 2>/dev/null \
|
||||
|| command -v chromium-browser 2>/dev/null \
|
||||
|| command -v google-chrome 2>/dev/null \
|
||||
|| true
|
||||
)"
|
||||
|
||||
if [ -z "$browser_bin" ]; then
|
||||
echo "MESSAGE=Chromium or Google Chrome is not installed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
profile_dir="${BROWSER_PROFILE_DIR:-$HOME/.local/share/live-browser/profile}"
|
||||
start_url="${BROWSER_START_URL:-https://www.tiktok.com/live}"
|
||||
locale="${BROWSER_LOCALE:-zh-CN}"
|
||||
debug_port="${BROWSER_REMOTE_DEBUG_PORT:-9222}"
|
||||
|
||||
mkdir -p "$profile_dir"
|
||||
|
||||
args=(
|
||||
"--user-data-dir=$profile_dir"
|
||||
"--password-store=basic"
|
||||
"--lang=$locale"
|
||||
"--no-first-run"
|
||||
"--restore-last-session"
|
||||
"--remote-debugging-port=$debug_port"
|
||||
"--start-maximized"
|
||||
"--ozone-platform-hint=auto"
|
||||
)
|
||||
|
||||
if [ "${LIVE_BROWSER_HWACCEL:-auto}" = "disabled" ]; then
|
||||
args+=("--disable-gpu")
|
||||
fi
|
||||
|
||||
if [ -n "${BROWSER_EXTRA_FLAGS:-}" ]; then
|
||||
# shellcheck disable=SC2206
|
||||
extra_flags=(${BROWSER_EXTRA_FLAGS})
|
||||
args+=("${extra_flags[@]}")
|
||||
fi
|
||||
|
||||
exec "$browser_bin" "${args[@]}" "$start_url"
|
||||
520
scripts/linux/service_ctl.sh
Normal file
520
scripts/linux/service_ctl.sh
Normal file
@@ -0,0 +1,520 @@
|
||||
#!/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"
|
||||
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}"
|
||||
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;raw-web-scrcpy=http://${HOSTNAME_ALIAS}.local:${ANDROID_PANEL_PORT}"
|
||||
}
|
||||
|
||||
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_android_gateway_status() {
|
||||
if ! have nmcli || [ -z "$ANDROID_DOWNLINK_IF" ]; then
|
||||
kv available 0
|
||||
kv running 0
|
||||
kv status unavailable
|
||||
kv detail "Set ANDROID_DOWNLINK_IF in system-stack.env to enable gateway mode"
|
||||
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
|
||||
5
scripts/linux/webtty_shell.sh
Normal file
5
scripts/linux/webtty_shell.sh
Normal file
@@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
cd "$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
exec bash -l
|
||||
Reference in New Issue
Block a user