mirror of
https://github.com/liyunfan1223/azerothcore-wotlk.git
synced 2026-08-07 07:17:57 +00:00
322 lines
8.6 KiB
Bash
Executable File
322 lines
8.6 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
# AzerothCore Starter Script
|
|
# This script handles the execution of AzerothCore binaries with optional GDB support
|
|
#
|
|
# Usage: starter <binpath> <binfile> [gdb_file] [config] [syslog] [syserr] [gdb_enabled] [crashes_path]
|
|
#
|
|
# Parameters:
|
|
# $1 - Binary path (required)
|
|
# $2 - Binary file name (required)
|
|
# $3 - GDB configuration file (optional)
|
|
# $4 - Configuration file path (optional)
|
|
# $5 - System log file (optional)
|
|
# $6 - System error file (optional)
|
|
# $7 - GDB enabled flag (0/1, optional)
|
|
# $8 - Crashes directory path (optional)
|
|
|
|
BINPATH="$1"
|
|
BINFILE="$2"
|
|
GDB_FILE="$3"
|
|
CONFIG="$4"
|
|
SYSLOG="$5"
|
|
SYSERR="$6"
|
|
GDB_ENABLED="${7:-0}"
|
|
CRASHES_PATH="$8"
|
|
|
|
# Default values
|
|
CURRENT_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
DEFAULT_CRASHES_PATH=$(realpath "$BINPATH/crashes")
|
|
[ -n "$CONFIG" ] && CONFIG_ABS=$(realpath "$CONFIG")
|
|
|
|
# Set defaults if not provided
|
|
CRASHES_PATH="${CRASHES_PATH:-$DEFAULT_CRASHES_PATH}"
|
|
|
|
# Validate binary
|
|
if [ -z "$BINPATH" ] || [ -z "$BINFILE" ]; then
|
|
echo "Error: Binary path and file are required"
|
|
echo "Usage: $0 <binpath> <binfile> [gdb_file] [config] [syslog] [syserr] [gdb_enabled] [crashes_path]"
|
|
exit 1
|
|
fi
|
|
|
|
BINARY="$BINPATH/$BINFILE"
|
|
if [ ! -f "$BINARY" ]; then
|
|
echo "Error: Binary '$BINARY' not found"
|
|
exit 1
|
|
fi
|
|
|
|
function config_value() {
|
|
local key="$1"
|
|
|
|
if [ -z "${CONFIG_ABS:-}" ] || [ ! -f "$CONFIG_ABS" ]; then
|
|
return 1
|
|
fi
|
|
|
|
awk -F= -v key="$key" '
|
|
{
|
|
configKey = $1
|
|
gsub(/^[[:space:]]+|[[:space:]]+$/, "", configKey)
|
|
}
|
|
configKey == key {
|
|
value = $0
|
|
sub(/^[^=]*=/, "", value)
|
|
sub(/[[:space:]]*[#;].*$/, "", value)
|
|
gsub(/"/, "", value)
|
|
gsub(/^[[:space:]]+|[[:space:]]+$/, "", value)
|
|
print value
|
|
exit
|
|
}
|
|
' "$CONFIG_ABS"
|
|
}
|
|
|
|
function command_contains_config() {
|
|
local command_line="$1"
|
|
|
|
if [ -z "${CONFIG_ABS:-}" ]; then
|
|
return 0
|
|
fi
|
|
|
|
case "$command_line" in
|
|
*" -c $CONFIG_ABS"*|*" -c \"$CONFIG_ABS\""*)
|
|
return 0
|
|
;;
|
|
esac
|
|
|
|
return 1
|
|
}
|
|
|
|
function find_existing_instances() {
|
|
local line pid command_line
|
|
|
|
while IFS= read -r line; do
|
|
pid="${line%% *}"
|
|
command_line="${line#* }"
|
|
|
|
[ -z "$pid" ] && continue
|
|
[ "$pid" = "$$" ] && continue
|
|
[ "$pid" = "$PPID" ] && continue
|
|
|
|
case "$command_line" in
|
|
*"/starter "*|*"pgrep "*)
|
|
continue
|
|
;;
|
|
esac
|
|
|
|
if command_contains_config "$command_line"; then
|
|
printf '%s\n' "$line"
|
|
fi
|
|
done < <(pgrep -fa "$EXECPATH" 2>/dev/null || true)
|
|
}
|
|
|
|
function listening_on_port() {
|
|
local port="$1"
|
|
|
|
command -v ss >/dev/null 2>&1 || return 1
|
|
ss -H -ltn "sport = :$port" 2>/dev/null | grep -q .
|
|
}
|
|
|
|
function guarded_ports() {
|
|
local port soap_enabled soap_port
|
|
|
|
if [ "$BINFILE" != "worldserver" ]; then
|
|
return 0
|
|
fi
|
|
|
|
port="$(config_value "WorldServerPort" || true)"
|
|
if [ -n "$port" ]; then
|
|
printf '%s\n' "$port"
|
|
fi
|
|
|
|
soap_enabled="$(config_value "SOAP.Enabled" || true)"
|
|
soap_port="$(config_value "SOAP.Port" || true)"
|
|
if [ "$soap_enabled" = "1" ] && [ -n "$soap_port" ]; then
|
|
printf '%s\n' "$soap_port"
|
|
fi
|
|
}
|
|
|
|
function find_listening_guard_ports() {
|
|
local port
|
|
|
|
while IFS= read -r port; do
|
|
[ -z "$port" ] && continue
|
|
if listening_on_port "$port"; then
|
|
printf '%s\n' "$port"
|
|
fi
|
|
done < <(guarded_ports)
|
|
}
|
|
|
|
function wait_for_startup_guard() {
|
|
local timeout="${AC_STARTUP_GUARD_TIMEOUT:-90}"
|
|
local interval="${AC_STARTUP_GUARD_INTERVAL:-2}"
|
|
local elapsed=0
|
|
local instances ports
|
|
local sleep_for
|
|
local warned=0
|
|
|
|
if [ "${AC_STARTUP_SKIP_GUARD:-0}" = "1" ]; then
|
|
return 0
|
|
fi
|
|
|
|
case "$timeout" in
|
|
''|*[!0-9]*)
|
|
echo "Startup guard: invalid AC_STARTUP_GUARD_TIMEOUT '$timeout', using 90."
|
|
timeout=90
|
|
;;
|
|
esac
|
|
|
|
case "$interval" in
|
|
''|*[!0-9]*)
|
|
echo "Startup guard: invalid AC_STARTUP_GUARD_INTERVAL '$interval', using 2."
|
|
interval=2
|
|
;;
|
|
esac
|
|
|
|
if [ "$interval" -lt 1 ]; then
|
|
echo "Startup guard: AC_STARTUP_GUARD_INTERVAL must be at least 1, using 1."
|
|
interval=1
|
|
fi
|
|
|
|
while true; do
|
|
instances="$(find_existing_instances)"
|
|
ports="$(find_listening_guard_ports)"
|
|
|
|
if [ -z "$instances" ] && [ -z "$ports" ]; then
|
|
return 0
|
|
fi
|
|
|
|
if [ "$elapsed" -ge "$timeout" ]; then
|
|
echo "Startup guard: refusing to start duplicate $BINFILE after waiting ${timeout}s."
|
|
if [ -n "$instances" ]; then
|
|
echo "Startup guard: existing matching process(es):"
|
|
echo "$instances"
|
|
fi
|
|
if [ -n "$ports" ]; then
|
|
echo "Startup guard: configured port(s) still listening: $(echo "$ports" | paste -sd ',' -)"
|
|
fi
|
|
exit 0
|
|
fi
|
|
|
|
if [ "$warned" -eq 0 ]; then
|
|
echo "Startup guard: waiting for existing $BINFILE instance or configured port(s) to clear..."
|
|
warned=1
|
|
fi
|
|
|
|
sleep_for="$interval"
|
|
if [ $((elapsed + sleep_for)) -gt "$timeout" ]; then
|
|
sleep_for=$((timeout - elapsed))
|
|
fi
|
|
|
|
[ "$sleep_for" -le 0 ] && continue
|
|
|
|
sleep "$sleep_for"
|
|
elapsed=$((elapsed + sleep_for))
|
|
done
|
|
}
|
|
|
|
# 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")
|
|
|
|
wait_for_startup_guard
|
|
|
|
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 file if it is not defined
|
|
if [ -z "$GDB_FILE" ]; then
|
|
|
|
# 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_ABS" ]; then
|
|
echo "run -c $CONFIG_ABS" >> "$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
|
|
|
|
|
|
GDB_FILE="$GDB_TEMP_FILE"
|
|
fi
|
|
|
|
|
|
|
|
# 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_FILE" --batch "$EXECPATH" >> "$SYSLOG" 2>> "$SYSERR"
|
|
else
|
|
echo "> Console enabled"
|
|
if [ -n "$SYSLOG" ] && [ -n "$SYSERR" ]; then
|
|
gdb -x "$GDB_FILE" --batch "$EXECPATH" > >(tee "$SYSLOG") 2> >(tee "$SYSERR" >&2)
|
|
else
|
|
gdb -x "$GDB_FILE" --batch "$EXECPATH"
|
|
fi
|
|
fi
|
|
|
|
|
|
# clean up temporary GDB file if it exists
|
|
if [ -n "$GDB_TEMP_FILE" ]; then
|
|
# Clean up temporary GDB file
|
|
rm -f "$GDB_TEMP_FILE"
|
|
fi
|
|
else
|
|
echo "Starting $BINFILE without GDB"
|
|
# Determine if PM2 is active
|
|
is_pm2_active="0"
|
|
[ "$AC_LAUNCHED_BY_PM2" == "1" ] && is_pm2_active="1"
|
|
|
|
# Determine if interactive mode is enabled
|
|
is_interactive_enabled="1"
|
|
[ "$AC_DISABLE_INTERACTIVE" == "1" ] && is_interactive_enabled="0"
|
|
|
|
# use normal execution if we are running the binary under PM2
|
|
# or when interactive mode is enabled
|
|
if [[ "$is_pm2_active" == "1" || "$is_interactive_enabled" == "1" ]]; then
|
|
echo "Running AC"
|
|
"$EXECPATH" ${CONFIG_ABS:+-c "$CONFIG_ABS"}
|
|
else
|
|
# When AC_DISABLE_INTERACTIVE is set to 1 and we are not in PM2
|
|
# This means we are using systemd without interactive mode and no session managers
|
|
# in this case we need to run AC with unbuffer for line-buffered output
|
|
# NOTE unbuffer doesn't fully support interactive mode
|
|
if command -v unbuffer >/dev/null 2>&1; then
|
|
echo "Running AC with unbuffer for line-buffered output"
|
|
unbuffer "$EXECPATH" ${CONFIG_ABS:+-c "$CONFIG_ABS"}
|
|
else
|
|
echo "⚠️ unbuffer not found, the output may not be line-buffered. Try installing expect."
|
|
exec "$EXECPATH" ${CONFIG_ABS:+-c "$CONFIG_ABS"}
|
|
fi
|
|
fi
|
|
fi
|