414 lines
8.8 KiB
Bash
Executable File
414 lines
8.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
ROOT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)"
|
|
RUN_DIR="$ROOT_DIR/run"
|
|
ENV_FILE="${APP_ENV_FILE:-$ROOT_DIR/.env}"
|
|
|
|
if [ -f "$ENV_FILE" ]; then
|
|
set -a
|
|
# shellcheck disable=SC1090
|
|
. "$ENV_FILE"
|
|
set +a
|
|
fi
|
|
|
|
APP_BIND="${APP_BIND:-0.0.0.0}"
|
|
APP_PORT="${APP_PORT:-2026}"
|
|
TARGETS_FILE="${TARGETS_FILE:-$ROOT_DIR/config/targets.json}"
|
|
YUMI_INTERNAL_HOST="${YUMI_INTERNAL_HOST:-127.0.0.1}"
|
|
YUMI_INTERNAL_PORT="${YUMI_INTERNAL_PORT:-2027}"
|
|
HAIYI_INTERNAL_HOST="${HAIYI_INTERNAL_HOST:-127.0.0.1}"
|
|
HAIYI_INTERNAL_PORT="${HAIYI_INTERNAL_PORT:-2028}"
|
|
GATEWAY_BIND="$APP_BIND"
|
|
GATEWAY_PORT="$APP_PORT"
|
|
|
|
GATEWAY_SYSTEMD_SERVICE_NAME="hy-app-monitor.service"
|
|
YUMI_SYSTEMD_SERVICE_NAME="hy-app-monitor-yumi.service"
|
|
HAIYI_SYSTEMD_SERVICE_NAME="hy-app-monitor-haiyi.service"
|
|
|
|
if [ -x "$ROOT_DIR/.venv/bin/python" ]; then
|
|
PYTHON_BIN_DEFAULT="$ROOT_DIR/.venv/bin/python"
|
|
else
|
|
PYTHON_BIN_DEFAULT="python3"
|
|
fi
|
|
|
|
PYTHON_BIN="${PYTHON_BIN:-$PYTHON_BIN_DEFAULT}"
|
|
|
|
mkdir -p "$RUN_DIR"
|
|
|
|
suite_service_names() {
|
|
printf '%s\n' gateway yumi haiyi
|
|
}
|
|
|
|
service_display_name() {
|
|
case "$1" in
|
|
gateway) echo "gateway" ;;
|
|
yumi) echo "yumi" ;;
|
|
haiyi) echo "haiyi" ;;
|
|
*)
|
|
echo "unknown service: $1" >&2
|
|
return 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
service_unit_name() {
|
|
case "$1" in
|
|
gateway) echo "$GATEWAY_SYSTEMD_SERVICE_NAME" ;;
|
|
yumi) echo "$YUMI_SYSTEMD_SERVICE_NAME" ;;
|
|
haiyi) echo "$HAIYI_SYSTEMD_SERVICE_NAME" ;;
|
|
*)
|
|
echo "unknown service: $1" >&2
|
|
return 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
service_entry_file() {
|
|
case "$1" in
|
|
gateway) echo "$ROOT_DIR/server.py" ;;
|
|
yumi) echo "$ROOT_DIR/yumi_server.py" ;;
|
|
haiyi) echo "$ROOT_DIR/haiyi_server.py" ;;
|
|
*)
|
|
echo "unknown service: $1" >&2
|
|
return 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
service_bind() {
|
|
case "$1" in
|
|
gateway) echo "$GATEWAY_BIND" ;;
|
|
yumi) echo "$YUMI_INTERNAL_HOST" ;;
|
|
haiyi) echo "$HAIYI_INTERNAL_HOST" ;;
|
|
*)
|
|
echo "unknown service: $1" >&2
|
|
return 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
service_port() {
|
|
case "$1" in
|
|
gateway) echo "$GATEWAY_PORT" ;;
|
|
yumi) echo "$YUMI_INTERNAL_PORT" ;;
|
|
haiyi) echo "$HAIYI_INTERNAL_PORT" ;;
|
|
*)
|
|
echo "unknown service: $1" >&2
|
|
return 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
service_base_path() {
|
|
case "$1" in
|
|
gateway) echo "" ;;
|
|
yumi) echo "/yumi" ;;
|
|
haiyi) echo "/haiyi" ;;
|
|
*)
|
|
echo "unknown service: $1" >&2
|
|
return 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
service_pid_file() {
|
|
echo "$RUN_DIR/hy-app-monitor-$1.pid"
|
|
}
|
|
|
|
service_log_file() {
|
|
echo "$RUN_DIR/hy-app-monitor-$1.log"
|
|
}
|
|
|
|
service_command_pattern() {
|
|
basename "$(service_entry_file "$1")"
|
|
}
|
|
|
|
has_systemd_unit() {
|
|
local name="$1"
|
|
if ! command -v systemctl >/dev/null 2>&1; then
|
|
return 1
|
|
fi
|
|
systemctl cat "$(service_unit_name "$name")" >/dev/null 2>&1
|
|
}
|
|
|
|
has_full_systemd_suite() {
|
|
local name
|
|
for name in $(suite_service_names); do
|
|
if ! has_systemd_unit "$name"; then
|
|
return 1
|
|
fi
|
|
done
|
|
return 0
|
|
}
|
|
|
|
systemd_main_pid() {
|
|
local name="$1"
|
|
local main_pid
|
|
|
|
if ! has_systemd_unit "$name"; then
|
|
return 1
|
|
fi
|
|
|
|
main_pid="$(systemctl show --property MainPID --value "$(service_unit_name "$name")" 2>/dev/null || true)"
|
|
if [ -z "${main_pid:-}" ] || [ "$main_pid" = "0" ]; then
|
|
return 1
|
|
fi
|
|
|
|
echo "$main_pid"
|
|
}
|
|
|
|
pid_matches_project() {
|
|
local name="$1"
|
|
local pid="$2"
|
|
if [ -z "${pid:-}" ] || ! kill -0 "$pid" >/dev/null 2>&1; then
|
|
return 1
|
|
fi
|
|
|
|
local command
|
|
command="$(ps -p "$pid" -o command= 2>/dev/null || true)"
|
|
if [[ "$command" != *"$(service_command_pattern "$name")"* ]]; then
|
|
return 1
|
|
fi
|
|
|
|
if command -v lsof >/dev/null 2>&1; then
|
|
local expected_port
|
|
expected_port="$(service_port "$name")"
|
|
if ! lsof -a -p "$pid" -iTCP:"$expected_port" -sTCP:LISTEN >/dev/null 2>&1; then
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
if command -v lsof >/dev/null 2>&1; then
|
|
local cwd
|
|
cwd="$(lsof -a -p "$pid" -d cwd -Fn 2>/dev/null | sed -n 's/^n//p' | head -n 1)"
|
|
if [ -n "$cwd" ] && [ "$cwd" != "$ROOT_DIR" ]; then
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
discover_project_pid() {
|
|
local name="$1"
|
|
if ! command -v lsof >/dev/null 2>&1; then
|
|
return 1
|
|
fi
|
|
|
|
local pid
|
|
while IFS= read -r pid; do
|
|
if pid_matches_project "$name" "$pid"; then
|
|
echo "$pid"
|
|
return 0
|
|
fi
|
|
done < <(lsof -tiTCP:"$(service_port "$name")" -sTCP:LISTEN 2>/dev/null || true)
|
|
|
|
return 1
|
|
}
|
|
|
|
read_pid() {
|
|
local name="$1"
|
|
local pid_file
|
|
pid_file="$(service_pid_file "$name")"
|
|
|
|
if [ -f "$pid_file" ]; then
|
|
local pid
|
|
pid="$(cat "$pid_file")"
|
|
if pid_matches_project "$name" "$pid"; then
|
|
echo "$pid"
|
|
return 0
|
|
fi
|
|
fi
|
|
|
|
discover_project_pid "$name"
|
|
}
|
|
|
|
is_running() {
|
|
local name="$1"
|
|
local pid
|
|
local pid_file
|
|
pid_file="$(service_pid_file "$name")"
|
|
pid="$(read_pid "$name" || true)"
|
|
if [ -z "${pid:-}" ]; then
|
|
rm -f "$pid_file"
|
|
return 1
|
|
fi
|
|
if ! kill -0 "$pid" >/dev/null 2>&1; then
|
|
rm -f "$pid_file"
|
|
return 1
|
|
fi
|
|
|
|
echo "$pid" >"$pid_file"
|
|
return 0
|
|
}
|
|
|
|
stop_legacy_process() {
|
|
local name="$1"
|
|
local pid
|
|
local pid_file
|
|
local managed_pid
|
|
pid_file="$(service_pid_file "$name")"
|
|
pid="$(read_pid "$name" || true)"
|
|
managed_pid="$(systemd_main_pid "$name" || true)"
|
|
|
|
if [ -n "${pid:-}" ] && [ -n "${managed_pid:-}" ] && [ "$pid" = "$managed_pid" ]; then
|
|
rm -f "$pid_file"
|
|
return 0
|
|
fi
|
|
|
|
if [ -n "${pid:-}" ] && kill -0 "$pid" >/dev/null 2>&1; then
|
|
kill "$pid" >/dev/null 2>&1 || true
|
|
local _i
|
|
for _i in $(seq 1 10); do
|
|
if ! kill -0 "$pid" >/dev/null 2>&1; then
|
|
break
|
|
fi
|
|
sleep 1
|
|
done
|
|
kill -9 "$pid" >/dev/null 2>&1 || true
|
|
fi
|
|
|
|
rm -f "$pid_file"
|
|
}
|
|
|
|
stop_all_legacy_processes() {
|
|
local name
|
|
for name in $(suite_service_names); do
|
|
stop_legacy_process "$name"
|
|
done
|
|
}
|
|
|
|
export_service_env() {
|
|
local name="$1"
|
|
export APP_BIND
|
|
export APP_PORT
|
|
export APP_BASE_PATH
|
|
export TARGETS_FILE
|
|
export PYTHONUNBUFFERED=1
|
|
|
|
APP_BIND="$(service_bind "$name")"
|
|
APP_PORT="$(service_port "$name")"
|
|
APP_BASE_PATH="$(service_base_path "$name")"
|
|
export APP_BIND APP_PORT APP_BASE_PATH TARGETS_FILE PYTHONUNBUFFERED
|
|
}
|
|
|
|
start_local_service() {
|
|
local name="$1"
|
|
local pid_file
|
|
local log_file
|
|
local entry_file
|
|
|
|
if is_running "$name"; then
|
|
echo "$(service_display_name "$name") already running pid=$(read_pid "$name")"
|
|
return 0
|
|
fi
|
|
|
|
if [ "$name" = "yumi" ] && [ ! -f "$TARGETS_FILE" ]; then
|
|
echo "targets file not found: $TARGETS_FILE" >&2
|
|
return 1
|
|
fi
|
|
|
|
pid_file="$(service_pid_file "$name")"
|
|
log_file="$(service_log_file "$name")"
|
|
entry_file="$(service_entry_file "$name")"
|
|
|
|
export_service_env "$name"
|
|
|
|
nohup "$PYTHON_BIN" "$entry_file" >>"$log_file" 2>&1 &
|
|
local pid=$!
|
|
echo "$pid" >"$pid_file"
|
|
|
|
sleep 1
|
|
if kill -0 "$pid" >/dev/null 2>&1; then
|
|
echo "$(service_display_name "$name") started pid=$pid port=$(service_port "$name")"
|
|
return 0
|
|
fi
|
|
|
|
echo "$(service_display_name "$name") failed to start" >&2
|
|
tail -n 40 "$log_file" >&2 || true
|
|
return 1
|
|
}
|
|
|
|
stop_local_service() {
|
|
local name="$1"
|
|
local pid_file
|
|
pid_file="$(service_pid_file "$name")"
|
|
|
|
if ! is_running "$name"; then
|
|
echo "$(service_display_name "$name") not running"
|
|
rm -f "$pid_file"
|
|
return 0
|
|
fi
|
|
|
|
local pid
|
|
pid="$(read_pid "$name")"
|
|
kill "$pid" >/dev/null 2>&1 || true
|
|
|
|
local _i
|
|
for _i in $(seq 1 10); do
|
|
if ! kill -0 "$pid" >/dev/null 2>&1; then
|
|
rm -f "$pid_file"
|
|
echo "$(service_display_name "$name") stopped"
|
|
return 0
|
|
fi
|
|
sleep 1
|
|
done
|
|
|
|
kill -9 "$pid" >/dev/null 2>&1 || true
|
|
rm -f "$pid_file"
|
|
echo "$(service_display_name "$name") force stopped"
|
|
}
|
|
|
|
status_local_service() {
|
|
local name="$1"
|
|
if is_running "$name"; then
|
|
echo "$(service_display_name "$name") running pid=$(read_pid "$name") port=$(service_port "$name")"
|
|
return 0
|
|
fi
|
|
|
|
echo "$(service_display_name "$name") stopped"
|
|
return 1
|
|
}
|
|
|
|
start_systemd_suite() {
|
|
local name
|
|
for name in yumi haiyi gateway; do
|
|
systemctl start "$(service_unit_name "$name")"
|
|
done
|
|
for name in $(suite_service_names); do
|
|
systemctl is-active "$(service_unit_name "$name")"
|
|
done
|
|
}
|
|
|
|
stop_systemd_suite() {
|
|
local name
|
|
for name in gateway yumi haiyi; do
|
|
systemctl stop "$(service_unit_name "$name")" >/dev/null 2>&1 || true
|
|
done
|
|
}
|
|
|
|
restart_systemd_suite() {
|
|
local name
|
|
for name in yumi haiyi gateway; do
|
|
systemctl restart "$(service_unit_name "$name")"
|
|
done
|
|
for name in $(suite_service_names); do
|
|
systemctl is-active "$(service_unit_name "$name")"
|
|
done
|
|
}
|
|
|
|
status_systemd_suite() {
|
|
local all_active=0
|
|
local name
|
|
for name in $(suite_service_names); do
|
|
if systemctl is-active --quiet "$(service_unit_name "$name")"; then
|
|
echo "$(service_display_name "$name") running under systemd port=$(service_port "$name")"
|
|
else
|
|
echo "$(service_display_name "$name") stopped"
|
|
all_active=1
|
|
fi
|
|
done
|
|
return "$all_active"
|
|
}
|