diff options
Diffstat (limited to 'apps/startup-scripts/src')
-rw-r--r-- | apps/startup-scripts/src/.gitignore | 1 | ||||
-rw-r--r-- | apps/startup-scripts/src/conf.sh.dist | 57 | ||||
-rwxr-xr-x | apps/startup-scripts/src/examples/restarter-auth.sh | 48 | ||||
-rwxr-xr-x | apps/startup-scripts/src/examples/restarter-world.sh | 47 | ||||
-rwxr-xr-x | apps/startup-scripts/src/examples/starter-auth.sh | 46 | ||||
-rwxr-xr-x | apps/startup-scripts/src/examples/starter-world.sh | 47 | ||||
-rw-r--r-- | apps/startup-scripts/src/gdb.conf | 7 | ||||
-rwxr-xr-x | apps/startup-scripts/src/run-engine | 467 | ||||
-rwxr-xr-x | apps/startup-scripts/src/service-manager.sh | 1261 | ||||
-rwxr-xr-x | apps/startup-scripts/src/simple-restarter | 89 | ||||
-rwxr-xr-x | apps/startup-scripts/src/starter | 117 |
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 |