summaryrefslogtreecommitdiff
path: root/apps/startup-scripts/src
diff options
context:
space:
mode:
Diffstat (limited to 'apps/startup-scripts/src')
-rw-r--r--apps/startup-scripts/src/.gitignore1
-rw-r--r--apps/startup-scripts/src/conf.sh.dist57
-rwxr-xr-xapps/startup-scripts/src/examples/restarter-auth.sh48
-rwxr-xr-xapps/startup-scripts/src/examples/restarter-world.sh47
-rwxr-xr-xapps/startup-scripts/src/examples/starter-auth.sh46
-rwxr-xr-xapps/startup-scripts/src/examples/starter-world.sh47
-rw-r--r--apps/startup-scripts/src/gdb.conf7
-rwxr-xr-xapps/startup-scripts/src/run-engine467
-rwxr-xr-xapps/startup-scripts/src/service-manager.sh1261
-rwxr-xr-xapps/startup-scripts/src/simple-restarter89
-rwxr-xr-xapps/startup-scripts/src/starter117
11 files changed, 2187 insertions, 0 deletions
diff --git a/apps/startup-scripts/src/.gitignore b/apps/startup-scripts/src/.gitignore
new file mode 100644
index 0000000000..cd3d225360
--- /dev/null
+++ b/apps/startup-scripts/src/.gitignore
@@ -0,0 +1 @@
+logs \ No newline at end of file
diff --git a/apps/startup-scripts/src/conf.sh.dist b/apps/startup-scripts/src/conf.sh.dist
new file mode 100644
index 0000000000..69fbeadb9b
--- /dev/null
+++ b/apps/startup-scripts/src/conf.sh.dist
@@ -0,0 +1,57 @@
+# AzerothCore Run Engine Default Configuration
+# This file contains default values that can be overridden by environment variables
+# Priority order: conf.sh > environment variables > conf.sh.dist (this file)
+
+# Enable/disable GDB execution
+export GDB_ENABLED="${RUN_ENGINE_GDB_ENABLED:-0}"
+
+# [optional] GDB configuration file
+# default: gdb.conf
+export GDB="${RUN_ENGINE_GDB:-}"
+
+# Directory where binaries are stored
+export BINPATH="${RUN_ENGINE_BINPATH:-}"
+
+# Server binary name (e.g., worldserver, authserver)
+export SERVERBIN="${RUN_ENGINE_SERVERBIN:-}"
+
+# Path to server configuration file (including the file name)
+# ex: /home/user/azerothcore/etc/worldserver.conf
+export CONFIG="${RUN_ENGINE_CONFIG:-}"
+
+# Session manager to use: none|auto|tmux|screen
+# auto will detect the best available option
+export SESSION_MANAGER="${RUN_ENGINE_SESSION_MANAGER:-none}"
+
+# Default session manager (fallback when SESSION_MANAGER is not set)
+export DEFAULT_SESSION_MANAGER="${RUN_ENGINE_DEFAULT_SESSION_MANAGER:-none}"
+
+# Path of the crashes directory
+# If not specified, it will be created in the same directory as logs named "crashes"
+export CRASHES_PATH="${RUN_ENGINE_CRASHES_PATH:-}"
+
+# Path of log files directory
+export LOGS_PATH="${RUN_ENGINE_LOGS_PATH:-}"
+
+# Prefix name for log files to avoid collision with other instances
+export LOG_PREFIX_NAME="${RUN_ENGINE_LOG_PREFIX_NAME:-}"
+
+# [optional] Name of session (tmux session or screen session)
+# If not specified, a default name will be generated based on server binary
+export SESSION_NAME="${RUN_ENGINE_SESSION_NAME:-}"
+
+# [optional] Screen-specific options: -A -m -d -S
+# WARNING: if you are running it under a systemd service
+# please do not remove -m -d arguments from screen if you are using it,
+# or keep WITH_CONSOLE=0. Otherwise the journald-logging system will take
+# 100% of CPU slowing down the whole machine.
+export SCREEN_OPTIONS="${RUN_ENGINE_SCREEN_OPTIONS:-}"
+
+# Enable/disable console output
+# If disabled, output will be redirected to logging files
+export WITH_CONSOLE="${RUN_ENGINE_WITH_CONSOLE:-0}"
+
+# Server PID (needed when GDB_ENABLED=1)
+export SERVERPID="${RUN_ENGINE_SERVERPID:-}"
+
+
diff --git a/apps/startup-scripts/src/examples/restarter-auth.sh b/apps/startup-scripts/src/examples/restarter-auth.sh
new file mode 100755
index 0000000000..9557ebdf37
--- /dev/null
+++ b/apps/startup-scripts/src/examples/restarter-auth.sh
@@ -0,0 +1,48 @@
+#!/usr/bin/env bash
+
+# AzerothCore Auth Server Restarter Example
+# This example shows how to use the run-engine with restart functionality for authserver
+
+PATH_RUNENGINE="./"
+CONFIG_FILE="./conf-auth.sh"
+
+# Method 1: Using configuration file (recommended)
+if [ -f "$CONFIG_FILE" ]; then
+ echo "Starting authserver with restart loop using config file: $CONFIG_FILE"
+ source "$CONFIG_FILE"
+ "$PATH_RUNENGINE/run-engine" restart "$SERVERBIN" --config "$CONFIG_FILE"
+else
+ echo "Error: Configuration file not found: $CONFIG_FILE"
+ echo "Please create $CONFIG_FILE by copying and modifying conf.sh.dist"
+ echo "Make sure to set: export SERVERBIN=\"authserver\""
+ echo ""
+ echo "Alternative: Start with binary path directly"
+ echo "Example: $PATH_RUNENGINE/run-engine restart /path/to/bin/authserver"
+ echo "Example: $PATH_RUNENGINE/run-engine restart authserver # if in PATH"
+ exit 1
+fi
+
+# Method 2: Direct binary path (full path)
+# Uncomment the line below to start with full binary path
+#
+# "$PATH_RUNENGINE/run-engine" restart /home/user/azerothcore/bin/authserver --server-config /path/to/authserver.conf
+
+# Method 3: Binary name only (system PATH)
+# Uncomment the line below if authserver is in your system PATH
+#
+# "$PATH_RUNENGINE/run-engine" restart authserver --server-config /path/to/authserver.conf
+
+# Method 4: With session manager (tmux/screen)
+# Uncomment the line below to use tmux session
+#
+# "$PATH_RUNENGINE/run-engine" restart authserver --session-manager tmux --server-config /path/to/authserver.conf
+
+# Method 5: Environment variables only
+# Uncomment the lines below for environment variable configuration
+#
+# export RUN_ENGINE_BINPATH="/path/to/your/bin"
+# export RUN_ENGINE_SERVERBIN="authserver"
+# export RUN_ENGINE_CONFIG="/path/to/authserver.conf"
+# "$PATH_RUNENGINE/run-engine" restart authserver
+
+
diff --git a/apps/startup-scripts/src/examples/restarter-world.sh b/apps/startup-scripts/src/examples/restarter-world.sh
new file mode 100755
index 0000000000..2649b3f84d
--- /dev/null
+++ b/apps/startup-scripts/src/examples/restarter-world.sh
@@ -0,0 +1,47 @@
+#!/usr/bin/env bash
+
+# AzerothCore World Server Restarter Example
+# This example shows how to use the run-engine with restart functionality for worldserver
+
+PATH_RUNENGINE="./"
+CONFIG_FILE="./conf-world.sh"
+
+# Method 1: Using configuration file (recommended)
+if [ -f "$CONFIG_FILE" ]; then
+ echo "Starting worldserver with restart loop using config file: $CONFIG_FILE"
+ "$PATH_RUNENGINE/run-engine" restart "$SERVERBIN" --config "$CONFIG_FILE"
+else
+ echo "Error: Configuration file not found: $CONFIG_FILE"
+ echo "Please create $CONFIG_FILE by copying and modifying conf.sh.dist"
+ echo "Make sure to set: export SERVERBIN=\"worldserver\""
+ echo ""
+ echo "Alternative: Start with binary path directly"
+ echo "Example: $PATH_RUNENGINE/run-engine restart /path/to/bin/worldserver"
+ echo "Example: $PATH_RUNENGINE/run-engine restart worldserver # if in PATH"
+ exit 1
+fi
+
+# Method 2: Direct binary path (full path)
+# Uncomment the line below to start with full binary path
+#
+# "$PATH_RUNENGINE/run-engine" restart /home/user/azerothcore/bin/worldserver --server-config /path/to/worldserver.conf
+
+# Method 3: Binary name only (system PATH)
+# Uncomment the line below if worldserver is in your system PATH
+#
+# "$PATH_RUNENGINE/run-engine" restart worldserver --server-config /path/to/worldserver.conf
+
+# Method 4: With session manager (tmux/screen)
+# Uncomment the line below to use tmux session
+#
+# "$PATH_RUNENGINE/run-engine" restart worldserver --session-manager tmux --server-config /path/to/worldserver.conf
+
+# Method 5: Environment variables only
+# Uncomment the lines below for environment variable configuration
+#
+# export RUN_ENGINE_BINPATH="/path/to/your/bin"
+# export RUN_ENGINE_SERVERBIN="worldserver"
+# export RUN_ENGINE_CONFIG="/path/to/worldserver.conf"
+# "$PATH_RUNENGINE/run-engine" restart worldserver
+
+
diff --git a/apps/startup-scripts/src/examples/starter-auth.sh b/apps/startup-scripts/src/examples/starter-auth.sh
new file mode 100755
index 0000000000..52fcb384c8
--- /dev/null
+++ b/apps/startup-scripts/src/examples/starter-auth.sh
@@ -0,0 +1,46 @@
+#!/usr/bin/env bash
+
+# AzerothCore Auth Server Starter Example
+# This example shows how to use the run-engine to start authserver without restart loop
+
+PATH_RUNENGINE="./"
+CONFIG_FILE="./conf-auth.sh"
+
+# Method 1: Using configuration file (recommended)
+if [ -f "$CONFIG_FILE" ]; then
+ echo "Starting authserver (single run) with config file: $CONFIG_FILE"
+ "$PATH_RUNENGINE/run-engine" start "$SERVERBIN" --config "$CONFIG_FILE"
+else
+ echo "Error: Configuration file not found: $CONFIG_FILE"
+ echo "Please create $CONFIG_FILE by copying and modifying conf.sh.dist"
+ echo "Make sure to set: export SERVERBIN=\"authserver\""
+ echo ""
+ echo "Alternative: Start with binary path directly"
+ echo "Example: $PATH_RUNENGINE/run-engine start /path/to/bin/authserver"
+ echo "Example: $PATH_RUNENGINE/run-engine start authserver # if in PATH"
+ exit 1
+fi
+
+# Method 2: Direct binary path (full path)
+# Uncomment the line below to start with full binary path
+#
+# "$PATH_RUNENGINE/run-engine" start /home/user/azerothcore/bin/authserver --server-config /path/to/authserver.conf
+
+# Method 3: Binary name only (system PATH)
+# Uncomment the line below if authserver is in your system PATH
+#
+# "$PATH_RUNENGINE/run-engine" start authserver --server-config /path/to/authserver.conf
+
+# Method 4: With session manager (tmux/screen)
+# Uncomment the line below to use tmux session
+#
+# "$PATH_RUNENGINE/run-engine" start authserver --session-manager tmux --server-config /path/to/authserver.conf
+
+# Method 5: Environment variables only
+# Uncomment the lines below for environment variable configuration
+#
+# export RUN_ENGINE_BINPATH="/path/to/your/bin"
+# export RUN_ENGINE_SERVERBIN="authserver"
+# export RUN_ENGINE_CONFIG="/path/to/authserver.conf"
+# "$PATH_RUNENGINE/run-engine" start authserver
+
diff --git a/apps/startup-scripts/src/examples/starter-world.sh b/apps/startup-scripts/src/examples/starter-world.sh
new file mode 100755
index 0000000000..1eb1c4d32a
--- /dev/null
+++ b/apps/startup-scripts/src/examples/starter-world.sh
@@ -0,0 +1,47 @@
+#!/usr/bin/env bash
+
+# AzerothCore World Server Starter Example
+# This example shows how to use the run-engine to start worldserver without restart loop
+
+PATH_RUNENGINE="./"
+CONFIG_FILE="./conf-world.sh"
+
+# Method 1: Using configuration file (recommended)
+if [ -f "$CONFIG_FILE" ]; then
+ echo "Starting worldserver (single run) with config file: $CONFIG_FILE"
+ "$PATH_RUNENGINE/run-engine" start "$SERVERBIN" --config "$CONFIG_FILE"
+else
+ echo "Error: Configuration file not found: $CONFIG_FILE"
+ echo "Please create $CONFIG_FILE by copying and modifying conf.sh.dist"
+ echo "Make sure to set: export SERVERBIN=\"worldserver\""
+ echo ""
+ echo "Alternative: Start with binary path directly"
+ echo "Example: $PATH_RUNENGINE/run-engine start /path/to/bin/worldserver"
+ echo "Example: $PATH_RUNENGINE/run-engine start worldserver # if in PATH"
+ exit 1
+fi
+
+# Method 2: Direct binary path (full path)
+# Uncomment the line below to start with full binary path
+#
+# "$PATH_RUNENGINE/run-engine" start /home/user/azerothcore/bin/worldserver --server-config /path/to/worldserver.conf
+
+# Method 3: Binary name only (system PATH)
+# Uncomment the line below if worldserver is in your system PATH
+#
+# "$PATH_RUNENGINE/run-engine" start worldserver --server-config /path/to/worldserver.conf
+
+# Method 4: With session manager (tmux/screen)
+# Uncomment the line below to use tmux session
+#
+# "$PATH_RUNENGINE/run-engine" start worldserver --session-manager tmux --server-config /path/to/worldserver.conf
+
+# Method 5: Environment variables only
+# Uncomment the lines below for environment variable configuration
+#
+# export RUN_ENGINE_BINPATH="/path/to/your/bin"
+# export RUN_ENGINE_SERVERBIN="worldserver"
+# export RUN_ENGINE_CONFIG="/path/to/worldserver.conf"
+# "$PATH_RUNENGINE/run-engine" start worldserver
+
+
diff --git a/apps/startup-scripts/src/gdb.conf b/apps/startup-scripts/src/gdb.conf
new file mode 100644
index 0000000000..d6802a56b6
--- /dev/null
+++ b/apps/startup-scripts/src/gdb.conf
@@ -0,0 +1,7 @@
+set logging enabled on
+set debug timestamp
+run
+bt
+bt full
+info thread
+thread apply all backtrace full
diff --git a/apps/startup-scripts/src/run-engine b/apps/startup-scripts/src/run-engine
new file mode 100755
index 0000000000..2860339a5e
--- /dev/null
+++ b/apps/startup-scripts/src/run-engine
@@ -0,0 +1,467 @@
+#!/usr/bin/env bash
+
+# AzerothCore Run Engine
+# Advanced script for running AzerothCore services with session management and restart capabilities
+#
+# This script can be sourced to provide functions or executed directly with parameters
+#
+# Configuration Priority Order (highest to lowest):
+# 1. conf.sh - User configuration file (highest priority)
+# 2. Command line arguments (--config, --server-config, etc.)
+# 3. Environment variables (RUN_ENGINE_*)
+# 4. conf.sh.dist - Default configuration (lowest priority)
+#
+# Environment Variables:
+# RUN_ENGINE_CONFIG_FILE - Path to temporary configuration file (optional)
+# RUN_ENGINE_SESSION_MANAGER - Session manager (none|auto|tmux|screen, default: auto)
+# RUN_ENGINE_BINPATH - Binary directory path
+# RUN_ENGINE_SERVERBIN - Server binary name (worldserver|authserver)
+# RUN_ENGINE_CONFIG - Server configuration file path
+# RUN_ENGINE_LOGS_PATH - Directory for log files
+# RUN_ENGINE_CRASHES_PATH - Directory for crash dumps
+# RUN_ENGINE_SESSION_NAME - Session name for tmux/screen
+
+export RUN_ENGINE_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+
+# Configuration priority order:
+# 1. conf.sh (highest priority - user overrides)
+# 2. Environment variables (RUN_ENGINE_*)
+# 3. conf.sh.dist (lowest priority - defaults)
+
+# Load default configuration first (sets defaults from environment variables)
+if [ -e "$RUN_ENGINE_PATH/conf.sh.dist" ]; then
+ source "$RUN_ENGINE_PATH/conf.sh.dist"
+fi
+
+# Load user configuration if exists (this takes priority over everything)
+if [ -e "$RUN_ENGINE_PATH/conf.sh" ]; then
+ source "$RUN_ENGINE_PATH/conf.sh"
+fi
+
+# Load configuration
+function load_config() {
+ local config_file="$1"
+
+ # If a specific config file is provided via command line, load it
+ # This allows temporary overrides for specific runs
+ if [ -n "$config_file" ] && [ -e "$config_file" ]; then
+ echo "Loading configuration from: $config_file"
+ source "$config_file"
+ elif [ -n "$RUN_ENGINE_CONFIG_FILE" ] && [ -e "$RUN_ENGINE_CONFIG_FILE" ]; then
+ echo "Loading configuration from environment: $RUN_ENGINE_CONFIG_FILE"
+ source "$RUN_ENGINE_CONFIG_FILE"
+ fi
+
+ # Final override with any remaining environment variables
+ # This ensures that even after loading config files, environment variables take precedence
+ BINPATH="${RUN_ENGINE_BINPATH:-$BINPATH}"
+ SERVERBIN="${RUN_ENGINE_SERVERBIN:-$SERVERBIN}"
+ CONFIG="${RUN_ENGINE_CONFIG:-$CONFIG}"
+ SESSION_MANAGER="${RUN_ENGINE_SESSION_MANAGER:-$SESSION_MANAGER}"
+ LOGS_PATH="${RUN_ENGINE_LOGS_PATH:-$LOGS_PATH}"
+ CRASHES_PATH="${RUN_ENGINE_CRASHES_PATH:-$CRASHES_PATH}"
+}
+
+# Detect available session manager
+function detect_session_manager() {
+ if command -v tmux >/dev/null 2>&1; then
+ echo "tmux"
+ elif command -v screen >/dev/null 2>&1; then
+ echo "screen"
+ else
+ echo "none"
+ fi
+}
+
+# Determine which session manager to use
+function get_session_manager() {
+ local requested="$1"
+
+ case "$requested" in
+ "none")
+ echo "none"
+ ;;
+ "auto")
+ detect_session_manager
+ ;;
+ "tmux")
+ if command -v tmux >/dev/null 2>&1; then
+ echo "tmux"
+ else
+ echo "error"
+ fi
+ ;;
+ "screen")
+ if command -v screen >/dev/null 2>&1; then
+ echo "screen"
+ else
+ echo "error"
+ fi
+ ;;
+ *)
+ echo "none"
+ ;;
+ esac
+}
+
+# Configure log files
+function configure_files() {
+ TRACE_BEGIN_STRING="SIGSEGV"
+ TRACE_FILE="$LOGS_PATH/${LOG_PREFIX_NAME}_trace.log"
+ ERR_FILE="$LOGS_PATH/${LOG_PREFIX_NAME}_error.log"
+ SYSLOG="$LOGS_PATH/${LOG_PREFIX_NAME}_system.log"
+ SYSERR="$LOGS_PATH/${LOG_PREFIX_NAME}_system.err"
+ LINKS_FILE="$LOGS_PATH/${LOG_PREFIX_NAME}_crash_links.link"
+}
+
+# Check if service is running
+function check_status() {
+ local session_name="$1"
+ local ret=1
+
+ # Check for GDB process
+ local gdbres=$(pgrep -f "gdb.*--batch.*$SERVERBIN")
+ if [[ "$GDB_ENABLED" -eq 1 && -n "$gdbres" ]]; then
+ return 1
+ fi
+
+ # Check for binary process
+ local binres=$(pgrep -f "$SERVERBIN -c $CONFIG")
+ if [ -n "$binres" ]; then
+ return 1
+ fi
+
+ # Check session manager
+ if [ -n "$session_name" ]; then
+ case "$(get_session_manager "${SESSION_MANAGER:-auto}")" in
+ "tmux")
+ tmux has-session -t "$session_name" 2>/dev/null && return 1
+ ;;
+ "screen")
+ screen -ls "$session_name" 2>/dev/null | grep -q "$session_name" && return 1
+ ;;
+ esac
+ fi
+
+ return 0
+}
+
+# Run with session manager
+function run_with_session() {
+ local session_manager="$1"
+ local session_name="$2"
+ local wrapper="$3"
+ shift 3
+ local args=("$@")
+
+ if [ "$wrapper" = "simple-restarter" ]; then
+ script_path="$RUN_ENGINE_PATH/simple-restarter"
+ else
+ script_path="$RUN_ENGINE_PATH/starter"
+ fi
+
+ case "$session_manager" in
+ "tmux")
+ echo "> Starting with tmux session: $session_name - attach with 'tmux attach -t $session_name'"
+ tmux new-session -d -s "$session_name" -- "$script_path" "${args[@]}"
+ ;;
+ "screen")
+ local OPTIONS="-A -m -d -S"
+ if [ -n "$SCREEN_OPTIONS" ]; then
+ OPTIONS="$SCREEN_OPTIONS"
+ fi
+ echo "> Starting with screen session: $session_name (options: $OPTIONS) - attach with 'screen -r $session_name'"
+ echo "screen $OPTIONS \"$session_name\" -- \"$script_path\" ${args[*]}"
+ screen $OPTIONS "$session_name" -- "$script_path" "${args[@]}"
+ ;;
+ "none"|*)
+ echo "> Starting without session manager"
+ "$script_path" "${args[@]}"
+ ;;
+ esac
+}
+
+# Parse command line arguments
+function parse_arguments() {
+ local mode="$1"
+ local serverbin="$2"
+ shift 2
+
+ local config_file=""
+ local serverconfig=""
+ local session_manager=""
+
+ # Parse named arguments
+ while [[ $# -gt 0 ]]; do
+ case $1 in
+ --config)
+ config_file="$2"
+ shift 2
+ ;;
+ --server-config)
+ serverconfig="$2"
+ shift 2
+ ;;
+ --session-manager)
+ session_manager="$2"
+ shift 2
+ ;;
+ *)
+ echo "Unknown argument: $1"
+ return 1
+ ;;
+ esac
+ done
+
+ # Export parsed values for use by start_service
+ export PARSED_MODE="$mode"
+ export PARSED_SERVERBIN="$serverbin"
+ export PARSED_CONFIG_FILE="$config_file"
+ export PARSED_SERVERCONFIG="$serverconfig"
+ export PARSED_SESSION_MANAGER="$session_manager"
+}
+
+# Start service (single run or with simple-restarter)
+function start_service() {
+ local config_file="$1"
+ local serverbin_path="$2"
+ local serverconfig="$3"
+ local use_restarter="${4:-false}"
+ local session_manager_choice="$5"
+
+ # Load configuration first
+ load_config "$config_file"
+
+ # if no session manager is specified, get it from config
+ if [ -z "$session_manager_choice" ]; then
+ session_manager_choice="$SESSION_MANAGER"
+ fi
+
+
+ # Parse serverbin_path to extract BINPATH and SERVERBIN
+ if [ -n "$serverbin_path" ]; then
+ # If it's a full path, extract directory and binary name
+ if [[ "$serverbin_path" == */* ]]; then
+ BINPATH="$(dirname "$serverbin_path")"
+ SERVERBIN="$(basename "$serverbin_path")"
+ else
+ # If it's just a binary name, use it as-is (system PATH)
+ SERVERBIN="$serverbin_path"
+ BINPATH="${BINPATH:-""}" # Empty means use current directory or system PATH
+ fi
+ fi
+
+ # Use environment/config values if not set from command line
+ BINPATH="${BINPATH:-$RUN_ENGINE_BINPATH}"
+ SERVERBIN="${SERVERBIN:-$RUN_ENGINE_SERVERBIN}"
+ CONFIG="${serverconfig:-$RUN_ENGINE_CONFIG}"
+
+ echo "SERVERBIN: $SERVERBIN"
+
+ # Validate required parameters
+ if [ -z "$SERVERBIN" ]; then
+ echo "Error: SERVERBIN is required"
+ echo "Could not determine server binary from: $serverbin_path"
+ echo "Provide it as:"
+ echo " - Full path: $0 <mode> /path/to/bin/worldserver"
+ echo " - Binary name: $0 <mode> worldserver"
+ echo " - Environment variables: RUN_ENGINE_SERVERBIN"
+ echo " - Configuration file with SERVERBIN variable"
+ return 1
+ fi
+
+ # If BINPATH is set, validate binary exists and create log paths
+ if [ -n "$BINPATH" ]; then
+ if [ ! -d "$BINPATH" ]; then
+ echo "Error: BINPATH not found: $BINPATH"
+ return 1
+ fi
+
+ # Set up directories and logging relative to BINPATH
+ LOGS_PATH="${LOGS_PATH:-"$BINPATH/logs"}"
+ mkdir -p "$LOGS_PATH"
+ mkdir -p "$LOGS_PATH/crashes"
+ else
+ # For system binaries, try to detect binary location and create logs accordingly
+ local detected_binpath=""
+
+ # Try to find binary in system PATH
+ local binary_location=$(which "$SERVERBIN" 2>/dev/null)
+ if [ -n "$binary_location" ]; then
+ detected_binpath="$(dirname "$binary_location")"
+ echo "Binary found in system PATH: $binary_location"
+ # Set BINPATH to the detected location so starter script can find the binary
+ BINPATH="$detected_binpath"
+ fi
+
+ # Set up log paths based on detected or fallback location
+ if [ -n "$detected_binpath" ]; then
+ LOGS_PATH="${LOGS_PATH:-"$detected_binpath/logs"}"
+ else
+ # Fallback to current directory for logs
+ LOGS_PATH="${LOGS_PATH:-./logs}"
+ fi
+
+ CRASHES_PATH="${CRASHES_PATH:-"$LOGS_PATH/crashes"}"
+
+ mkdir -p "$LOGS_PATH"
+ mkdir -p "$CRASHES_PATH"
+ fi
+
+ # Set up logging names
+ LOG_PREFIX_NAME="${LOG_PREFIX_NAME:-${SERVERBIN%server}}"
+
+ # Set up session name (with backward compatibility for SCREEN_NAME)
+ SESSION_NAME="${SESSION_NAME:-$SCREEN_NAME}"
+ SESSION_NAME="${SESSION_NAME:-AC-${SERVERBIN%server}}"
+
+ configure_files
+
+ local session_manager=$(get_session_manager "$session_manager_choice")
+
+ if [ "$session_manager" = "error" ]; then
+ echo "Error: Invalid session manager specified: $session_manager_choice, is it installed?"
+ exit 1
+ fi
+
+ echo "Using session manager: $session_manager"
+ echo "Starting server: $SERVERBIN"
+
+ if [ -n "$CONFIG" ]; then
+ echo "Server config: $CONFIG"
+ else
+ echo "Server config: default (not specified)"
+ fi
+
+ if [ "$use_restarter" = "true" ]; then
+ # Use simple-restarter for restart functionality
+ local gdb_enabled="${GDB_ENABLED:-0}"
+ run_with_session "$session_manager" "$SESSION_NAME" "simple-restarter" "$BINPATH" "$SERVERBIN" "$GDB" "$CONFIG" "$SYSLOG" "$SYSERR" "$gdb_enabled" "$CRASHES_PATH"
+ else
+ # Single run using starter
+ local gdb_enabled="${GDB_ENABLED:-0}"
+ run_with_session "$session_manager" "$SESSION_NAME" "starter" "$BINPATH" "$SERVERBIN" "$GDB" "$CONFIG" "$SYSLOG" "$SYSERR" "$gdb_enabled" "$CRASHES_PATH"
+ fi
+}
+
+# Cleanup function
+function finish() {
+ local session_manager=$(get_session_manager "${SESSION_MANAGER:-auto}")
+ if [ -n "$SESSION_NAME" ]; then
+ case "$session_manager" in
+ "tmux")
+ tmux kill-session -t "$SESSION_NAME" 2>/dev/null || true
+ ;;
+ "screen")
+ screen -X -S "$SESSION_NAME" quit 2>/dev/null || true
+ ;;
+ esac
+ fi
+}
+
+# Legacy compatibility functions for old examples
+function restarter() {
+ echo "Legacy function 'restarter' called - redirecting to new API"
+ start_service "" "" "" "true" "${SESSION_MANAGER:-auto}"
+}
+
+function starter() {
+ echo "Legacy function 'starter' called - redirecting to new API"
+ start_service "" "" "" "false" "${SESSION_MANAGER:-auto}"
+}
+
+# Set trap for cleanup (currently disabled to avoid interfering with systemd)
+# trap finish EXIT
+
+# Main execution when script is run directly
+if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
+ case "${1:-help}" in
+ "start"|"restart")
+ if [ $# -lt 2 ]; then
+ echo "Error: Missing required arguments"
+ echo "Usage: $0 <mode> <serverbin> [options]"
+ echo "Example: $0 start worldserver --config ./conf-world.sh --server-config worldserver.conf"
+ exit 1
+ fi
+
+ # Parse arguments
+ if ! parse_arguments "$@"; then
+ exit 1
+ fi
+
+ # Determine restart mode
+ use_restarter="false"
+ if [ "$PARSED_MODE" = "restart" ]; then
+ use_restarter="true"
+ fi
+
+ # Start service with parsed arguments
+ start_service "$PARSED_CONFIG_FILE" "$PARSED_SERVERBIN" "$PARSED_SERVERCONFIG" "$use_restarter" "$PARSED_SESSION_MANAGER"
+ ;;
+ "help"|*)
+ echo "AzerothCore Run Engine"
+ echo ""
+ echo "Usage: $0 <mode> <serverbin> [options]"
+ echo ""
+ echo "Modes:"
+ echo " start - Start service once (no restart on crash)"
+ echo " restart - Start service with restart on crash (uses simple-restarter)"
+ echo ""
+ echo "Required Parameters:"
+ echo " serverbin - Server binary (full path or binary name)"
+ echo " Full path: /path/to/bin/worldserver"
+ echo " Binary name: worldserver (uses system PATH)"
+ echo ""
+ echo "Options:"
+ echo " --config <file> - Path to configuration file"
+ echo " --server-config <file> - Server configuration file (sets -c parameter)"
+ echo " --session-manager <type> - Session manager: none|auto|tmux|screen (default: auto)"
+ echo ""
+ echo "Configuration Priority (highest to lowest):"
+ echo " 1. conf.sh - User configuration file"
+ echo " 2. Command line arguments (--config, --server-config, etc.)"
+ echo " 3. Environment variables (RUN_ENGINE_*)"
+ echo " 4. conf.sh.dist - Default configuration"
+ echo ""
+ echo "Environment Variables:"
+ echo " RUN_ENGINE_CONFIG_FILE - Config file path"
+ echo " RUN_ENGINE_SESSION_MANAGER - Session manager (default: auto)"
+ echo " RUN_ENGINE_BINPATH - Binary directory path"
+ echo " RUN_ENGINE_SERVERBIN - Server binary name"
+ echo " RUN_ENGINE_CONFIG - Server configuration file"
+ echo " RUN_ENGINE_LOGS_PATH - Directory for log files"
+ echo " RUN_ENGINE_CRASHES_PATH - Directory for crash dumps"
+ echo " RUN_ENGINE_SESSION_NAME - Session name for tmux/screen"
+ echo ""
+ echo "Examples:"
+ echo ""
+ echo " # Using full path to binary"
+ echo " $0 start /home/user/ac/bin/worldserver"
+ echo ""
+ echo " # Using binary name (system PATH)"
+ echo " $0 start worldserver"
+ echo ""
+ echo " # With configuration file"
+ echo " $0 start worldserver --config ./conf-world.sh"
+ echo ""
+ echo " # With server configuration (sets -c parameter)"
+ echo " $0 start /path/to/bin/worldserver --server-config /etc/worldserver.conf"
+ echo ""
+ echo " # With session manager"
+ echo " $0 restart worldserver --session-manager tmux"
+ echo ""
+ echo " # Complete example"
+ echo " $0 restart /home/user/ac/bin/worldserver --config ./conf-world.sh --server-config worldserver.conf --session-manager screen"
+ echo ""
+ echo "Binary Resolution:"
+ echo " - Full path (contains /): Extracts directory and binary name"
+ echo " - Binary name only: Uses system PATH to find executable"
+ echo " Auto-detection will check current directory first, then system PATH"
+ echo ""
+ echo "Server Config:"
+ echo " If --server-config is specified, it's passed as -c parameter to the server."
+ echo " If not specified, the server will use its default configuration."
+ ;;
+ esac
+fi
+
diff --git a/apps/startup-scripts/src/service-manager.sh b/apps/startup-scripts/src/service-manager.sh
new file mode 100755
index 0000000000..1c44c9c247
--- /dev/null
+++ b/apps/startup-scripts/src/service-manager.sh
@@ -0,0 +1,1261 @@
+#!/usr/bin/env bash
+
+# AzerothCore Service Setup
+# A unified interface for managing AzerothCore services with PM2 or systemd
+# This script provides commands to create, update, delete, and manage server instances
+
+# Script location
+CURRENT_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+
+SCRIPT_DIR="$CURRENT_PATH"
+
+ROOT_DIR="$(cd "$CURRENT_PATH/../../.." && pwd)"
+
+# Configuration directory
+CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/azerothcore/services"
+REGISTRY_FILE="$CONFIG_DIR/service_registry.json"
+
+# Colors for output
+YELLOW='\033[1;33m'
+GREEN='\033[0;32m'
+RED='\033[0;31m'
+BLUE='\033[0;34m'
+NC='\033[0m' # No Color
+
+# Create config directory if it doesn't exist
+mkdir -p "$CONFIG_DIR"
+
+# Initialize registry if it doesn't exist
+if [ ! -f "$REGISTRY_FILE" ]; then
+ echo "[]" > "$REGISTRY_FILE"
+fi
+
+# Check dependencies
+check_dependencies() {
+ command -v jq >/dev/null 2>&1 || {
+ echo -e "${RED}Error: jq is required but not installed. Please install jq package.${NC}"
+ exit 1
+ }
+}
+
+# Check if PM2 is installed
+check_pm2() {
+ if ! command -v pm2 >/dev/null 2>&1; then
+ echo -e "${RED}Error: PM2 is not installed. Please install PM2 first:${NC}"
+ echo " npm install -g pm2"
+ return 1
+ fi
+}
+
+# Check if systemd is available
+check_systemd() {
+ if ! command -v systemctl >/dev/null 2>&1; then
+ echo -e "${RED}Error: systemd is not available on this system${NC}"
+ return 1
+ fi
+}
+
+# Auto-detect provider based on system availability
+auto_detect_provider() {
+ if check_systemd 2>/dev/null; then
+ echo "systemd"
+ elif check_pm2 2>/dev/null; then
+ echo "pm2"
+ else
+ echo -e "${RED}Error: Neither systemd nor PM2 is available on this system${NC}" >&2
+ echo -e "${YELLOW}Please install PM2 (npm install -g pm2) or ensure systemd is available${NC}" >&2
+ return 1
+ fi
+}
+
+# Helper functions
+function print_help() {
+
+ local base_name="$(basename $0)"
+
+ echo -e "${BLUE}AzerothCore Service Setup${NC}"
+ echo "A unified interface for managing AzerothCore services with PM2 or systemd"
+ echo ""
+ echo "Usage:"
+ echo " $base_name create <service-type> <service-name> [options]"
+ echo " $base_name update <service-name> [options]"
+ echo " $base_name delete <service-name>"
+ echo " $base_name list [provider]"
+ echo " $base_name start|stop|restart|status <service-name>"
+ echo " $base_name logs <service-name> [--follow]"
+ echo " $base_name attach <service-name>"
+ echo " $base_name edit-config <service-name>"
+ echo ""
+ echo "Providers:"
+ echo " pm2 - Use PM2 process manager"
+ echo " systemd - Use systemd service manager"
+ echo " auto - Automatically choose systemd or fallback to pm2 (default)"
+ echo ""
+ echo "Service Types:"
+ echo " auth - Authentication server"
+ echo " world - World server (use different names for multiple realms)"
+ echo ""
+ echo "Options:"
+ echo " --provider <type> - Service provider (pm2|systemd|auto, default: auto)"
+ echo " --bin-path <path> - Path to the server binary directory (required)"
+ echo " --server-config <path> - Path to the server configuration file"
+ echo " --session-manager <type> - Session manager (none|tmux|screen, default: none)"
+ echo " --gdb-enabled <0|1> - Enable GDB debugging (default: 0)"
+ echo " --system - Create as system service (systemd only, requires sudo)"
+ echo " --user - Create as user service (systemd only, default)"
+ echo " --max-memory <value> - Maximum memory limit (PM2 only)"
+ echo " --max-restarts <value> - Maximum restart attempts (PM2 only)"
+ echo " --no-start - Do not start the service after creation"
+ echo ""
+ echo "Examples:"
+ echo " # Create auth server (auto-detects provider)"
+ echo " $base_name create auth authserver --bin-path /home/user/azerothcore/bin"
+ echo ""
+ echo " # Create PM2 auth server explicitly"
+ echo " $base_name create auth authserver --provider pm2 --bin-path /home/user/azerothcore/bin"
+ echo ""
+ echo " # Create systemd world server with debugging enabled"
+ echo " $base_name create world worldserver-realm1 --provider systemd"
+ echo " --bin-path /home/user/azerothcore/bin"
+ echo " --server-config /home/user/azerothcore/etc/worldserver.conf"
+ echo " --gdb-enabled 1 --session-manager tmux"
+ echo ""
+ echo " # Create service without starting it"
+ echo " $base_name create auth authserver --bin-path /home/user/azerothcore/bin --no-start"
+ echo ""
+ echo " # Update run-engine configuration"
+ echo " $base_name update worldserver-realm1 --session-manager screen --gdb-enabled 0"
+ echo ""
+ echo " # Service management"
+ echo " $base_name start worldserver-realm1"
+ echo " $base_name logs worldserver-realm1 --follow"
+ echo " $base_name attach worldserver-realm1"
+ echo " $base_name list pm2"
+ echo ""
+ echo "Notes:"
+ echo " - Configuration editing modifies run-engine settings (GDB, session manager, etc.)"
+ echo " - Use --server-config for the actual server configuration file"
+ echo " - Services use run-engine in 'start' mode for single-shot execution"
+ echo " - Restart on crash is handled by PM2 or systemd, not by run-engine"
+ 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"
+}
+
+function register_service() {
+ local service_name="$1"
+ local provider="$2"
+ local service_type="$3"
+ local config_file="$CONFIG_DIR/$service_name.conf"
+
+ # Add to registry
+ local tmp_file=$(mktemp)
+ jq --arg name "$service_name" \
+ --arg provider "$provider" \
+ --arg type "$service_type" \
+ --arg config "$config_file" \
+ '. += [{"name": $name, "provider": $provider, "type": $type, "config": $config}]' \
+ "$REGISTRY_FILE" > "$tmp_file"
+ mv "$tmp_file" "$REGISTRY_FILE"
+
+ echo -e "${GREEN}Service $service_name registered successfully${NC}"
+}
+
+function validate_service_exists() {
+ local service_name="$1"
+ local provider="$2"
+
+ if [ "$provider" = "pm2" ]; then
+ # Check if service exists in PM2
+ if ! pm2 id "$service_name" > /dev/null 2>&1; then
+ return 1 # Service not found
+ fi
+ elif [ "$provider" = "systemd" ]; then
+ # Check if service exists in systemd
+ local systemd_type="--user"
+ if [ -f "/etc/systemd/system/$service_name.service" ]; then
+ systemd_type="--system"
+ fi
+
+ if [ "$systemd_type" = "--system" ]; then
+ if ! systemctl is-active "$service_name.service" >/dev/null 2>&1 && \
+ ! systemctl is-enabled "$service_name.service" >/dev/null 2>&1 && \
+ ! systemctl is-failed "$service_name.service" >/dev/null 2>&1; then
+ return 1 # Service not found
+ fi
+ else
+ if ! systemctl --user is-active "$service_name.service" >/dev/null 2>&1 && \
+ ! systemctl --user is-enabled "$service_name.service" >/dev/null 2>&1 && \
+ ! systemctl --user is-failed "$service_name.service" >/dev/null 2>&1; then
+ return 1 # Service not found
+ fi
+ fi
+ fi
+
+ return 0 # Service exists
+}
+
+function sync_registry() {
+ echo -e "${YELLOW}Syncing service registry with actual services...${NC}"
+
+ local services=$(jq -c '.[]' "$REGISTRY_FILE")
+ local tmp_file=$(mktemp)
+
+ # Initialize with empty array
+ echo "[]" > "$tmp_file"
+
+ # Check each service in registry
+ while read -r service_info; do
+ if [ -n "$service_info" ]; then
+ local name=$(echo "$service_info" | jq -r '.name')
+ local provider=$(echo "$service_info" | jq -r '.provider')
+
+ if validate_service_exists "$name" "$provider"; then
+ # Service exists, add it to the new registry
+ jq --argjson service "$service_info" '. += [$service]' "$tmp_file" > "$tmp_file.new"
+ mv "$tmp_file.new" "$tmp_file"
+ else
+ echo -e "${YELLOW}Service '$name' no longer exists. Removing from registry.${NC}"
+ # Don't add to new registry
+ fi
+ fi
+ done <<< "$services"
+
+ # Replace registry with synced version
+ mv "$tmp_file" "$REGISTRY_FILE"
+ echo -e "${GREEN}Registry synchronized.${NC}"
+}
+
+function unregister_service() {
+ local service_name="$1"
+
+ # Remove from registry
+ local tmp_file=$(mktemp)
+ jq --arg name "$service_name" '. | map(select(.name != $name))' "$REGISTRY_FILE" > "$tmp_file"
+ mv "$tmp_file" "$REGISTRY_FILE"
+
+ # Remove configuration file
+ rm -f "$CONFIG_DIR/$service_name.conf"
+
+ echo -e "${GREEN}Service $service_name unregistered${NC}"
+}
+
+function get_service_info() {
+ local service_name="$1"
+ jq --arg name "$service_name" '.[] | select(.name == $name)' "$REGISTRY_FILE"
+}
+
+# PM2 service management functions
+function pm2_create_service() {
+ local service_name="$1"
+ local command="$2"
+ shift 2
+
+ check_pm2 || return 1
+
+ # Parse additional PM2 options
+ local max_memory=""
+ local max_restarts=""
+ local additional_args=""
+
+ while [[ $# -gt 0 ]]; do
+ case "$1" in
+ --max-memory)
+ max_memory="$2"
+ shift 2
+ ;;
+ --max-restarts)
+ max_restarts="$2"
+ shift 2
+ ;;
+ *)
+ additional_args+=" $1"
+ shift
+ ;;
+ esac
+ done
+
+ # Build PM2 start command
+ local pm2_cmd="pm2 start '$command$additional_args' --name '$service_name'"
+
+ # Add memory limit if specified
+ if [ -n "$max_memory" ]; then
+ pm2_cmd+=" --max-memory-restart $max_memory"
+ fi
+
+ # Add max restarts if specified
+ if [ -n "$max_restarts" ]; then
+ pm2_cmd+=" --max-restarts $max_restarts"
+ fi
+
+ # Execute command
+ echo -e "${YELLOW}Creating PM2 service: $service_name${NC}"
+
+ if eval "$pm2_cmd"; then
+ echo -e "${GREEN}PM2 service '$service_name' created successfully${NC}"
+ pm2 save
+ return 0
+ else
+ echo -e "${RED}Failed to create PM2 service '$service_name'${NC}"
+ return 1
+ fi
+}
+
+function pm2_remove_service() {
+ local service_name="$1"
+
+ check_pm2 || return 1
+
+ echo -e "${YELLOW}Stopping and removing PM2 service: $service_name${NC}"
+
+ # Stop the service if it's running
+ if pm2 id "$service_name" > /dev/null 2>&1; then
+ pm2 stop "$service_name" 2>/dev/null || true
+ pm2 delete "$service_name" 2>/dev/null
+
+ # Verify the service was removed
+ if pm2 id "$service_name" > /dev/null 2>&1; then
+ echo -e "${RED}Failed to remove PM2 service '$service_name'${NC}"
+ return 1
+ fi
+
+ pm2 save
+ echo -e "${GREEN}PM2 service '$service_name' stopped and removed${NC}"
+ else
+ echo -e "${YELLOW}PM2 service '$service_name' not found or already removed${NC}"
+ fi
+
+ return 0
+}
+
+function pm2_service_action() {
+ local action="$1"
+ local service_name="$2"
+
+ check_pm2 || return 1
+
+ echo -e "${YELLOW}${action^} PM2 service: $service_name${NC}"
+ pm2 "$action" "$service_name"
+}
+
+function pm2_service_logs() {
+ local service_name="$1"
+ local follow="$2"
+
+ check_pm2 || return 1
+
+ echo -e "${YELLOW}Showing PM2 logs for: $service_name${NC}"
+ if [ "$follow" = "true" ]; then
+ pm2 logs "$service_name" --lines 50
+ else
+ pm2 logs "$service_name" --lines 50 --nostream
+ fi
+}
+
+# Systemd service management functions
+function get_systemd_dir() {
+ local type="$1"
+ if [ "$type" = "--system" ]; then
+ echo "/etc/systemd/system"
+ else
+ echo "${XDG_CONFIG_HOME:-$HOME/.config}/systemd/user"
+ fi
+}
+
+function systemd_create_service() {
+ local service_name="$1"
+ local command="$2"
+ local systemd_type="--user"
+ shift 2
+
+ check_systemd || return 1
+
+ # Parse systemd type
+ while [[ $# -gt 0 ]]; do
+ case "$1" in
+ --system|--user)
+ systemd_type="$1"
+ shift
+ ;;
+ *)
+ command+=" $1"
+ shift
+ ;;
+ esac
+ done
+
+ local systemd_dir=$(get_systemd_dir "$systemd_type")
+ local service_file="$systemd_dir/$service_name.service"
+
+ # Create systemd directory if it doesn't exist
+ if [ "$systemd_type" = "--system" ]; then
+ if [ "$EUID" -ne 0 ]; then
+ echo -e "${RED}Error: System services require root privileges. Use sudo.${NC}"
+ return 1
+ fi
+ mkdir -p "$systemd_dir"
+ else
+ mkdir -p "$systemd_dir"
+ fi
+
+ # Create service file
+ echo -e "${YELLOW}Creating systemd service: $service_name${NC}"
+
+ if [ "$systemd_type" = "--system" ]; then
+ # System service template (with User directive)
+ cat > "$service_file" << EOF
+[Unit]
+Description=AzerothCore $service_name
+After=network.target
+
+[Service]
+Type=forking
+ExecStart=$command
+Restart=always
+RestartSec=3
+User=$(whoami)
+Group=$(id -gn)
+WorkingDirectory=$(realpath "$bin_path")
+StandardOutput=journal+console
+StandardError=journal+console
+
+[Install]
+WantedBy=multi-user.target
+EOF
+ else
+ # User service template (no User/Group directives)
+ cat > "$service_file" << EOF
+[Unit]
+Description=AzerothCore $service_name
+After=network.target
+
+[Service]
+Type=forking
+ExecStart=$command
+Restart=always
+RestartSec=3
+WorkingDirectory=$(realpath "$bin_path")
+StandardOutput=journal+console
+StandardError=journal+console
+
+[Install]
+WantedBy=default.target
+EOF
+ fi
+
+ if [ "$systemd_type" = "--system" ]; then
+ sed -i 's/WantedBy=default.target/WantedBy=multi-user.target/' "$service_file"
+ fi
+
+ # Reload systemd and enable service
+ if [ "$systemd_type" = "--system" ]; then
+ systemctl daemon-reload
+ systemctl enable "$service_name.service"
+ else
+ systemctl --user daemon-reload
+ systemctl --user enable "$service_name.service"
+ fi
+
+ echo -e "${GREEN}Systemd service '$service_name' created successfully${NC}"
+ return 0
+}
+
+function systemd_remove_service() {
+ local service_name="$1"
+ local systemd_type="--user"
+
+ check_systemd || return 1
+
+ # Try to determine if it's a system or user service
+ if [ -f "/etc/systemd/system/$service_name.service" ]; then
+ systemd_type="--system"
+ fi
+
+ local systemd_dir=$(get_systemd_dir "$systemd_type")
+ local service_file="$systemd_dir/$service_name.service"
+
+ echo -e "${YELLOW}Stopping and removing systemd service: $service_name (${systemd_type#--})${NC}"
+
+ # Check if service file exists
+ if [ ! -f "$service_file" ]; then
+ echo -e "${YELLOW}Systemd service file '$service_file' not found or already removed${NC}"
+ return 0
+ fi
+
+ # Stop and disable service
+ local removal_failed=false
+ if [ "$systemd_type" = "--system" ]; then
+ if [ "$EUID" -ne 0 ]; then
+ echo -e "${RED}Error: System services require root privileges. Use sudo.${NC}"
+ return 1
+ fi
+ systemctl stop "$service_name.service" 2>/dev/null || true
+ systemctl disable "$service_name.service" 2>/dev/null || true
+ systemctl daemon-reload
+
+ # Verify service is no longer active
+ if systemctl is-active "$service_name.service" >/dev/null 2>&1; then
+ echo -e "${RED}Warning: Failed to stop system service '$service_name'${NC}"
+ removal_failed=true
+ fi
+ else
+ systemctl --user stop "$service_name.service" 2>/dev/null || true
+ systemctl --user disable "$service_name.service" 2>/dev/null || true
+ systemctl --user daemon-reload
+
+ # Verify service is no longer active
+ if systemctl --user is-active "$service_name.service" >/dev/null 2>&1; then
+ echo -e "${RED}Warning: Failed to stop user service '$service_name'${NC}"
+ removal_failed=true
+ fi
+ fi
+
+ # Remove service file
+ if rm -f "$service_file"; then
+ echo -e "${GREEN}Systemd service '$service_name' stopped and removed${NC}"
+ if [ "$removal_failed" = "true" ]; then
+ echo -e "${YELLOW}Note: Service may still be running but configuration was removed${NC}"
+ fi
+ return 0
+ else
+ echo -e "${RED}Failed to remove systemd service file '$service_file'${NC}"
+ return 1
+ fi
+}
+
+function systemd_service_action() {
+ local action="$1"
+ local service_name="$2"
+ local systemd_type="--user"
+
+ check_systemd || return 1
+
+ # Try to determine if it's a system or user service
+ if [ -f "/etc/systemd/system/$service_name.service" ]; then
+ systemd_type="--system"
+ fi
+
+ echo -e "${YELLOW}${action^} systemd service: $service_name${NC}"
+
+ if [ "$systemd_type" = "--system" ]; then
+ systemctl "$action" "$service_name.service"
+ else
+ systemctl --user "$action" "$service_name.service"
+ fi
+}
+
+function systemd_service_logs() {
+ local service_name="$1"
+ local follow="$2"
+ local systemd_type="--user"
+
+ check_systemd || return 1
+
+ # Try to determine if it's a system or user service
+ if [ -f "/etc/systemd/system/$service_name.service" ]; then
+ systemd_type="--system"
+ fi
+
+ echo -e "${YELLOW}Showing systemd logs for: $service_name${NC}"
+ if [ "$follow" = "true" ]; then
+ if [ "$systemd_type" = "--system" ]; then
+ journalctl --unit="$service_name.service" -e -f
+ else
+ journalctl --user-unit="$service_name.service" -e -f
+ fi
+ else
+ if [ "$systemd_type" = "--system" ]; then
+ journalctl --unit="$service_name.service" -e --lines 50 --no-pager
+ else
+ journalctl --user-unit="$service_name.service" -e --lines 50 --no-pager
+ fi
+ fi
+}
+
+function create_service() {
+ local service_type="$1"
+ local service_name="$2"
+ shift 2
+
+ # Validate service type
+ if [[ "$service_type" != "auth" && "$service_type" != "world" ]]; then
+ echo -e "${RED}Error: Invalid service type. Use 'auth' or 'world'${NC}"
+ return 1
+ fi
+
+ # Check if service already exists
+ if [ -n "$(get_service_info "$service_name")" ]; then
+ echo -e "${RED}Error: Service '$service_name' already exists${NC}"
+ return 1
+ fi
+
+ # Default values for run-engine configuration
+ local provider="auto"
+ local bin_path="$BINPATH/bin" # get from config or environment
+ local server_config=""
+ local session_manager="none"
+ local gdb_enabled="0"
+ local systemd_type="--user"
+ local pm2_opts=""
+ local auto_start="true"
+
+ # Parse options
+ while [[ $# -gt 0 ]]; do
+ case "$1" in
+ --provider)
+ provider="$2"
+ shift 2
+ ;;
+ --bin-path)
+ bin_path="$2"
+ shift 2
+ ;;
+ --server-config)
+ server_config="$2"
+ shift 2
+ ;;
+ --session-manager)
+ session_manager="$2"
+ shift 2
+ ;;
+ --gdb-enabled)
+ gdb_enabled="$2"
+ shift 2
+ ;;
+ --system)
+ systemd_type="--system"
+ shift
+ ;;
+ --user)
+ systemd_type="--user"
+ shift
+ ;;
+ --max-memory|--max-restarts)
+ pm2_opts="$pm2_opts $1 $2"
+ shift 2
+ ;;
+ --no-start)
+ auto_start="false"
+ shift
+ ;;
+ *)
+ echo -e "${RED}Error: Unknown option: $1${NC}"
+ return 1
+ ;;
+ esac
+ done
+
+ # Auto-detect provider if set to auto
+ if [ "$provider" = "auto" ]; then
+ if ! provider=$(auto_detect_provider); then
+ return 1
+ fi
+ echo -e "${BLUE}Auto-detected provider: $provider${NC}"
+ fi
+
+ # Validate provider
+ if [[ "$provider" != "pm2" && "$provider" != "systemd" ]]; then
+ echo -e "${RED}Error: Invalid provider. Use 'pm2', 'systemd', or 'auto'${NC}"
+ return 1
+ fi
+
+ # Determine server binary based on service type
+ local server_bin="${service_type}server"
+ local server_binary_path=$(realpath "$bin_path/$server_bin")
+
+ # Check if binary exists
+ if [ ! -f "$server_binary_path" ]; then
+ echo -e "${RED}Error: Server binary not found: $server_binary_path${NC}"
+ return 1
+ fi
+
+ # Create run-engine configuration file for this service
+ local run_engine_config="$CONFIG_DIR/$service_name-run-engine.conf"
+ cat > "$run_engine_config" << EOF
+# run-engine configuration for service: $service_name
+# This file contains run-engine specific settings
+
+# Enable/disable GDB execution
+export GDB_ENABLED=$gdb_enabled
+
+# Session manager (none|auto|tmux|screen)
+export SESSION_MANAGER="$session_manager"
+
+# Session name for tmux/screen (optional)
+export SESSION_NAME="${service_name}"
+
+# Binary directory path
+export BINPATH="$bin_path"
+
+# Server binary name
+export SERVERBIN="$server_bin"
+
+# Server configuration file path
+export CONFIG="$server_config"
+
+# Show console output for easier debugging
+export WITH_CONSOLE=1
+EOF
+
+ # Create service configuration file for our registry
+ cat > "$CONFIG_DIR/$service_name.conf" << EOF
+# AzerothCore service configuration for $service_name
+# Created: $(date)
+# Provider: $provider
+# Service Type: $service_type
+
+# run-engine configuration file
+RUN_ENGINE_CONFIG_FILE="$run_engine_config"
+
+# Provider-specific options
+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"
+
+ # 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" $pm2_opts; then
+ service_creation_success=true
+ fi
+ else
+ if pm2_create_service "$service_name" "$run_engine_cmd"; then
+ service_creation_success=true
+ fi
+ fi
+
+ elif [ "$provider" = "systemd" ]; then
+ if systemd_create_service "$service_name" "$run_engine_cmd" "$systemd_type"; then
+ service_creation_success=true
+ fi
+ fi
+
+ # Check if service creation was successful
+ if [ "$service_creation_success" = "true" ]; then
+ # Register the service
+ register_service "$service_name" "$provider" "$service_type"
+ echo -e "${GREEN}Service '$service_name' created successfully${NC}"
+ echo -e "${BLUE}Run-engine config: $run_engine_config${NC}"
+
+ # Auto-start the service unless --no-start was specified
+ if [ "$auto_start" = "true" ]; then
+ echo -e "${YELLOW}Starting service '$service_name'...${NC}"
+ if service_action "start" "$service_name"; then
+ echo -e "${GREEN}Service '$service_name' started successfully${NC}"
+ else
+ echo -e "${YELLOW}Warning: Service '$service_name' was created but failed to start${NC}"
+ echo -e "${BLUE}You can start it manually with: $0 start $service_name${NC}"
+ fi
+ else
+ echo -e "${BLUE}Service created but not started (--no-start specified)${NC}"
+ echo -e "${BLUE}Start it manually with: $0 start $service_name${NC}"
+ fi
+ else
+ # Remove configuration files if service creation failed
+ rm -f "$CONFIG_DIR/$service_name.conf"
+ rm -f "$run_engine_config"
+ echo -e "${RED}Failed to create service '$service_name'${NC}"
+ return 1
+ fi
+}
+
+function update_service() {
+ local service_name="$1"
+ shift
+
+ # Check if service exists
+ local service_info=$(get_service_info "$service_name")
+ if [ -z "$service_info" ]; then
+ echo -e "${RED}Error: Service '$service_name' not found${NC}"
+ return 1
+ fi
+
+ # Extract service information
+ local provider=$(echo "$service_info" | jq -r '.provider')
+ local service_type=$(echo "$service_info" | jq -r '.type')
+ local config_file=$(echo "$service_info" | jq -r '.config')
+
+ # Load current configuration
+ source "$config_file"
+
+ # Load current run-engine configuration
+ if [ -f "$RUN_ENGINE_CONFIG_FILE" ]; then
+ source "$RUN_ENGINE_CONFIG_FILE"
+ fi
+
+ # Parse options to update
+ local config_updated=false
+ while [[ $# -gt 0 ]]; do
+ case "$1" in
+ --bin-path)
+ export BINPATH="$2"
+ config_updated=true
+ shift 2
+ ;;
+ --server-config)
+ export SERVER_CONFIG="$2"
+ config_updated=true
+ shift 2
+ ;;
+ --session-manager)
+ export SESSION_MANAGER="$2"
+ config_updated=true
+ shift 2
+ ;;
+ --gdb-enabled)
+ export GDB_ENABLED="$2"
+ config_updated=true
+ shift 2
+ ;;
+ --system)
+ SYSTEMD_TYPE="--system"
+ shift
+ ;;
+ --user)
+ SYSTEMD_TYPE="--user"
+ shift
+ ;;
+ --max-memory|--max-restarts)
+ PM2_OPTS="$PM2_OPTS $1 $2"
+ shift 2
+ ;;
+ *)
+ echo -e "${RED}Error: Unknown option: $1${NC}"
+ return 1
+ ;;
+ esac
+ done
+
+ if [ "$config_updated" = "true" ]; then
+ # Update run-engine configuration file
+ cat > "$RUN_ENGINE_CONFIG_FILE" << EOF
+# run-engine configuration for service: $service_name
+# Updated: $(date)
+
+# Enable/disable GDB execution
+export GDB_ENABLED=${GDB_ENABLED:-0}
+
+# Session manager (none|auto|tmux|screen)
+export SESSION_MANAGER="${SESSION_MANAGER:-none}"
+
+# Session name for tmux/screen
+export SESSION_NAME="${service_name}"
+
+# Binary directory path
+export BINPATH="${BINPATH}"
+
+# Server binary name
+export SERVERBIN="${SERVERBIN}"
+
+# Server configuration file path
+export CONFIG="${SERVER_CONFIG}"
+EOF
+
+ echo -e "${GREEN}Run-engine configuration updated: $RUN_ENGINE_CONFIG_FILE${NC}"
+ echo -e "${YELLOW}Note: Restart the service to apply changes${NC}"
+ else
+ echo -e "${YELLOW}No run-engine configuration changes made${NC}"
+ fi
+
+ # Update service configuration
+ cat > "$config_file" << EOF
+# AzerothCore service configuration for $service_name
+# Updated: $(date)
+# Provider: $provider
+# Service Type: $service_type
+
+# run-engine configuration file
+RUN_ENGINE_CONFIG_FILE="$RUN_ENGINE_CONFIG_FILE"
+
+# Provider-specific options
+SYSTEMD_TYPE="$SYSTEMD_TYPE"
+PM2_OPTS="$PM2_OPTS"
+EOF
+}
+
+function delete_service() {
+ local service_name="$1"
+
+ # Check if service exists
+ local service_info=$(get_service_info "$service_name")
+ if [ -z "$service_info" ]; then
+ echo -e "${RED}Error: Service '$service_name' not found${NC}"
+ return 1
+ fi
+
+ # Extract provider and config
+ local provider=$(echo "$service_info" | jq -r '.provider')
+ local config_file=$(echo "$service_info" | jq -r '.config')
+
+ # Load configuration to get run-engine config file
+ if [ -f "$config_file" ]; then
+ source "$config_file"
+ fi
+
+ echo -e "${YELLOW}Deleting service '$service_name' (provider: $provider)...${NC}"
+
+ # Stop and remove the service
+ local removal_success=false
+ if [ "$provider" = "pm2" ]; then
+ if pm2_remove_service "$service_name"; then
+ removal_success=true
+ fi
+ elif [ "$provider" = "systemd" ]; then
+ if systemd_remove_service "$service_name"; then
+ removal_success=true
+ fi
+ fi
+
+ if [ "$removal_success" = "true" ]; then
+ # Remove run-engine configuration file
+ 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
+
+ # Unregister service
+ unregister_service "$service_name"
+ echo -e "${GREEN}Service '$service_name' deleted successfully${NC}"
+ else
+ echo -e "${RED}Failed to remove service '$service_name' from $provider${NC}"
+ return 1
+ fi
+}
+
+function list_services() {
+ local provider_filter="$1"
+
+ # Sync registry first
+ sync_registry
+
+ echo -e "${BLUE}AzerothCore Services${NC}"
+ echo "====================="
+
+ if [ "$(jq 'length' "$REGISTRY_FILE")" = "0" ]; then
+ echo "No services registered"
+ return
+ fi
+
+ # Show PM2 services
+ if [ -z "$provider_filter" ] || [ "$provider_filter" = "pm2" ]; then
+ local pm2_services=$(jq -r '.[] | select(.provider == "pm2") | .name' "$REGISTRY_FILE" 2>/dev/null)
+ if [ -n "$pm2_services" ] && command -v pm2 >/dev/null 2>&1; then
+ echo -e "\n${YELLOW}PM2 Services:${NC}"
+ pm2 list
+ fi
+ fi
+
+ # Show systemd services
+ if [ -z "$provider_filter" ] || [ "$provider_filter" = "systemd" ]; then
+ local systemd_services=$(jq -r '.[] | select(.provider == "systemd") | .name' "$REGISTRY_FILE" 2>/dev/null)
+ if [ -n "$systemd_services" ] && command -v systemctl >/dev/null 2>&1; then
+ echo -e "\n${YELLOW}Systemd User Services:${NC}"
+ while read -r service_name; do
+ if [ -n "$service_name" ]; then
+ systemctl --user status "$service_name.service" --no-pager -l || true
+ echo ""
+ fi
+ done <<< "$systemd_services"
+
+ # Also check for system services
+ local system_services=""
+ while read -r service_name; do
+ if [ -n "$service_name" ] && [ -f "/etc/systemd/system/$service_name.service" ]; then
+ system_services+="$service_name "
+ fi
+ done <<< "$systemd_services"
+
+ if [ -n "$system_services" ]; then
+ echo -e "${YELLOW}Systemd System Services:${NC}"
+ for service_name in $system_services; do
+ systemctl status "$service_name.service" --no-pager -l || true
+ echo ""
+ done
+ fi
+ fi
+ fi
+}
+
+function service_action() {
+ local action="$1"
+ local service_name="$2"
+
+ # Check if service exists
+ local service_info=$(get_service_info "$service_name")
+ if [ -z "$service_info" ]; then
+ echo -e "${RED}Error: Service '$service_name' not found${NC}"
+ return 1
+ fi
+
+ # Extract provider
+ local provider=$(echo "$service_info" | jq -r '.provider')
+
+ # Execute action
+ if [ "$provider" = "pm2" ]; then
+ pm2_service_action "$action" "$service_name"
+ elif [ "$provider" = "systemd" ]; then
+ systemd_service_action "$action" "$service_name"
+ fi
+}
+
+function service_logs() {
+ local service_name="$1"
+ local follow="${2:-false}"
+
+ # Check if service exists
+ local service_info=$(get_service_info "$service_name")
+ if [ -z "$service_info" ]; then
+ echo -e "${RED}Error: Service '$service_name' not found${NC}"
+ return 1
+ fi
+
+ # Extract provider
+ local provider=$(echo "$service_info" | jq -r '.provider')
+
+ # Show logs
+ if [ "$provider" = "pm2" ]; then
+ pm2_service_logs "$service_name" "$follow"
+ elif [ "$provider" = "systemd" ]; then
+ systemd_service_logs "$service_name" "$follow"
+ fi
+}
+
+function edit_config() {
+ local service_name="$1"
+
+ # Check if service exists
+ local service_info=$(get_service_info "$service_name")
+ if [ -z "$service_info" ]; then
+ echo -e "${RED}Error: Service '$service_name' not found${NC}"
+ return 1
+ fi
+
+ # Get configuration file path
+ local config_file=$(echo "$service_info" | jq -r '.config')
+
+ # Load configuration to get run-engine config file
+ source "$config_file"
+
+ # 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}"
+ ${EDITOR:-nano} "$RUN_ENGINE_CONFIG_FILE"
+
+ echo -e "${GREEN}Configuration updated. Run '$0 restart $service_name' to apply changes.${NC}"
+}
+
+function attach_to_service() {
+ local service_name="$1"
+
+ # Check if service exists
+ local service_info=$(get_service_info "$service_name")
+ if [ -z "$service_info" ]; then
+ echo -e "${RED}Error: Service '$service_name' not found${NC}"
+ return 1
+ fi
+
+ # Extract provider
+ local provider=$(echo "$service_info" | jq -r '.provider')
+ local config_file=$(echo "$service_info" | jq -r '.config')
+
+ # Load configuration to get run-engine config file
+ if [ ! -f "$config_file" ]; then
+ echo -e "${RED}Error: Service configuration file not found: $config_file${NC}"
+ return 1
+ fi
+
+ 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}"
+ return 1
+ fi
+
+ source "$RUN_ENGINE_CONFIG_FILE"
+
+ # Auto-detect session manager and attach accordingly
+ case "$SESSION_MANAGER" in
+ "tmux")
+ attach_tmux_session "$service_name" "$provider"
+ ;;
+ "screen")
+ attach_screen_session "$service_name" "$provider"
+ ;;
+ "none"|"auto"|*)
+ # No session manager - launch interactive shell directly
+ attach_interactive_shell "$service_name" "$provider"
+ ;;
+ esac
+}
+
+function attach_interactive_shell() {
+ local service_name="$1"
+ local provider="$2"
+
+ # Get service info again to access configuration
+ local service_info=$(get_service_info "$service_name")
+ local config_file=$(echo "$service_info" | jq -r '.config')
+
+ source "$config_file"
+ source "$RUN_ENGINE_CONFIG_FILE"
+
+ echo -e "${RED}Error: Cannot attach to service '$service_name'${NC} [for now]"
+ echo -e "${YELLOW}Interactive attachment requires a session manager (tmux or screen).${NC}"
+ echo ""
+ echo -e "${BLUE}Current session manager: $SESSION_MANAGER${NC}"
+ echo ""
+ echo -e "${YELLOW}To enable interactive attachment:${NC}"
+ echo " 1. Update the service to use tmux or screen:"
+ echo " $0 update $service_name --session-manager tmux"
+ echo " 2. Restart the service:"
+ echo " $0 restart $service_name"
+ echo " 3. Then try attach again:"
+ echo " $0 attach $service_name"
+ echo ""
+ echo -e "${BLUE}Alternative: Use 'logs <service_name> --follow' to monitor the service:${NC}"
+ echo " $0 logs $service_name --follow"
+
+ return 1
+}
+
+function attach_tmux_session() {
+ local service_name="$1"
+ local provider="$2"
+
+ # Check if tmux is available
+ if ! command -v tmux >/dev/null 2>&1; then
+ echo -e "${RED}Error: tmux is not installed${NC}"
+ echo -e "${BLUE}Starting interactive session without tmux...${NC}"
+ attach_interactive_shell "$service_name" "$provider"
+ return
+ fi
+
+ # Try to attach to tmux session
+ local tmux_session="$service_name"
+ echo -e "${YELLOW}Attempting to attach to tmux session: $tmux_session${NC}"
+
+ if tmux has-session -t "$tmux_session" 2>/dev/null; then
+ echo -e "${GREEN}Attaching to tmux session...${NC}"
+ tmux attach-session -t "$tmux_session"
+ else
+ echo -e "${RED}Error: tmux session '$tmux_session' not found${NC}"
+ echo -e "${YELLOW}Available tmux sessions:${NC}"
+ tmux list-sessions 2>/dev/null || echo "No active tmux sessions"
+ echo -e "${BLUE}Starting new interactive session instead...${NC}"
+ attach_interactive_shell "$service_name" "$provider"
+ fi
+}
+
+function attach_screen_session() {
+ local service_name="$1"
+ local provider="$2"
+
+ # Check if screen is available
+ if ! command -v screen >/dev/null 2>&1; then
+ echo -e "${RED}Error: screen is not installed${NC}"
+ echo -e "${BLUE}Starting interactive session without screen...${NC}"
+ attach_interactive_shell "$service_name" "$provider"
+ return
+ fi
+
+ # Try to attach to screen session
+ local screen_session="$service_name"
+ echo -e "${YELLOW}Attempting to attach to screen session: $screen_session${NC}"
+
+ if screen -list | grep -q "$screen_session"; then
+ echo -e "${GREEN}Attaching to screen session...${NC}"
+ screen -r "$screen_session"
+ else
+ echo -e "${RED}Error: screen session '$screen_session' not found${NC}"
+ echo -e "${YELLOW}Available screen sessions:${NC}"
+ screen -list 2>/dev/null || echo "No active screen sessions"
+ echo -e "${BLUE}Starting new interactive session instead...${NC}"
+ attach_interactive_shell "$service_name" "$provider"
+ fi
+}
+
+
+
+
+# Main execution
+check_dependencies
+
+# Main command processing
+case "${1:-help}" in
+ create)
+ if [ $# -lt 3 ]; then
+ echo -e "${RED}Error: Not enough arguments for create command${NC}"
+ print_help
+ exit 1
+ fi
+ create_service "$2" "$3" "${@:4}"
+ ;;
+ update)
+ if [ $# -lt 2 ]; then
+ echo -e "${RED}Error: Service name required for update command${NC}"
+ print_help
+ exit 1
+ fi
+ update_service "$2" "${@:3}"
+ ;;
+ delete)
+ if [ $# -lt 2 ]; then
+ echo -e "${RED}Error: Service name required for delete command${NC}"
+ print_help
+ exit 1
+ fi
+ delete_service "$2"
+ ;;
+ list)
+ list_services "$2"
+ ;;
+ start|stop|restart|status)
+ if [ $# -lt 2 ]; then
+ echo -e "${RED}Error: Service name required for $1 command${NC}"
+ print_help
+ exit 1
+ fi
+ service_action "$1" "$2"
+ ;;
+ logs)
+ if [ $# -lt 2 ]; then
+ echo -e "${RED}Error: Service name required for logs command${NC}"
+ print_help
+ exit 1
+ fi
+ if [ "$3" = "--follow" ]; then
+ service_logs "$2" "true"
+ else
+ service_logs "$2" "false"
+ fi
+ ;;
+ edit-config)
+ if [ $# -lt 2 ]; then
+ echo -e "${RED}Error: Service name required for edit-config command${NC}"
+ print_help
+ exit 1
+ fi
+ edit_config "$2"
+ ;;
+ attach)
+ if [ $# -lt 2 ]; then
+ echo -e "${RED}Error: Service name required for attach command${NC}"
+ print_help
+ exit 1
+ fi
+ attach_to_service "$2"
+ ;;
+ help|--help|-h)
+ print_help
+ ;;
+ *)
+ echo -e "${RED}Error: Unknown command: $1${NC}"
+ print_help
+ exit 1
+ ;;
+esac
diff --git a/apps/startup-scripts/src/simple-restarter b/apps/startup-scripts/src/simple-restarter
new file mode 100755
index 0000000000..c5540e4ab9
--- /dev/null
+++ b/apps/startup-scripts/src/simple-restarter
@@ -0,0 +1,89 @@
+#!/usr/bin/env bash
+
+# AzerothCore Simple Restarter
+# This script is a wrapper around the starter script that provides restart functionality
+# and maintains compatibility with the acore dashboard
+#
+# Usage: simple-restarter <binary> [gdb_file] [config] [syslog] [syserr] [gdb_enabled] [crashes_path]
+#
+# Parameters (same as starter):
+# $1 - Binary to execute (required)
+# $2 - GDB configuration file (optional)
+# $3 - Configuration file path (optional)
+# $4 - System log file (optional)
+# $5 - System error file (optional)
+# $6 - GDB enabled flag (0/1, optional)
+# $7 - Crashes directory path (optional)
+
+# Get script directory
+CURRENT_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+
+# Parameters (same as starter)
+BINPATH="$1"
+BINFILE="$2"
+GDB_FILE="$3"
+CONFIG="$4"
+SYSLOG="$5"
+SYSERR="$6"
+GDB_ENABLED="${7:-0}"
+CRASHES_PATH="$8"
+
+BINARY="$BINPATH/$BINFILE"
+
+# Default values (same as starter)
+DEFAULT_CRASHES_PATH="./crashes"
+DEFAULT_GDB_FILE="$CURRENT_PATH/gdb.conf"
+
+# Set defaults if not provided
+CRASHES_PATH="${CRASHES_PATH:-$DEFAULT_CRASHES_PATH}"
+GDB_FILE="${GDB_FILE:-$DEFAULT_GDB_FILE}"
+
+# Counters for crash detection
+_instant_crash_count=0
+_restart_count=0
+
+# Check if starter script exists
+STARTER_SCRIPT="$CURRENT_PATH/starter"
+if [ ! -f "$STARTER_SCRIPT" ]; then
+ echo "Error: starter script not found at $STARTER_SCRIPT"
+ exit 1
+fi
+
+# Main restart loop
+while true; do
+ STARTING_TIME=$(date +%s)
+
+ # Use starter script to launch the binary with all parameters
+ "$STARTER_SCRIPT" "$BINPATH" "$BINFILE" "$GDB_FILE" "$CONFIG" "$SYSLOG" "$SYSERR" "$GDB_ENABLED" "$CRASHES_PATH"
+
+ _exit_code=$?
+
+ echo "$(basename "$BINARY") terminated with exit code: $_exit_code"
+
+ # Calculate runtime
+ ENDING_TIME=$(date +%s)
+ DIFFERENCE=$((ENDING_TIME - STARTING_TIME))
+
+ ((_restart_count++))
+ echo "$(basename "$BINARY") terminated after $DIFFERENCE seconds, restart count: $_restart_count"
+
+ # Crash loop detection
+ if [ $DIFFERENCE -lt 10 ]; then
+ # Increment instant crash count if runtime is lower than 10 seconds
+ ((_instant_crash_count++))
+ echo "Warning: Quick restart detected ($DIFFERENCE seconds) - instant crash count: $_instant_crash_count"
+ else
+ # Reset count on successful longer run
+ _instant_crash_count=0
+ fi
+
+ # Prevent infinite crash loops
+ if [ $_instant_crash_count -gt 5 ]; then
+ echo "Error: $(basename "$BINARY") restarter exited. Infinite crash loop prevented (6 crashes in under 10 seconds each)"
+ echo "Please check your system configuration and logs"
+ exit 1
+ fi
+
+ echo "$(basename "$BINARY") will restart in 3 seconds..."
+ sleep 3
+done
diff --git a/apps/startup-scripts/src/starter b/apps/startup-scripts/src/starter
new file mode 100755
index 0000000000..967146efad
--- /dev/null
+++ b/apps/startup-scripts/src/starter
@@ -0,0 +1,117 @@
+#!/usr/bin/env bash
+
+# AzerothCore Starter Script
+# This script handles the execution of AzerothCore binaries with optional GDB support
+#
+# Usage: starter <binary> [gdb_file] [config] [syslog] [syserr] [gdb_enabled] [crashes_path]
+#
+# Parameters:
+# $1 - Binary to execute (required)
+# $2 - GDB configuration file (optional)
+# $3 - Configuration file path (optional)
+# $4 - System log file (optional)
+# $5 - System error file (optional)
+# $6 - GDB enabled flag (0/1, optional)
+# $7 - Crashes directory path (optional)
+
+BINPATH="$1"
+BINFILE="$2"
+GDB_FILE="$3"
+CONFIG="$4"
+SYSLOG="$5"
+SYSERR="$6"
+GDB_ENABLED="${7:-0}"
+CRASHES_PATH="$8"
+
+BINARY=$(realpath "$BINPATH/$BINFILE")
+
+# Default values
+CURRENT_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+DEFAULT_CRASHES_PATH="$CURRENT_PATH/logs/crashes"
+DEFAULT_GDB_FILE="$CURRENT_PATH/gdb.conf"
+
+# Set defaults if not provided
+CONFIG="${CONFIG:-""}"
+CRASHES_PATH="${CRASHES_PATH:-$DEFAULT_CRASHES_PATH}"
+GDB_FILE="${GDB_FILE:-$DEFAULT_GDB_FILE}"
+
+# Validate binary
+if [ -z "$BINARY" ]; then
+ echo "Error: Binary parameter is required"
+ echo "Usage: $0 <binary> [gdb_file] [config] [syslog] [syserr] [gdb_enabled] [crashes_path]"
+ exit 1
+fi
+
+if [ ! -f "$BINARY" ]; then
+ echo "Error: Binary '$BINARY' not found"
+ exit 1
+fi
+
+# Create crashes directory if it doesn't exist
+mkdir -p "$CRASHES_PATH"
+
+cd $BINPATH || {
+ echo "Error: Could not change to binary path '$BINPATH'"
+ exit 1
+}
+
+EXECPATH=$(realpath "$BINFILE")
+
+if [ "$GDB_ENABLED" -eq 1 ]; then
+ echo "Starting $EXECPATH with GDB enabled"
+
+ # Generate GDB configuration on the fly
+ TIMESTAMP=$(date +%Y-%m-%d-%H-%M-%S)
+ GDB_TEMP_FILE="$CRASHES_PATH/gdb-$TIMESTAMP.conf"
+ GDB_OUTPUT_FILE="$CRASHES_PATH/gdb-$TIMESTAMP.txt"
+
+ # Create GDB configuration
+ cat > "$GDB_TEMP_FILE" << EOF
+set logging file $GDB_OUTPUT_FILE
+set logging enabled on
+set debug timestamp
+EOF
+
+ # Add run command with config if specified
+ if [ -n "$CONFIG" ]; then
+ echo "run -c $CONFIG" >> "$GDB_TEMP_FILE"
+ else
+ echo "run" >> "$GDB_TEMP_FILE"
+ fi
+
+ cat >> "$GDB_TEMP_FILE" << EOF
+bt
+bt full
+info thread
+thread apply all backtrace full
+EOF
+
+ # Create log files if specified
+ if [ -n "$SYSLOG" ]; then
+ [ ! -f "$SYSLOG" ] && touch "$SYSLOG"
+ fi
+ if [ -n "$SYSERR" ]; then
+ [ ! -f "$SYSERR" ] && touch "$SYSERR"
+ fi
+
+ # Execute with GDB
+ if [ "${WITH_CONSOLE:-0}" -eq 0 ] && [ -n "$SYSLOG" ] && [ -n "$SYSERR" ]; then
+ gdb -x "$GDB_TEMP_FILE" --batch "$EXECPATH" >> "$SYSLOG" 2>> "$SYSERR"
+ else
+ echo "> Console enabled"
+ if [ -n "$SYSLOG" ] && [ -n "$SYSERR" ]; then
+ gdb -x "$GDB_TEMP_FILE" --batch "$EXECPATH" > >(tee "$SYSLOG") 2> >(tee "$SYSERR" >&2)
+ else
+ gdb -x "$GDB_TEMP_FILE" --batch "$EXECPATH"
+ fi
+ fi
+
+ # Cleanup temporary GDB file
+ rm -f "$GDB_TEMP_FILE"
+else
+ if [ -n "$CONFIG" ]; then
+ script -q -e -c "$EXECPATH -c \"$CONFIG\""
+ else
+ script -q -e -c "$EXECPATH"
+ fi
+fi