summaryrefslogtreecommitdiff
path: root/apps/startup-scripts/src/service-manager.sh
diff options
context:
space:
mode:
Diffstat (limited to 'apps/startup-scripts/src/service-manager.sh')
-rwxr-xr-xapps/startup-scripts/src/service-manager.sh825
1 files changed, 757 insertions, 68 deletions
diff --git a/apps/startup-scripts/src/service-manager.sh b/apps/startup-scripts/src/service-manager.sh
index ccc4e8e359..4f597fbebe 100755
--- a/apps/startup-scripts/src/service-manager.sh
+++ b/apps/startup-scripts/src/service-manager.sh
@@ -16,6 +16,19 @@ ROOT_DIR="$(cd "$CURRENT_PATH/../../.." && pwd)"
# Configuration directory (can be overridden with AC_SERVICE_CONFIG_DIR)
CONFIG_DIR="${AC_SERVICE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/azerothcore/services}"
REGISTRY_FILE="$CONFIG_DIR/service_registry.json"
+export AC_SERVICE_CONFIG_DIR="${AC_SERVICE_CONFIG_DIR:-$CONFIG_DIR}"
+
+# Default values for variables that might be loaded from config files
+# This prevents "unbound variable" errors when sourcing configuration files
+RUN_ENGINE_CONFIG_FILE="${RUN_ENGINE_CONFIG_FILE:-}"
+SESSION_MANAGER="${SESSION_MANAGER:-}"
+SESSION_NAME="${SESSION_NAME:-}"
+BINPATH="${BINPATH:-}"
+SERVERBIN="${SERVERBIN:-}"
+CONFIG="${CONFIG:-}"
+RESTART_POLICY="${RESTART_POLICY:-}"
+GDB_ENABLED="${GDB_ENABLED:-}"
+SERVER_CONFIG="${SERVER_CONFIG:-}"
# Colors for output
readonly YELLOW='\033[1;33m'
@@ -32,6 +45,114 @@ if [ ! -f "$REGISTRY_FILE" ]; then
echo "[]" > "$REGISTRY_FILE"
fi
+# Path conversion utilities for portability
+# When AC_SERVICE_CONFIG_DIR (hence CONFIG_DIR) is set, always store paths
+# relative to it. Resolve relative paths back against CONFIG_DIR.
+function make_path_relative() {
+ local input="$1"
+
+ # Pass through empty or non-absolute inputs
+ if [ -z "$input" ] || [[ ! "$input" = /* ]]; then
+ echo "$input"
+ return
+ fi
+
+ # If AC_SERVICE_CONFIG_DIR is explicitly set, check if path is under it
+ if [ -n "${AC_SERVICE_CONFIG_DIR:-}" ]; then
+ local config_dir_abs
+ config_dir_abs="$(realpath -m "$AC_SERVICE_CONFIG_DIR" 2>/dev/null || echo "$AC_SERVICE_CONFIG_DIR")"
+ local rel_path=""
+
+ if command -v realpath >/dev/null 2>&1; then
+ rel_path="$(realpath --relative-to="$config_dir_abs" "$input" 2>/dev/null || true)"
+ if [ -z "$rel_path" ]; then
+ rel_path="$(realpath -m --relative-to="$config_dir_abs" "$input" 2>/dev/null || true)"
+ fi
+ fi
+
+ if [ -z "$rel_path" ] && command -v python3 >/dev/null 2>&1; then
+ rel_path="$(python3 -c 'import os,sys; print(os.path.relpath(sys.argv[1], sys.argv[2]))' "$input" "$config_dir_abs" 2>/dev/null || true)"
+ fi
+
+ if [ -n "$rel_path" ]; then
+ echo "$rel_path"
+ return
+ fi
+ fi
+
+ # If not under AC_SERVICE_CONFIG_DIR or AC_SERVICE_CONFIG_DIR not set, return absolute path unchanged
+ echo "$input"
+}
+
+function make_path_absolute() {
+ local input="$1"
+
+ # Already absolute
+ if [[ "$input" = /* ]]; then
+ echo "$input"
+ return
+ fi
+
+ # Resolve relative paths against AC_SERVICE_CONFIG_DIR when explicitly set
+ if [ -n "${AC_SERVICE_CONFIG_DIR:-}" ] && [ -n "$input" ]; then
+ local config_dir_abs
+ config_dir_abs="$(realpath "$AC_SERVICE_CONFIG_DIR" 2>/dev/null || echo "$AC_SERVICE_CONFIG_DIR")"
+ # Join and normalize; do not require the target to exist
+ realpath -m "$config_dir_abs/$input" 2>/dev/null || echo "$config_dir_abs/$input"
+ return
+ fi
+
+ # Fallback: try to normalize relative to current directory
+ realpath -m "$input" 2>/dev/null || echo "$input"
+}
+
+# Tokenize a shell command string without executing it. Supports basic quoting
+# and escaping rules so paths with spaces remain intact.
+# Serialize a command definition (binary + args) to JSON while converting paths
+# to be relative to CONFIG_DIR when possible.
+function serialize_exec_definition() {
+ local command_path="$1"
+ shift
+ local -a args=("$@")
+ local rel_command="$command_path"
+
+ if [[ -n "$command_path" ]]; then
+ rel_command="$(make_path_relative "$command_path")"
+ fi
+
+ local -a rel_args=()
+ local arg
+ for arg in "${args[@]}"; do
+ if [[ "$arg" == /* ]]; then
+ rel_args+=("$(make_path_relative "$arg")")
+ else
+ rel_args+=("$arg")
+ fi
+ done
+
+ local args_json
+ args_json=$(printf '%s\0' "${rel_args[@]}" | jq -R -s 'split("\u0000")[:-1]')
+
+ jq -n --arg command "$rel_command" --argjson args "$args_json" '{command: $command, args: $args}'
+}
+
+# Combine command + args into a shell-safe string suitable for pm2/systemd
+# ExecStart lines. Each token is bash-quoted to preserve spaces.
+function render_exec_command() {
+ local command_path="$1"
+ shift
+ local -a args=("$@")
+ local parts=()
+ local token
+
+ parts+=("$(printf '%q' "$command_path")")
+ for token in "${args[@]}"; do
+ parts+=("$(printf '%q' "$token")")
+ done
+
+ (IFS=' '; printf '%s' "${parts[*]}")
+}
+
# Check dependencies
check_dependencies() {
command -v jq >/dev/null 2>&1 || {
@@ -53,6 +174,18 @@ function add_service_to_registry() {
local gdb_enabled="$9"
local pm2_opts="${10}"
local server_config="${11}"
+ local exec_definition="${12:-}" # JSON payload describing command + args
+
+ # Convert paths to relative if possible for portability
+ local relative_bin_path="$(make_path_relative "$bin_path")"
+
+ # Convert server_config to relative if possible for portability
+ local relative_server_config=""
+ if [ -n "$server_config" ] && [ "$server_config" != "null" ]; then
+ relative_server_config="$(make_path_relative "$server_config")"
+ else
+ relative_server_config="$server_config"
+ fi
# Remove any existing entry with the same service name to avoid duplicates
local tmp_file
@@ -61,10 +194,14 @@ function add_service_to_registry() {
# Add the new entry to the registry
tmp_file=$(mktemp)
+ local exec_json_payload="null"
+ if [ -n "$exec_definition" ]; then
+ exec_json_payload="$exec_definition"
+ fi
jq --arg name "$service_name" \
--arg provider "$provider" \
--arg type "$service_type" \
- --arg bin_path "$bin_path" \
+ --arg bin_path "$relative_bin_path" \
--arg args "$args" \
--arg created "$(date -Iseconds)" \
--arg systemd_type "$systemd_type" \
@@ -72,8 +209,9 @@ function add_service_to_registry() {
--arg session_manager "$session_manager" \
--arg gdb_enabled "$gdb_enabled" \
--arg pm2_opts "$pm2_opts" \
- --arg server_config "$server_config" \
- '. += [{"name": $name, "provider": $provider, "type": $type, "bin_path": $bin_path, "args": $args, "created": $created, "status": "active", "systemd_type": $systemd_type, "restart_policy": $restart_policy, "session_manager": $session_manager, "gdb_enabled": $gdb_enabled, "pm2_opts": $pm2_opts, "server_config": $server_config}]' \
+ --arg server_config "$relative_server_config" \
+ --argjson exec "$exec_json_payload" \
+ '. += [{"name": $name, "provider": $provider, "type": $type, "bin_path": $bin_path, "exec": $exec, "args": $args, "created": $created, "status": "active", "systemd_type": $systemd_type, "restart_policy": $restart_policy, "session_manager": $session_manager, "gdb_enabled": $gdb_enabled, "pm2_opts": $pm2_opts, "server_config": $server_config}]' \
"$REGISTRY_FILE" > "$tmp_file" && mv "$tmp_file" "$REGISTRY_FILE"
echo -e "${GREEN}Service '$service_name' added to registry${NC}"
@@ -117,7 +255,7 @@ function restore_missing_services() {
local name=$(echo "$service" | jq -r '.name')
local provider=$(echo "$service" | jq -r '.provider')
local service_type=$(echo "$service" | jq -r '.type')
- local bin_path=$(echo "$service" | jq -r '.bin_path // "unknown"')
+ local bin_path_raw=$(echo "$service" | jq -r '.bin_path // "unknown"')
local args=$(echo "$service" | jq -r '.args // ""')
local status=$(echo "$service" | jq -r '.status // "active"')
local systemd_type=$(echo "$service" | jq -r '.systemd_type // "--user"')
@@ -125,11 +263,23 @@ function restore_missing_services() {
local session_manager=$(echo "$service" | jq -r '.session_manager // "none"')
local gdb_enabled=$(echo "$service" | jq -r '.gdb_enabled // "0"')
local pm2_opts=$(echo "$service" | jq -r '.pm2_opts // ""')
- local server_config=$(echo "$service" | jq -r '.server_config // ""')
+ local server_config_raw=$(echo "$service" | jq -r '.server_config // ""')
+
+ # Convert paths back to absolute for operation
+ local bin_path="$(make_path_absolute "$bin_path_raw")"
+ local server_config=""
+ if [ -n "$server_config_raw" ] && [ "$server_config_raw" != "null" ] && [ "$server_config_raw" != "" ]; then
+ server_config="$(make_path_absolute "$server_config_raw")"
+ else
+ server_config="$server_config_raw"
+ fi
local service_exists=false
if [ "$provider" = "pm2" ]; then
+ echo "Check if PM2 is installed..."
+ check_pm2 || { echo -e "${RED}PM2 is not installed. Cannot check service status.${NC}"; exit 1; }
+
if pm2 describe "$name" >/dev/null 2>&1; then
service_exists=true
fi
@@ -166,14 +316,61 @@ function restore_missing_services() {
local name=$(echo "$service" | jq -r '.name')
local provider=$(echo "$service" | jq -r '.provider')
local service_type=$(echo "$service" | jq -r '.type')
- local bin_path=$(echo "$service" | jq -r '.bin_path')
- local args=$(echo "$service" | jq -r '.args')
local systemd_type=$(echo "$service" | jq -r '.systemd_type // "--user"')
local restart_policy=$(echo "$service" | jq -r '.restart_policy // "always"')
local session_manager=$(echo "$service" | jq -r '.session_manager // "none"')
local gdb_enabled=$(echo "$service" | jq -r '.gdb_enabled // "0"')
local pm2_opts=$(echo "$service" | jq -r '.pm2_opts // ""')
- local server_config=$(echo "$service" | jq -r '.server_config // ""')
+ local status=$(echo "$service" | jq -r '.status // "active"')
+ local service_resolved="$(get_service_info_resolved "$name")"
+ local bin_path="$(echo "$service_resolved" | jq -r '.bin_path // "unknown"')"
+ local server_config="$(echo "$service_resolved" | jq -r '.server_config // ""')"
+ if [ "$server_config" = "null" ]; then
+ server_config=""
+ fi
+ local exec_definition_raw="$(echo "$service" | jq -c '.exec // null')"
+ local exec_command_abs="$(echo "$service_resolved" | jq -r '.exec.command // ""')"
+ local -a exec_args_abs=()
+ if [ -n "$exec_definition_raw" ] && [ "$exec_definition_raw" != "null" ]; then
+ while IFS= read -r arg; do
+ exec_args_abs+=("$arg")
+ done < <(echo "$service_resolved" | jq -r '.exec.args[]?')
+ fi
+ local exec_command_string=""
+ if [ -n "$exec_command_abs" ] && [ "$exec_command_abs" != "null" ]; then
+ exec_command_string="$(render_exec_command "$exec_command_abs" "${exec_args_abs[@]}")"
+ fi
+ local server_binary_path_for_configs="$bin_path"
+ local run_engine_config_path=""
+ if [ ${#exec_args_abs[@]} -gt 0 ]; then
+ if [ -n "${exec_args_abs[1]:-}" ] && [ "${exec_args_abs[1]}" != "null" ]; then
+ server_binary_path_for_configs="${exec_args_abs[1]}"
+ fi
+ local arg_index
+ for arg_index in "${!exec_args_abs[@]}"; do
+ if [ "${exec_args_abs[$arg_index]}" = "--config" ]; then
+ local next_index=$((arg_index + 1))
+ if [ $next_index -lt ${#exec_args_abs[@]} ]; then
+ run_engine_config_path="${exec_args_abs[$next_index]}"
+ fi
+ break
+ fi
+ done
+ fi
+ if [ -n "$server_binary_path_for_configs" ] && [ "$server_binary_path_for_configs" != "unknown" ] && [ "$server_binary_path_for_configs" != "null" ]; then
+ server_binary_path_for_configs="$(make_path_absolute "$server_binary_path_for_configs")"
+ fi
+ if [ -z "$run_engine_config_path" ] || [ "$run_engine_config_path" = "null" ]; then
+ run_engine_config_path="$CONFIG_DIR/$name-run-engine.conf"
+ else
+ run_engine_config_path="$(make_path_absolute "$run_engine_config_path")"
+ fi
+ if [ -d "$run_engine_config_path" ]; then
+ run_engine_config_path="$CONFIG_DIR/$name-run-engine.conf"
+ fi
+ if [[ "$run_engine_config_path" != /* ]]; then
+ run_engine_config_path="$(make_path_absolute "$run_engine_config_path")"
+ fi
echo ""
echo -e "${YELLOW}Service '$name' ($provider) is missing${NC}"
@@ -182,13 +379,20 @@ function restore_missing_services() {
if [ "$bin_path" = "unknown" ] || [ "$bin_path" = "null" ] || [ "$status" = "migrated" ]; then
echo " Binary: <needs manual configuration>"
- echo " Args: <needs manual configuration>"
+ echo " Exec: <needs manual configuration>"
echo ""
echo -e "${YELLOW}This service needs to be recreated manually:${NC}"
echo " $0 create $service_type $name --provider $provider --bin-path /path/to/your/bin"
else
echo " Binary: $bin_path"
- echo " Args: $args"
+ if [ -n "$exec_command_string" ]; then
+ echo " Exec: $exec_command_string"
+ else
+ echo " Exec: <unavailable>"
+ fi
+ if [ -n "$server_config" ]; then
+ echo " Server config: $server_config"
+ fi
fi
echo ""
@@ -204,19 +408,30 @@ function restore_missing_services() {
fi
else
echo -e "${BLUE}Recreating service '$name'...${NC}"
+ if ! ensure_service_configs_restored "$name" "$service_type" "$provider" "$server_binary_path_for_configs" "$server_config" "$restart_policy" "$session_manager" "$gdb_enabled" "$systemd_type" "$pm2_opts" "$run_engine_config_path" "false"; then
+ echo -e "${YELLOW}Warning: Unable to restore configuration files for '$name'${NC}"
+ fi
if [ "$provider" = "pm2" ]; then
- if [ "$args" != "null" ] && [ -n "$args" ]; then
- pm2_create_service "$name" "$bin_path $args" "$restart_policy" $pm2_opts
+ if [ -z "$exec_command_string" ] || [ "$exec_definition_raw" = "null" ]; then
+ echo -e "${RED}Cannot recreate PM2 service automatically: missing exec definition${NC}"
else
- pm2_create_service "$name" "$bin_path" "$restart_policy" $pm2_opts
+ if [ -n "$pm2_opts" ]; then
+ pm2_create_service "$name" "$exec_command_string" "$restart_policy" "$bin_path" "$server_config" "$exec_definition_raw" $pm2_opts
+ else
+ pm2_create_service "$name" "$exec_command_string" "$restart_policy" "$bin_path" "$server_config" "$exec_definition_raw"
+ fi
fi
elif [ "$provider" = "systemd" ]; then
echo -e "${BLUE}Attempting to recreate systemd service '$name' automatically...${NC}"
- if systemd_create_service "$name" "$bin_path $args" "$restart_policy" "$systemd_type" "$session_manager" "$gdb_enabled" "$server_config"; then
- echo -e "${GREEN}Systemd service '$name' recreated successfully${NC}"
+ if [ -z "$exec_command_string" ] || [ "$exec_definition_raw" = "null" ]; then
+ echo -e "${RED}Cannot recreate systemd service automatically: missing exec definition${NC}"
else
- echo -e "${RED}Failed to recreate systemd service '$name'. Please recreate manually.${NC}"
- echo " $0 create $name $service_type --provider systemd --bin-path $bin_path"
+ if systemd_create_service "$name" "$exec_command_string" "$restart_policy" "$bin_path" "$server_config" "$exec_definition_raw" "$systemd_type"; then
+ echo -e "${GREEN}Systemd service '$name' recreated successfully${NC}"
+ else
+ echo -e "${RED}Failed to recreate systemd service '$name'. Please recreate manually.${NC}"
+ echo " $0 create $name $service_type --provider systemd --bin-path $bin_path"
+ fi
fi
fi
fi
@@ -275,7 +490,7 @@ function print_help() {
echo " $base_name update <service-name> [options]"
echo " $base_name delete <service-name>"
echo " $base_name list [provider]"
- echo " $base_name restore"
+ echo " $base_name restore [--sync-only]"
echo " $base_name start|stop|restart|status <service-name>"
echo " $base_name logs <service-name> [--follow]"
echo " $base_name attach <service-name>"
@@ -342,6 +557,9 @@ function print_help() {
echo " # Restore missing services from registry"
echo " $base_name restore"
echo ""
+ echo " # Normalize registry paths to be relative to AC_SERVICE_CONFIG_DIR"
+ echo " $base_name registry-normalize"
+ echo ""
echo "Notes:"
echo " - Configuration editing modifies run-engine settings (GDB, session manager, etc.)"
echo " - Use --server-config for the actual server configuration file"
@@ -353,10 +571,13 @@ function print_help() {
echo " - attach command automatically detects the configured session manager and connects appropriately"
echo " - attach always provides interactive access to the server console"
echo " - Use 'logs' command to view service logs without interaction"
- echo " - restore command checks registry and helps recreate missing services"
+ echo " - restore command first normalizes registry paths, syncs config files, and then recreates missing services"
echo ""
echo "Environment Variables:"
echo " AC_SERVICE_CONFIG_DIR - Override default config directory for services registry"
+ echo " When set, binary and configuration paths under this directory"
+ echo " are stored as relative paths for improved portability."
+ echo " Use '$base_name registry-normalize' to rewrite existing entries."
}
@@ -442,19 +663,314 @@ function get_service_info() {
jq --arg name "$service_name" '.[] | select(.name == $name)' "$REGISTRY_FILE"
}
+# Get service info with resolved paths (relative paths converted to absolute)
+function get_service_info_resolved() {
+ local service_name="$1"
+ local service_info="$(get_service_info "$service_name")"
+
+ if [ -z "$service_info" ] || [ "$service_info" = "null" ]; then
+ echo ""
+ return
+ fi
+
+ # Extract paths and convert them
+ local bin_path_raw="$(echo "$service_info" | jq -r '.bin_path // ""')"
+ local server_config_raw="$(echo "$service_info" | jq -r '.server_config // ""')"
+ local exec_command_raw="$(echo "$service_info" | jq -r '.exec.command // ""')"
+ local exec_args_raw_json="$(echo "$service_info" | jq -c '.exec.args // []')"
+
+ local bin_path_resolved=""
+ local server_config_resolved=""
+ local exec_command_resolved=""
+ local -a exec_args_resolved=()
+
+ if [ -n "$bin_path_raw" ] && [ "$bin_path_raw" != "null" ] && [ "$bin_path_raw" != "" ]; then
+ bin_path_resolved="$(make_path_absolute "$bin_path_raw")"
+ else
+ bin_path_resolved="$bin_path_raw"
+ fi
+
+ if [ -n "$exec_command_raw" ] && [ "$exec_command_raw" != "null" ]; then
+ exec_command_resolved="$(make_path_absolute "$exec_command_raw")"
+ else
+ exec_command_resolved="$exec_command_raw"
+ fi
+
+ if [ -n "$exec_args_raw_json" ] && [ "$exec_args_raw_json" != "null" ]; then
+ local prev_arg=""
+ while IFS= read -r arg; do
+ if [[ -z "$arg" ]]; then
+ exec_args_resolved+=("$arg")
+ elif [ "$prev_arg" = "--config" ]; then
+ exec_args_resolved+=("$(make_path_absolute "$arg")")
+ elif [[ "$arg" == /* ]]; then
+ exec_args_resolved+=("$(make_path_absolute "$arg")")
+ elif [[ "$arg" == */* && "$arg" != -* ]]; then
+ exec_args_resolved+=("$(make_path_absolute "$arg")")
+ else
+ exec_args_resolved+=("$arg")
+ fi
+ prev_arg="$arg"
+ done < <(echo "$exec_args_raw_json" | jq -r '.[]?')
+ fi
+
+ if [ -n "$server_config_raw" ] && [ "$server_config_raw" != "null" ] && [ "$server_config_raw" != "" ]; then
+ server_config_resolved="$(make_path_absolute "$server_config_raw")"
+ else
+ server_config_resolved="$server_config_raw"
+ fi
+
+ local exec_args_resolved_json
+ exec_args_resolved_json=$(printf '%s\0' "${exec_args_resolved[@]}" | jq -R -s 'split("\u0000")[:-1]')
+ local exec_resolved_json
+ exec_resolved_json=$(jq -n --arg command "${exec_command_resolved:-}" --argjson args "$exec_args_resolved_json" '{command: $command, args: $args}')
+
+ # Return the service info with resolved paths
+ echo "$service_info" | jq --arg bin_path "$bin_path_resolved" \
+ --arg server_config "$server_config_resolved" \
+ --argjson exec "$exec_resolved_json" \
+ '.bin_path = $bin_path | .exec = $exec | .server_config = $server_config'
+}
+
+function ensure_service_configs_restored() {
+ local service_name="$1"
+ local service_type="$2"
+ local provider="$3"
+ local server_binary_path="$4"
+ local server_config_path="$5"
+ local restart_policy="$6"
+ local session_manager="$7"
+ local gdb_enabled="$8"
+ local systemd_type="$9"
+ local pm2_opts="${10:-}"
+ local run_engine_config_path="${11:-}"
+ local force_rewrite="${12:-false}"
+
+ if [ -z "$service_name" ]; then
+ return 1
+ fi
+
+ if [ -z "$server_binary_path" ] || [ "$server_binary_path" = "null" ] || [ "$server_binary_path" = "unknown" ]; then
+ return 0
+ fi
+
+ if [ "$server_config_path" = "null" ]; then
+ server_config_path=""
+ fi
+
+ if [ -z "$restart_policy" ] || [ "$restart_policy" = "null" ]; then
+ restart_policy="always"
+ fi
+
+ if [ -z "$session_manager" ] || [ "$session_manager" = "null" ]; then
+ session_manager="none"
+ fi
+
+ if [ -z "$gdb_enabled" ] || [ "$gdb_enabled" = "null" ]; then
+ gdb_enabled="0"
+ fi
+
+ if [ -z "$systemd_type" ] || [ "$systemd_type" = "null" ]; then
+ systemd_type="--user"
+ fi
+
+ pm2_opts="${pm2_opts:-}"
+ pm2_opts="${pm2_opts//$'\n'/ }"
+ pm2_opts="${pm2_opts#"${pm2_opts%%[![:space:]]*}"}"
+ pm2_opts="${pm2_opts%"${pm2_opts##*[![:space:]]}"}"
+
+ if [ -z "$run_engine_config_path" ] || [ "$run_engine_config_path" = "null" ]; then
+ run_engine_config_path="$CONFIG_DIR/$service_name-run-engine.conf"
+ fi
+
+ local service_conf_path="$CONFIG_DIR/$service_name.conf"
+ local server_binary_dir
+ local server_binary_name
+
+ server_binary_dir="$(dirname "$server_binary_path")"
+ server_binary_name="$(basename "$server_binary_path")"
+
+ mkdir -p "$(dirname "$run_engine_config_path")"
+
+ if [ "$force_rewrite" = "true" ] || [ ! -f "$run_engine_config_path" ]; then
+ echo -e "${BLUE}Restoring run-engine config: $run_engine_config_path${NC}"
+ cat > "$run_engine_config_path" << EOF
+# run-engine configuration for service: $service_name
+# Restored: $(date)
+
+# Enable/disable GDB execution
+export GDB_ENABLED=$gdb_enabled
+
+# Session manager (none|auto|tmux|screen)
+export SESSION_MANAGER="$session_manager"
+
+# Restart policy (on-failure|always)
+export RESTART_POLICY="$restart_policy"
+
+# Service mode - indicates this is running under a service manager (systemd/pm2)
+# When true, AC_DISABLE_INTERACTIVE will be set if no interactive session manager is used
+export SERVICE_MODE="true"
+
+# Session name for tmux/screen (optional)
+export SESSION_NAME="${service_name}"
+
+# Binary directory path
+export BINPATH="$server_binary_dir"
+
+# Server binary name
+export SERVERBIN="$server_binary_name"
+
+# Server configuration file path
+export CONFIG="$server_config_path"
+
+# Show console output for easier debugging
+export WITH_CONSOLE=1
+EOF
+ fi
+
+ if [ "$force_rewrite" = "true" ] || [ ! -f "$service_conf_path" ]; then
+ echo -e "${BLUE}Restoring service metadata: $service_conf_path${NC}"
+ cat > "$service_conf_path" << EOF
+# AzerothCore service configuration for $service_name
+# Restored: $(date)
+# Provider: $provider
+# Service Type: $service_type
+
+# run-engine configuration file
+RUN_ENGINE_CONFIG_FILE="$run_engine_config_path"
+
+# Restart policy
+RESTART_POLICY="$restart_policy"
+
+# Provider-specific options
+SYSTEMD_TYPE="$systemd_type"
+PM2_OPTS="$pm2_opts"
+EOF
+ fi
+
+ return 0
+}
+
+function sync_service_configs_from_registry() {
+ echo -e "${BLUE}Syncing service configuration files from registry...${NC}"
+
+ if [ ! -f "$REGISTRY_FILE" ] || [ ! -s "$REGISTRY_FILE" ]; then
+ echo -e "${YELLOW}No services registry found or empty${NC}"
+ return 0
+ fi
+
+ local services_count
+ services_count=$(jq length "$REGISTRY_FILE")
+ if [ "$services_count" -eq 0 ]; then
+ echo -e "${YELLOW}No services registered${NC}"
+ return 0
+ fi
+
+ local synced=0
+
+ for i in $(seq 0 $((services_count - 1))); do
+ local entry
+ entry=$(jq -c ".[$i]" "$REGISTRY_FILE")
+ [ -z "$entry" ] && continue
+
+ local name
+ name=$(echo "$entry" | jq -r '.name // empty')
+ [ -z "$name" ] && continue
+
+ local service_resolved
+ service_resolved="$(get_service_info_resolved "$name")"
+ [ -z "$service_resolved" ] && continue
+
+ local service_type provider restart_policy session_manager gdb_enabled systemd_type pm2_opts
+ service_type=$(echo "$entry" | jq -r '.type // ""')
+ provider=$(echo "$entry" | jq -r '.provider // ""')
+ restart_policy=$(echo "$entry" | jq -r '.restart_policy // "always"')
+ session_manager=$(echo "$entry" | jq -r '.session_manager // "none"')
+ gdb_enabled=$(echo "$entry" | jq -r '.gdb_enabled // "0"')
+ systemd_type=$(echo "$entry" | jq -r '.systemd_type // "--user"')
+ pm2_opts=$(echo "$entry" | jq -r '.pm2_opts // ""')
+
+ local server_binary_path
+ server_binary_path=$(echo "$service_resolved" | jq -r '.bin_path // ""')
+ if [ -z "$server_binary_path" ] || [ "$server_binary_path" = "null" ] || [ "$server_binary_path" = "unknown" ]; then
+ server_binary_path=""
+ fi
+
+ local server_config_path
+ server_config_path=$(echo "$service_resolved" | jq -r '.server_config // ""')
+ if [ -n "$server_config_path" ] && [ "$server_config_path" != "null" ]; then
+ server_config_path="$(make_path_absolute "$server_config_path")"
+ else
+ server_config_path=""
+ fi
+
+ local exec_definition
+ exec_definition=$(echo "$entry" | jq -c '.exec // null')
+ local -a exec_args=()
+ if [ -n "$exec_definition" ] && [ "$exec_definition" != "null" ]; then
+ while IFS= read -r arg; do
+ exec_args+=("$arg")
+ done < <(echo "$exec_definition" | jq -r '.args[]?')
+ fi
+
+ if { [ -z "$server_binary_path" ] || [ "$server_binary_path" = "null" ]; } && [ ${#exec_args[@]} -ge 2 ]; then
+ server_binary_path="$(make_path_absolute "${exec_args[1]}")"
+ fi
+
+ local run_engine_config_rel=""
+ for idx in "${!exec_args[@]}"; do
+ if [ "${exec_args[$idx]}" = "--config" ]; then
+ local next_idx=$((idx + 1))
+ if [ $next_idx -lt ${#exec_args[@]} ]; then
+ run_engine_config_rel="${exec_args[$next_idx]}"
+ fi
+ break
+ fi
+ done
+
+ if [ -z "$run_engine_config_rel" ] || [ "$run_engine_config_rel" = "null" ]; then
+ run_engine_config_rel="$name-run-engine.conf"
+ fi
+
+ local run_engine_config_path
+ if [[ "$run_engine_config_rel" = /* ]]; then
+ run_engine_config_path="$run_engine_config_rel"
+ else
+ run_engine_config_path="$CONFIG_DIR/$run_engine_config_rel"
+ fi
+ run_engine_config_path="$(realpath -m "$run_engine_config_path" 2>/dev/null || echo "$run_engine_config_path")"
+
+ if [ -n "$server_binary_path" ]; then
+ server_binary_path="$(make_path_absolute "$server_binary_path")"
+ fi
+
+ ensure_service_configs_restored "$name" "$service_type" "$provider" "$server_binary_path" "$server_config_path" "$restart_policy" "$session_manager" "$gdb_enabled" "$systemd_type" "$pm2_opts" "$run_engine_config_path" "true"
+ synced=$((synced + 1))
+ done
+
+ if [ "$synced" -gt 0 ]; then
+ echo -e "${GREEN}Synchronized $synced service configuration file(s)${NC}"
+ else
+ echo -e "${YELLOW}No service configuration files required synchronization${NC}"
+ fi
+}
+
# PM2 service management functions
function pm2_create_service() {
local service_name="$1"
- local command="$2"
+ local command_string="$2"
local restart_policy="$3"
- shift 3
+ local real_bin_path="$4"
+ local server_config_path="$5"
+ local exec_definition="$6"
+ shift 6
check_pm2 || return 1
# Parse additional PM2 options
local max_memory=""
local max_restarts=""
- local additional_args=""
+ local -a extra_pm2_args=()
while [[ $# -gt 0 ]]; do
case "$1" in
@@ -467,39 +983,79 @@ function pm2_create_service() {
shift 2
;;
*)
- additional_args+=" $1"
+ extra_pm2_args+=("$1")
shift
;;
esac
done
-
+
+ if [ -z "$exec_definition" ] || [ "$exec_definition" = "null" ]; then
+ echo -e "${RED}Error: Missing exec definition for PM2 service '$service_name'${NC}"
+ return 1
+ fi
+
+ local exec_command_rel
+ exec_command_rel=$(echo "$exec_definition" | jq -r '.command // empty')
+ if [ -z "$exec_command_rel" ] || [ "$exec_command_rel" = "null" ]; then
+ echo -e "${RED}Error: Exec command not defined for service '$service_name'${NC}"
+ return 1
+ fi
+
+ local exec_command_abs
+ exec_command_abs="$(make_path_absolute "$exec_command_rel")"
+ local -a exec_args_abs=()
+ local prev_arg=""
+ while IFS= read -r arg; do
+ if [[ -z "$arg" ]]; then
+ exec_args_abs+=("$arg")
+ elif [ "$prev_arg" = "--config" ]; then
+ exec_args_abs+=("$(make_path_absolute "$arg")")
+ elif [[ "$arg" == /* ]]; then
+ exec_args_abs+=("$(make_path_absolute "$arg")")
+ elif [[ "$arg" == */* && "$arg" != -* ]]; then
+ exec_args_abs+=("$(make_path_absolute "$arg")")
+ else
+ exec_args_abs+=("$arg")
+ fi
+ prev_arg="$arg"
+ done < <(echo "$exec_definition" | jq -r '.args[]?')
# Set stop exit codes based on restart policy
- local stop_exit_codes=""
+ local -a pm2_args=(start "$exec_command_abs" --interpreter none --name "$service_name")
if [ "$restart_policy" = "always" ]; then
# PM2 will restart on any exit code (including 0)
- stop_exit_codes=""
+ :
else
# PM2 will not restart on clean shutdown (exit code 0)
- stop_exit_codes=" --stop-exit-codes 0"
+ pm2_args+=("--stop-exit-codes" "0")
fi
- # Build PM2 start command with AzerothCore environment variable
- local pm2_cmd="AC_LAUNCHED_BY_PM2=1 pm2 start '$command$additional_args' --name '$service_name'$stop_exit_codes"
# Add memory limit if specified
if [ -n "$max_memory" ]; then
- pm2_cmd+=" --max-memory-restart $max_memory"
+ pm2_args+=("--max-memory-restart" "$max_memory")
fi
# Add max restarts if specified
if [ -n "$max_restarts" ]; then
- pm2_cmd+=" --max-restarts $max_restarts"
+ pm2_args+=("--max-restarts" "$max_restarts")
+ fi
+
+ if [ ${#extra_pm2_args[@]} -gt 0 ]; then
+ pm2_args+=("${extra_pm2_args[@]}")
+ fi
+
+ if [ ${#exec_args_abs[@]} -gt 0 ]; then
+ pm2_args+=("--")
+ pm2_args+=("${exec_args_abs[@]}")
fi
# Execute command
echo -e "${YELLOW}Creating PM2 service: $service_name${NC}"
+ if [ -n "$command_string" ]; then
+ echo " Exec: $command_string"
+ fi
- if eval "$pm2_cmd"; then
+ if AC_LAUNCHED_BY_PM2=1 pm2 "${pm2_args[@]}"; then
echo -e "${GREEN}PM2 service '$service_name' created successfully${NC}"
pm2 save
@@ -507,9 +1063,13 @@ function pm2_create_service() {
echo -e "${BLUE}Configuring PM2 startup for persistence...${NC}"
pm2 startup --auto >/dev/null 2>&1 || true
- # Add to registry (extract command and args from the full command)
- local clean_command="$command$additional_args"
- add_service_to_registry "$service_name" "pm2" "executable" "$command" "$additional_args" "" "$restart_policy" "none" "0" "$max_memory $max_restarts" ""
+ # Add to registry (use real binary path instead of command for portability)
+ local extra_args_serialized=""
+ if [ ${#extra_pm2_args[@]} -gt 0 ]; then
+ extra_args_serialized="$(printf '%s ' "${extra_pm2_args[@]}")"
+ extra_args_serialized="${extra_args_serialized% }"
+ fi
+ add_service_to_registry "$service_name" "pm2" "executable" "$real_bin_path" "$extra_args_serialized" "" "$restart_policy" "none" "0" "$max_memory $max_restarts" "$server_config_path" "$exec_definition"
return 0
else
@@ -552,12 +1112,12 @@ function pm2_remove_service() {
pm2 save
echo -e "${GREEN}PM2 service '$service_name' stopped and removed${NC}"
- # Remove from registry
- remove_service_from_registry "$service_name"
+ # Note: Registry removal is handled by the caller (delete_service)
+ return 0
else
- echo -e "${YELLOW}PM2 service '$service_name' not found or already removed${NC}"
- # Still try to remove from registry in case it's orphaned
- remove_service_from_registry "$service_name"
+ echo -e "${YELLOW}PM2 service '$service_name' not found${NC}"
+ # Service not found in PM2 - let caller decide about registry
+ return 0
fi
return 0
@@ -602,11 +1162,14 @@ function systemd_create_service() {
local service_name="$1"
local command="$2"
local restart_policy="$3"
+ local real_bin_path="$4"
+ local server_config_path="$5"
+ local exec_definition="$6"
local systemd_type="--user"
local bin_path=""
local gdb_enabled="0"
local server_config=""
- shift 3
+ shift 6
check_systemd || return 1
@@ -743,7 +1306,7 @@ EOF
echo -e "${GREEN}Systemd service '$service_name' created successfully with session manager '$session_manager'${NC}"
# Add to registry
- add_service_to_registry "$service_name" "systemd" "service" "$command" "" "$systemd_type" "$restart_policy" "$session_manager" "$gdb_enabled" "" "$server_config"
+ add_service_to_registry "$service_name" "systemd" "service" "$real_bin_path" "" "$systemd_type" "$restart_policy" "$session_manager" "$gdb_enabled" "" "$server_config_path" "$exec_definition"
return 0
}
@@ -805,9 +1368,7 @@ function systemd_remove_service() {
echo -e "${YELLOW}Note: Service may still be running but configuration was removed${NC}"
fi
- # Remove from registry
- remove_service_from_registry "$service_name"
-
+ # Note: Registry removal is handled by the caller (delete_service)
return 0
else
echo -e "${RED}Failed to remove systemd service file '$service_file'${NC}"
@@ -1051,24 +1612,29 @@ SYSTEMD_TYPE="$systemd_type"
PM2_OPTS="$pm2_opts"
EOF
- # Build run-engine command
- local run_engine_cmd="$SCRIPT_DIR/run-engine start $server_binary_path --config $run_engine_config"
+ # Build run-engine command definition
+ local run_engine_path="$SCRIPT_DIR/run-engine"
+ local -a run_engine_args=("start" "$server_binary_path" "--config" "$run_engine_config")
+ local run_engine_cmd
+ run_engine_cmd="$(render_exec_command "$run_engine_path" "${run_engine_args[@]}")"
+ local exec_definition
+ exec_definition="$(serialize_exec_definition "$run_engine_path" "${run_engine_args[@]}")"
# Create the actual service
local service_creation_success=false
if [ "$provider" = "pm2" ]; then
if [ -n "$pm2_opts" ]; then
- if pm2_create_service "$service_name" "$run_engine_cmd" "$restart_policy" $pm2_opts; then
+ if pm2_create_service "$service_name" "$run_engine_cmd" "$restart_policy" "$server_binary_path" "$server_config" "$exec_definition" $pm2_opts; then
service_creation_success=true
fi
else
- if pm2_create_service "$service_name" "$run_engine_cmd" "$restart_policy"; then
+ if pm2_create_service "$service_name" "$run_engine_cmd" "$restart_policy" "$server_binary_path" "$server_config" "$exec_definition"; then
service_creation_success=true
fi
fi
elif [ "$provider" = "systemd" ]; then
- if systemd_create_service "$service_name" "$run_engine_cmd" "$restart_policy" "$systemd_type"; then
+ if systemd_create_service "$service_name" "$run_engine_cmd" "$restart_policy" "$server_binary_path" "$server_config" "$exec_definition" "$systemd_type"; then
service_creation_success=true
fi
fi
@@ -1124,10 +1690,10 @@ function update_service() {
source "$config_file"
# Load current run-engine configuration
- if [ -f "$RUN_ENGINE_CONFIG_FILE" ]; then
+ if [ -n "${RUN_ENGINE_CONFIG_FILE:-}" ] && [ -f "$RUN_ENGINE_CONFIG_FILE" ]; then
source "$RUN_ENGINE_CONFIG_FILE"
else
- echo -e "${YELLOW}Warning: Run-engine configuration file not found: $RUN_ENGINE_CONFIG_FILE${NC}"
+ echo -e "${YELLOW}Warning: Run-engine configuration file not found: ${RUN_ENGINE_CONFIG_FILE:-<unset>}${NC}"
fi
# Parse options to update
@@ -1284,8 +1850,11 @@ function delete_service() {
fi
if [ "$removal_success" = "true" ]; then
+ # Remove from registry only after successful service removal
+ remove_service_from_registry "$service_name"
+
# Remove run-engine configuration file
- if [ -n "$RUN_ENGINE_CONFIG_FILE" ] && [ -f "$RUN_ENGINE_CONFIG_FILE" ]; then
+ if [ -n "${RUN_ENGINE_CONFIG_FILE:-}" ] && [ -f "$RUN_ENGINE_CONFIG_FILE" ]; then
rm -f "$RUN_ENGINE_CONFIG_FILE"
echo -e "${GREEN}Removed run-engine config: $RUN_ENGINE_CONFIG_FILE${NC}"
fi
@@ -1296,6 +1865,7 @@ function delete_service() {
echo -e "${GREEN}Service '$service_name' deleted successfully${NC}"
else
echo -e "${RED}Failed to remove service '$service_name' from $provider${NC}"
+ echo -e "${YELLOW}Registry entry preserved. Service may need manual cleanup.${NC}"
return 1
fi
}
@@ -1416,7 +1986,11 @@ function edit_config() {
# Open run-engine configuration file in editor
echo -e "${YELLOW}Editing run-engine configuration for: $service_name${NC}"
- echo -e "${BLUE}File: $RUN_ENGINE_CONFIG_FILE${NC}"
+ echo -e "${BLUE}File: ${RUN_ENGINE_CONFIG_FILE:-<unset>}${NC}"
+ if [ -z "${RUN_ENGINE_CONFIG_FILE:-}" ] || [ ! -f "$RUN_ENGINE_CONFIG_FILE" ]; then
+ echo -e "${RED}Error: Run-engine configuration file not found: ${RUN_ENGINE_CONFIG_FILE:-<unset>}${NC}"
+ return 1
+ fi
${EDITOR:-nano} "$RUN_ENGINE_CONFIG_FILE"
echo -e "${GREEN}Configuration updated. Run '$0 restart $service_name' to apply changes.${NC}"
@@ -1445,16 +2019,12 @@ function attach_to_service() {
source "$config_file"
# Load run-engine configuration
- if [ ! -f "$RUN_ENGINE_CONFIG_FILE" ]; then
- echo -e "${RED}Error: Run-engine configuration file not found: $RUN_ENGINE_CONFIG_FILE${NC}"
+ if [ -z "${RUN_ENGINE_CONFIG_FILE:-}" ] || [ ! -f "$RUN_ENGINE_CONFIG_FILE" ]; then
+ echo -e "${RED}Error: Run-engine configuration file not found: ${RUN_ENGINE_CONFIG_FILE:-<unset>}${NC}"
return 1
fi
- if [ ! -f "$RUN_ENGINE_CONFIG_FILE" ]; then
- echo -e "${RED}Error: Run-engine configuration file not found: $RUN_ENGINE_CONFIG_FILE${NC}"
- return 1
- fi
-
+ # Now safe to source
source "$RUN_ENGINE_CONFIG_FILE"
# Auto-detect session manager and attach accordingly
@@ -1721,6 +2291,9 @@ function wait_service_uptime() {
sleep 1
waited=$((waited + 1))
done
+ # show service logs for debugging
+ echo -e "${YELLOW}Service logs for '$service_name':${NC}"
+ service_logs "$service_name" true
echo -e "${RED}Timeout: $service_name did not reach ${min_seconds}s uptime within ${timeout}s${NC}" >&2
return 1
}
@@ -1770,8 +2343,8 @@ function attach_interactive_shell() {
source "$config_file"
# Check if RUN_ENGINE_CONFIG_FILE exists before sourcing
- if [ ! -f "$RUN_ENGINE_CONFIG_FILE" ]; then
- echo -e "${RED}Error: Run-engine configuration file not found: $RUN_ENGINE_CONFIG_FILE${NC}"
+ if [ -z "${RUN_ENGINE_CONFIG_FILE:-}" ] || [ ! -f "$RUN_ENGINE_CONFIG_FILE" ]; then
+ echo -e "${RED}Error: Run-engine configuration file not found: ${RUN_ENGINE_CONFIG_FILE:-<unset>}${NC}"
return 1
fi
@@ -1796,6 +2369,96 @@ function attach_interactive_shell() {
return 1
}
+# Normalize existing registry entries to use relative paths for fields that
+# represent filesystem paths (bin_path, server_config) and refresh exec command
+# definitions to the new schema.
+function normalize_registry_paths() {
+ if [ ! -f "$REGISTRY_FILE" ]; then
+ echo -e "${YELLOW}No registry file found at: $REGISTRY_FILE${NC}"
+ return 0
+ fi
+
+ # Validate JSON
+ if ! jq empty "$REGISTRY_FILE" >/dev/null 2>&1; then
+ echo -e "${RED}Error: Invalid JSON in $REGISTRY_FILE${NC}"
+ return 1
+ fi
+
+ local tmp_file
+ tmp_file=$(mktemp)
+ echo "[]" > "$tmp_file"
+
+ local count=0
+ while IFS= read -r entry; do
+ [ -z "$entry" ] && continue
+
+ # Extract raw values (empty if null or missing)
+ local bin_path_raw server_config_raw
+ bin_path_raw=$(echo "$entry" | jq -r '.bin_path // empty')
+ server_config_raw=$(echo "$entry" | jq -r '.server_config // empty')
+
+ # Compute normalized values
+ local bin_path_new server_config_new
+ if [ -n "$bin_path_raw" ]; then
+ bin_path_new="$(make_path_relative "$bin_path_raw")"
+ else
+ bin_path_new=""
+ fi
+ if [ -n "$server_config_raw" ]; then
+ server_config_new="$(make_path_relative "$server_config_raw")"
+ else
+ server_config_new=""
+ fi
+
+ local exec_command_raw
+ exec_command_raw=$(echo "$entry" | jq -r '.exec.command // empty')
+ local exec_args_array=()
+ local exec_json_new="null"
+ if [ -n "$exec_command_raw" ]; then
+ while IFS= read -r arg; do
+ exec_args_array+=("$arg")
+ done < <(echo "$entry" | jq -r '.exec.args[]?')
+ exec_json_new="$(serialize_exec_definition "$exec_command_raw" "${exec_args_array[@]}")"
+ fi
+
+ # Update entry (only override when non-empty; preserve nulls)
+ local updated
+ if [ "$exec_json_new" != "null" ]; then
+ updated=$(echo "$entry" | jq \
+ --arg bin "$bin_path_new" \
+ --arg srv "$server_config_new" \
+ --argjson exec "$exec_json_new" \
+ '
+ (.bin_path) |= (if $bin != "" then $bin else . end) |
+ (.server_config) |= (if $srv != "" then $srv else . end) |
+ (.exec) = $exec |
+ del(.real_bin_path)
+ ')
+ else
+ updated=$(echo "$entry" | jq \
+ --arg bin "$bin_path_new" \
+ --arg srv "$server_config_new" \
+ '
+ (.bin_path) |= (if $bin != "" then $bin else . end) |
+ (.server_config) |= (if $srv != "" then $srv else . end) |
+ del(.real_bin_path)
+ ')
+ fi
+
+ # Append to new array
+ if ! jq --argjson e "$updated" '. += [$e]' "$tmp_file" > "$tmp_file.new"; then
+ rm -f "$tmp_file" "$tmp_file.new"
+ echo -e "${RED}Error: Failed updating registry${NC}"
+ return 1
+ fi
+ mv "$tmp_file.new" "$tmp_file"
+ count=$((count+1))
+ done < <(jq -c '.[]?' "$REGISTRY_FILE")
+
+ mv "$tmp_file" "$REGISTRY_FILE"
+ echo -e "${GREEN}Normalized $count registry entrie(s) to relative paths${NC}"
+}
+
function attach_tmux_session() {
local service_name="$1"
local provider="$2"
@@ -1852,10 +2515,12 @@ function attach_screen_session() {
# Main execution
-check_dependencies
+# Only run the main logic if this script is executed directly (not sourced)
+if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
+ check_dependencies
-# Main command processing
-case "${1:-help}" in
+ # Main command processing
+ case "${1:-help}" in
create)
if [ $# -lt 3 ]; then
echo -e "${RED}Error: Not enough arguments for create command${NC}"
@@ -1884,7 +2549,26 @@ case "${1:-help}" in
list_services "${2:-}"
;;
restore)
- restore_missing_services
+ sync_only=false
+ if [ $# -gt 1 ]; then
+ for opt in "${@:2}"; do
+ case "$opt" in
+ --sync-only)
+ sync_only=true
+ ;;
+ *)
+ echo -e "${RED}Error: Unknown option for restore command: $opt${NC}"
+ print_help
+ exit 1
+ ;;
+ esac
+ done
+ fi
+ normalize_registry_paths
+ sync_service_configs_from_registry
+ if [ "$sync_only" = "false" ]; then
+ restore_missing_services
+ fi
;;
start|stop|restart|status)
if [ $# -lt 2 ]; then
@@ -1900,12 +2584,15 @@ case "${1:-help}" in
print_help
exit 1
fi
- if [ "$3" = "--follow" ]; then
+ if [ "${3:-}" = "--follow" ]; then
service_logs "$2" "true"
else
service_logs "$2" "false"
fi
;;
+ registry-normalize)
+ normalize_registry_paths
+ ;;
edit-config)
if [ $# -lt 2 ]; then
echo -e "${RED}Error: Service name required for edit-config command${NC}"
@@ -1977,3 +2664,5 @@ case "${1:-help}" in
exit 1
;;
esac
+
+fi # End of main execution block