summaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
Diffstat (limited to 'apps')
-rw-r--r--apps/EnumUtils/enumutils_describe.py10
-rw-r--r--apps/bash_shared/common.sh20
-rw-r--r--apps/bash_shared/defines.sh2
-rw-r--r--apps/bash_shared/includes.sh2
-rwxr-xr-xapps/ci/ci-install-modules.sh2
-rw-r--r--apps/compiler/includes/functions.sh36
-rw-r--r--apps/installer/includes/config/config-main.sh9
-rw-r--r--apps/installer/includes/config/config.sh60
-rw-r--r--apps/installer/includes/functions.sh4
-rw-r--r--apps/installer/includes/includes.sh9
-rw-r--r--apps/installer/includes/modules-manager/README.md6
-rw-r--r--apps/installer/includes/modules-manager/modules.sh151
-rw-r--r--apps/installer/includes/os_configs/debian.sh4
-rw-r--r--apps/installer/includes/os_configs/ubuntu.sh6
-rw-r--r--apps/installer/includes/os_configs/windows.sh5
-rw-r--r--apps/installer/main.sh4
-rwxr-xr-xapps/installer/test/test_module_commands.bats2
-rw-r--r--apps/startup-scripts/README.md34
-rwxr-xr-xapps/startup-scripts/src/run-engine7
-rwxr-xr-xapps/startup-scripts/src/service-manager.sh825
-rwxr-xr-xapps/startup-scripts/src/simple-restarter2
-rwxr-xr-x[-rw-r--r--]apps/startup-scripts/test/test_startup_scripts.bats183
22 files changed, 1218 insertions, 165 deletions
diff --git a/apps/EnumUtils/enumutils_describe.py b/apps/EnumUtils/enumutils_describe.py
index ef9bf72a4f..b1adee2aba 100644
--- a/apps/EnumUtils/enumutils_describe.py
+++ b/apps/EnumUtils/enumutils_describe.py
@@ -4,14 +4,14 @@ from os import walk, getcwd
notice = ('''/*
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Affero General Public License as published by the
- * Free Software Foundation; either version 3 of the License, or (at your
- * option) any later version.
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
diff --git a/apps/bash_shared/common.sh b/apps/bash_shared/common.sh
index acd23eacdf..46422119b1 100644
--- a/apps/bash_shared/common.sh
+++ b/apps/bash_shared/common.sh
@@ -1,17 +1,19 @@
function registerHooks() { acore_event_registerHooks "$@"; }
function runHooks() { acore_event_runHooks "$@"; }
-#shellcheck source=../../conf/dist/config.sh
-source "$AC_PATH_CONF/dist/config.sh" # include dist to avoid missing conf variables
+function acore_common_loadConfig() {
+ #shellcheck source=../../conf/dist/config.sh
+ source "$AC_PATH_CONF/dist/config.sh" # include dist to avoid missing conf variables
-# first check if it's defined in env, otherwise use the default
-USER_CONF_PATH=${USER_CONF_PATH:-"$AC_PATH_CONF/config.sh"}
+ # first check if it's defined in env, otherwise use the default
+ USER_CONF_PATH=${USER_CONF_PATH:-"$AC_PATH_CONF/config.sh"}
-if [ -f "$USER_CONF_PATH" ]; then
- source "$USER_CONF_PATH" # should overwrite previous
-else
- echo "NOTICE: file <$USER_CONF_PATH> not found, we use default configuration only."
-fi
+ if [ -f "$USER_CONF_PATH" ]; then
+ source "$USER_CONF_PATH" # should overwrite previous
+ else
+ echo "NOTICE: file <$USER_CONF_PATH> not found, we use default configuration only."
+ fi
+}
#
# Load modules
diff --git a/apps/bash_shared/defines.sh b/apps/bash_shared/defines.sh
index 4b014bd9c1..af9e9dfc97 100644
--- a/apps/bash_shared/defines.sh
+++ b/apps/bash_shared/defines.sh
@@ -25,4 +25,6 @@ export AC_PATH_MODULES="$AC_PATH_ROOT/modules"
export AC_PATH_DEPS="$AC_PATH_ROOT/deps"
+export AC_BASH_LIB_PATH="$AC_PATH_DEPS/acore/bash-lib/src"
+
export AC_PATH_VAR="$AC_PATH_ROOT/var"
diff --git a/apps/bash_shared/includes.sh b/apps/bash_shared/includes.sh
index d2bf07db12..679fc8e6d3 100644
--- a/apps/bash_shared/includes.sh
+++ b/apps/bash_shared/includes.sh
@@ -16,6 +16,8 @@ source "$AC_PATH_DEPS/acore/bash-lib/src/event/hooks.sh"
# shellcheck source=./common.sh
source "$AC_PATH_SHARED/common.sh"
+acore_common_loadConfig
+
if [[ "$OSTYPE" = "msys" ]]; then
AC_BINPATH_FULL="$BINPATH"
else
diff --git a/apps/ci/ci-install-modules.sh b/apps/ci/ci-install-modules.sh
index 5987eaf7ad..7ff6470750 100755
--- a/apps/ci/ci-install-modules.sh
+++ b/apps/ci/ci-install-modules.sh
@@ -35,7 +35,7 @@ git clone --depth=1 --branch=master https://github.com/azerothcore/mod-detailed-
git clone --depth=1 --branch=main https://github.com/azerothcore/mod-dmf-switch modules/mod-dmf-switch
git clone --depth=1 --branch=master https://github.com/azerothcore/mod-duel-reset modules/mod-duel-reset
git clone --depth=1 --branch=master https://github.com/azerothcore/mod-dynamic-xp modules/mod-dynamic-xp
-git clone --depth=1 --branch=master https://github.com/azerothcore/mod-eluna modules/mod-eluna
+git clone --depth=1 --branch=master https://github.com/azerothcore/mod-ale modules/mod-ale
git clone --depth=1 --branch=master https://github.com/azerothcore/mod-emblem-transfer modules/mod-emblem-transfer
git clone --depth=1 --branch=master https://github.com/azerothcore/mod-fireworks-on-level modules/mod-fireworks-on-level
git clone --depth=1 --branch=main https://github.com/azerothcore/mod-global-chat modules/mod-global-chat
diff --git a/apps/compiler/includes/functions.sh b/apps/compiler/includes/functions.sh
index c608cff24d..4428f9132f 100644
--- a/apps/compiler/includes/functions.sh
+++ b/apps/compiler/includes/functions.sh
@@ -1,5 +1,8 @@
#!/usr/bin/env bash
+# shellcheck source=../../../deps/acore/bash-lib/src/common/boolean.sh
+source "$AC_BASH_LIB_PATH/common/boolean.sh"
+
# Set SUDO variable - one liner
SUDO=""
@@ -135,7 +138,8 @@ function comp_compile() {
echo "Done"
;;
linux*|darwin*)
- local confDir=${CONFDIR:-"$AC_BINPATH_FULL/../etc"}
+ local confDir
+ confDir=${CONFDIR:-"$AC_BINPATH_FULL/../etc"}
# create the folders before installing to
# set the current user and permissions
@@ -143,6 +147,9 @@ function comp_compile() {
mkdir -p "$AC_BINPATH_FULL"
echo "Creating $confDir..."
mkdir -p "$confDir"
+ mkdir -p "$confDir/modules"
+
+ confDir=$(realpath "$confDir")
echo "Cmake install..."
$SUDO cmake --install . --config $CTYPE
@@ -156,14 +163,29 @@ function comp_compile() {
echo "Setting permissions on binary files"
find "$AC_BINPATH_FULL" -mindepth 1 -maxdepth 1 -type f -exec $SUDO chown root:root -- {} +
find "$AC_BINPATH_FULL" -mindepth 1 -maxdepth 1 -type f -exec $SUDO chmod u+s -- {} +
+ $SUDO setcap cap_sys_nice=eip "$AC_BINPATH_FULL/worldserver"
+ $SUDO setcap cap_sys_nice=eip "$AC_BINPATH_FULL/authserver"
fi
- [[ -f "$confDir/worldserver.conf.dist" ]] && \
- cp -v --no-clobber "$confDir/worldserver.conf.dist" "$confDir/worldserver.conf"
- [[ -f "$confDir/authserver.conf.dist" ]] && \
- cp -v --no-clobber "$confDir/authserver.conf.dist" "$confDir/authserver.conf"
- [[ -f "$confDir/dbimport.conf.dist" ]] && \
- cp -v --no-clobber "$confDir/dbimport.conf.dist" "$confDir/dbimport.conf"
+
+ if ( isTrue "$AC_ENABLE_CONF_COPY_ON_INSTALL" ) then
+ echo "Copying default configuration files to $confDir ..."
+ [[ -f "$confDir/worldserver.conf.dist" && ! -f "$confDir/worldserver.conf" ]] && \
+ cp -v "$confDir/worldserver.conf.dist" "$confDir/worldserver.conf"
+ [[ -f "$confDir/authserver.conf.dist" && ! -f "$confDir/authserver.conf" ]] && \
+ cp -v "$confDir/authserver.conf.dist" "$confDir/authserver.conf"
+ [[ -f "$confDir/dbimport.conf.dist" && ! -f "$confDir/dbimport.conf" ]] && \
+ cp -v "$confDir/dbimport.conf.dist" "$confDir/dbimport.conf"
+
+ for f in "$confDir/modules/"*.dist
+ do
+ [[ -e $f ]] || break # handle the case of no *.dist files
+ if [[ ! -f "${f%.dist}" ]]; then
+ echo "Copying module config $(basename "${f%.dist}")"
+ cp -v "$f" "${f%.dist}";
+ fi
+ done
+ fi
echo "Done"
;;
diff --git a/apps/installer/includes/config/config-main.sh b/apps/installer/includes/config/config-main.sh
new file mode 100644
index 0000000000..f5f0c01f64
--- /dev/null
+++ b/apps/installer/includes/config/config-main.sh
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+
+CURRENT_PATH=$( cd "$(dirname "${BASH_SOURCE[0]}")" || exit ; pwd )
+
+# shellcheck source=./config.sh
+source "$CURRENT_PATH/config.sh"
+
+acore_dash_config "$@"
+
diff --git a/apps/installer/includes/config/config.sh b/apps/installer/includes/config/config.sh
new file mode 100644
index 0000000000..40192c4008
--- /dev/null
+++ b/apps/installer/includes/config/config.sh
@@ -0,0 +1,60 @@
+#!/usr/bin/env bash
+
+CURRENT_PATH=$( cd "$(dirname "${BASH_SOURCE[0]}")" || exit ; pwd )
+
+# shellcheck source=../../../bash_shared/includes.sh
+source "$CURRENT_PATH/../../../bash_shared/includes.sh"
+# shellcheck source=../includes.sh
+source "$CURRENT_PATH/../includes.sh"
+# shellcheck source=../../../bash_shared/menu_system.sh
+source "$AC_PATH_APPS/bash_shared/menu_system.sh"
+
+function acore_dash_configShowValue() {
+ if [ $# -ne 1 ]; then
+ echo "Usage: show <VAR_NAME>"
+ return 1
+ fi
+
+ local varName="$1"
+ local varValue="${!varName}"
+ if [ -z "$varValue" ]; then
+ echo "$varName is not set."
+ else
+ echo "$varName=$varValue"
+ fi
+}
+
+function acore_dash_configLoad() {
+ acore_common_loadConfig
+ echo "Configuration loaded into the current shell session."
+}
+
+# Configuration management menu definition
+# Format: "key|short|description"
+config_menu_items=(
+ "show|s|Show configuration variable value"
+ "load|l|Load configurations variables within the current shell session"
+ "help|h|Show detailed help"
+ "quit|q|Close this menu"
+)
+
+# Menu command handler for configuration operations
+function handle_config_command() {
+ local key="$1"
+ shift
+
+ case "$key" in
+ "show")
+ acore_dash_configShowValue "$@"
+ ;;
+ "load")
+ acore_dash_configLoad
+ ;;
+ esac
+}
+
+function acore_dash_config() {
+ menu_run_with_items "CONFIG MANAGER" handle_config_command -- "${config_menu_items[@]}" -- "$@"
+ return $?
+}
+
diff --git a/apps/installer/includes/functions.sh b/apps/installer/includes/functions.sh
index 9dbe2652c1..3c9b2edba2 100644
--- a/apps/installer/includes/functions.sh
+++ b/apps/installer/includes/functions.sh
@@ -155,7 +155,7 @@ function inst_simple_restarter {
function inst_download_client_data {
# change the following version when needed
- local VERSION=v16
+ local VERSION=v18.0
echo "#######################"
echo "Client data downloader"
@@ -183,3 +183,5 @@ function inst_download_client_data {
&& echo "Remove downloaded file" && rm "$zipPath" \
&& echo "INSTALLED_VERSION=$VERSION" > "$dataVersionFile"
}
+
+
diff --git a/apps/installer/includes/includes.sh b/apps/installer/includes/includes.sh
index c0d6bb8bd6..e4c1b9f2b5 100644
--- a/apps/installer/includes/includes.sh
+++ b/apps/installer/includes/includes.sh
@@ -2,6 +2,7 @@
CURRENT_PATH=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd )
+# shellcheck source=../../bash_shared/includes.sh
source "$CURRENT_PATH/../../bash_shared/includes.sh"
AC_PATH_INSTALLER="$AC_PATH_APPS/installer"
@@ -9,14 +10,14 @@ AC_PATH_INSTALLER="$AC_PATH_APPS/installer"
J_PATH="$AC_PATH_DEPS/acore/joiner"
J_PATH_MODULES="$AC_PATH_MODULES"
+# shellcheck source=../../../deps/acore/joiner/joiner.sh
source "$J_PATH/joiner.sh"
-if [ -f "$AC_PATH_INSTALLER/config.sh" ]; then
- source "$AC_PATH_INSTALLER/config.sh" # should overwrite previous
-fi
-
+# shellcheck source=../../compiler/includes/includes.sh
source "$AC_PATH_APPS/compiler/includes/includes.sh"
+# shellcheck source=../../../deps/semver_bash/semver.sh
source "$AC_PATH_DEPS/semver_bash/semver.sh"
+# shellcheck source=../includes/functions.sh
source "$AC_PATH_INSTALLER/includes/functions.sh"
diff --git a/apps/installer/includes/modules-manager/README.md b/apps/installer/includes/modules-manager/README.md
index 93496a91a6..d6a160b6c7 100644
--- a/apps/installer/includes/modules-manager/README.md
+++ b/apps/installer/includes/modules-manager/README.md
@@ -63,7 +63,7 @@ repo[:dirname][@branch[:commit]]
./acore.sh module install https://github.com/azerothcore/mod-transmog.git@main
# Install multiple modules
-./acore.sh module install mod-transmog mod-eluna:custom-eluna
+./acore.sh module install mod-transmog mod-ale:custom-eluna
# Install all modules from list
./acore.sh module install --all
@@ -92,7 +92,7 @@ repo[:dirname][@branch[:commit]]
./acore.sh module remove https://github.com/azerothcore/mod-transmog.git
# Remove multiple modules
-./acore.sh module remove mod-transmog mod-eluna
+./acore.sh module remove mod-transmog mod-ale
```
### Searching Modules
@@ -232,7 +232,7 @@ repo_reference branch commit
# Examples:
azerothcore/mod-transmog master abc123def456
https://github.com/custom/mod-custom.git develop def456abc789
-mod-eluna:custom-eluna-dir main 789abc123def
+mod-ale:custom-eluna-dir main 789abc123def
```
The list maintains:
diff --git a/apps/installer/includes/modules-manager/modules.sh b/apps/installer/includes/modules-manager/modules.sh
index 787d07677c..89c7ea50ac 100644
--- a/apps/installer/includes/modules-manager/modules.sh
+++ b/apps/installer/includes/modules-manager/modules.sh
@@ -59,7 +59,6 @@ else
C_GREEN=''
C_YELLOW=''
C_BLUE=''
- C_MAGENTA=''
C_CYAN=''
fi
@@ -127,10 +126,13 @@ function inst_module_help() {
echo " ./acore.sh module # Interactive menu"
echo " ./acore.sh module search [terms...]"
echo " ./acore.sh module install [--all | modules...]"
- echo " ./acore.sh module update [--all | modules...]"
+ echo " ./acore.sh module update [--discard-changes] [--all | modules...]"
echo " ./acore.sh module remove [modules...]"
echo " ./acore.sh module list # List installed modules"
echo ""
+ echo "Options:"
+ echo " --discard-changes Reset module repositories to a clean state before updating"
+ echo ""
echo "Module Specification Syntax:"
echo " name # Simple name (e.g., mod-transmog)"
echo " owner/name # GitHub repository"
@@ -171,42 +173,8 @@ function inst_module_list() {
# Usage: ./acore.sh module <search|install|update|remove> [args...]
# ./acore.sh module # Interactive menu
function inst_module() {
- # If no arguments provided, start interactive menu
- if [[ $# -eq 0 ]]; then
- menu_run_with_items "MODULE MANAGER" handle_module_command -- "${module_menu_items[@]}" --
- return $?
- fi
-
- # Normalize arguments into an array
- local tokens=()
- read -r -a tokens <<< "$*"
- local cmd="${tokens[0]}"
- local args=("${tokens[@]:1}")
-
- case "$cmd" in
- ""|"help"|"-h"|"--help")
- inst_module_help
- ;;
- "search"|"s")
- inst_module_search "${args[@]}"
- ;;
- "install"|"i")
- inst_module_install "${args[@]}"
- ;;
- "update"|"u")
- inst_module_update "${args[@]}"
- ;;
- "remove"|"r")
- inst_module_remove "${args[@]}"
- ;;
- "list"|"l")
- inst_module_list "${args[@]}"
- ;;
- *)
- print_error "Unknown module command: $cmd. Use 'help' to see available commands."
- return 1
- ;;
- esac
+ menu_run_with_items "MODULE MANAGER" handle_module_command -- "${module_menu_items[@]}" -- "$@"
+ return $?
}
# =============================================================================
@@ -602,6 +570,37 @@ function inst_mod_is_installed() {
return 1
}
+# Discard local changes from a module repository to guarantee a clean update.
+function inst_module_reset_repo() {
+ local repo_ref="$1"
+ local dirname="$2"
+ local repo_path="$J_PATH_MODULES/$dirname"
+
+ if [ ! -d "$repo_path" ]; then
+ print_error "[$repo_ref] Cannot discard changes; path not found ($repo_path)."
+ return 1
+ fi
+
+ if [ ! -d "$repo_path/.git" ]; then
+ print_error "[$repo_ref] Cannot discard changes; $repo_path is not a git repository."
+ return 1
+ fi
+
+ print_warn "[$repo_ref] Discarding local changes (--discard-changes)."
+
+ if ! git -C "$repo_path" reset --hard >/dev/null 2>&1; then
+ print_error "[$repo_ref] Failed to reset repository at $repo_path."
+ return 1
+ fi
+
+ if ! git -C "$repo_path" clean -fd >/dev/null 2>&1; then
+ print_error "[$repo_ref] Failed to remove untracked files from $repo_path."
+ return 1
+ fi
+
+ return 0
+}
+
# =============================================================================
# Conflict Detection and Validation
# =============================================================================
@@ -649,7 +648,7 @@ function inst_getVersionBranch() {
res="none"
# since we've the pair version,branch alternated in not associative and one-dimensional
# array, we've to simulate the association with length/2 trick
- for idx in `seq 0 $((${#vers[*]}/2-1))`; do
+ for idx in $(seq 0 $((${#vers[*]}/2-1))); do
semverParseInto "${vers[idx*2]}" MODULE_MAJOR MODULE_MINOR MODULE_PATCH MODULE_SPECIAL
if [[ $MODULE_MAJOR -eq $ACV_MAJOR && $MODULE_MINOR -le $ACV_MINOR ]]; then
res="${vers[idx*2+1]}"
@@ -901,31 +900,48 @@ function inst_module_install {
# Update one or more modules
function inst_module_update {
- # Handle help request
- if [[ "$1" == "--help" || "$1" == "-h" ]]; then
- inst_module_help
- return 0
- fi
-
- # Support multiple modules and the --all flag; prompt if none specified.
- local args=("$@")
+ local modules=()
local use_all=false
- if [ ${#args[@]} -gt 0 ] && { [ "${args[0]}" = "--all" ] || [ "${args[0]}" = "-a" ]; }; then
- use_all=true
+ local discard_changes=false
+ local had_errors=0
+
+ while [[ $# -gt 0 ]]; do
+ case "$1" in
+ --help|-h)
+ inst_module_help
+ return 0
+ ;;
+ --all|-a)
+ use_all=true
+ ;;
+ --discard-changes|--reset|-r)
+ discard_changes=true
+ ;;
+ --)
+ shift || true
+ while [[ $# -gt 0 ]]; do
+ modules+=("$1")
+ shift || true
+ done
+ break
+ ;;
+ *)
+ modules+=("$1")
+ ;;
+ esac
shift || true
- fi
-
- local _tmp=$PWD
+ done
if $use_all; then
- local line repo_ref branch commit newCommit owner modname url dirname
+ local repo_ref branch commit owner modname url dirname newCommit
+ local parsed_output
while read -r repo_ref branch commit; do
[ -z "$repo_ref" ] && continue
- # Skip excluded modules during update --all
if inst_mod_is_excluded "$repo_ref"; then
print_warn "[$repo_ref] Excluded by MODULES_EXCLUDE_LIST (skipping)."
continue
fi
+
parsed_output=$(inst_parse_module_spec "$repo_ref")
IFS=' ' read -r _ owner modname _ _ url dirname <<< "$parsed_output"
@@ -935,16 +951,23 @@ function inst_module_update {
continue
fi
+ if $discard_changes; then
+ if ! inst_module_reset_repo "$repo_ref" "$dirname"; then
+ had_errors=1
+ continue
+ fi
+ fi
+
if Joiner:upd_repo "$url" "$dirname" "$branch" ""; then
newCommit=$(git -C "$J_PATH_MODULES/$dirname" rev-parse HEAD 2>/dev/null || echo "")
inst_mod_list_upsert "$repo_ref" "$branch" "$newCommit"
print_success "[$repo_ref] Updated to latest commit on '$branch'."
else
print_error "[$repo_ref] Cannot update"
+ had_errors=1
fi
done < <(inst_mod_list_read)
else
- local modules=("$@")
if [ ${#modules[@]} -eq 0 ]; then
echo "Type the name(s) of the module(s) to update"
read -p "Insert name(s): " _line
@@ -952,6 +975,7 @@ function inst_module_update {
fi
local spec repo_ref override_branch override_commit owner modname url dirname v b branch def newCommit
+ local parsed_output
for spec in "${modules[@]}"; do
[ -z "$spec" ] && continue
parsed_output=$(inst_parse_module_spec "$spec")
@@ -959,11 +983,10 @@ function inst_module_update {
dirname="${dirname:-$modname}"
if [ -d "$J_PATH_MODULES/$dirname/" ]; then
- # determine preferred branch if not provided
+ b=""
if [ -n "$override_branch" ] && [ "$override_branch" != "-" ]; then
b="$override_branch"
else
- # try reading acore-module.json for this repo
if [[ "$url" =~ github.com ]]; then
read v b < <(inst_getVersionBranch "https://raw.githubusercontent.com/${owner}/${modname}/master/acore-module.json")
else
@@ -981,21 +1004,35 @@ function inst_module_update {
fi
fi
+ if $discard_changes; then
+ if ! inst_module_reset_repo "$repo_ref" "$dirname"; then
+ had_errors=1
+ continue
+ fi
+ fi
+
if Joiner:upd_repo "$url" "$dirname" "$b" ""; then
newCommit=$(git -C "$J_PATH_MODULES/$dirname" rev-parse HEAD 2>/dev/null || echo "")
inst_mod_list_upsert "$repo_ref" "$b" "$newCommit"
print_success "[$repo_ref] Done, please re-run compiling and db assembly"
else
print_error "[$repo_ref] Cannot update"
+ had_errors=1
fi
else
print_error "[$repo_ref] Cannot update! Path doesn't exist ($J_PATH_MODULES/$dirname/)"
+ had_errors=1
fi
done
fi
echo ""
echo ""
+
+ if [ "$had_errors" -ne 0 ]; then
+ return 1
+ fi
+ return 0
}
# Remove one or more modules
diff --git a/apps/installer/includes/os_configs/debian.sh b/apps/installer/includes/os_configs/debian.sh
index 5bfc93f8f8..1aecbe3d26 100644
--- a/apps/installer/includes/os_configs/debian.sh
+++ b/apps/installer/includes/os_configs/debian.sh
@@ -32,7 +32,7 @@ $SUDO apt-get install -y gdbserver gdb unzip curl \
VAR_PATH="$CURRENT_PATH/../../../../var"
# run noninteractive install for MYSQL 8.4 LTS
-wget https://dev.mysql.com/get/mysql-apt-config_0.8.32-1_all.deb -P "$VAR_PATH"
-DEBIAN_FRONTEND="noninteractive" $SUDO dpkg -i "$VAR_PATH/mysql-apt-config_0.8.32-1_all.deb"
+wget https://dev.mysql.com/get/mysql-apt-config_0.8.35-1_all.deb -P "$VAR_PATH"
+DEBIAN_FRONTEND="noninteractive" $SUDO dpkg -i "$VAR_PATH/mysql-apt-config_0.8.35-1_all.deb"
$SUDO apt-get update
DEBIAN_FRONTEND="noninteractive" $SUDO apt-get install -y mysql-server libmysqlclient-dev
diff --git a/apps/installer/includes/os_configs/ubuntu.sh b/apps/installer/includes/os_configs/ubuntu.sh
index cd3944fa6d..c2c84fff35 100644
--- a/apps/installer/includes/os_configs/ubuntu.sh
+++ b/apps/installer/includes/os_configs/ubuntu.sh
@@ -40,8 +40,10 @@ apt-get -y install ccache clang cmake curl google-perftools libmysqlclient-dev m
# Do not install MySQL if we are in docker (It will be used a docker container instead) or we are explicitly skipping it.
if [[ $DOCKER != 1 && $SKIP_MYSQL_INSTALL != 1 ]]; then
# run noninteractive install for MYSQL 8.4 LTS
- wget https://dev.mysql.com/get/mysql-apt-config_0.8.32-1_all.deb -P "$VAR_PATH"
- DEBIAN_FRONTEND="noninteractive" $SUDO dpkg -i "$VAR_PATH/mysql-apt-config_0.8.32-1_all.deb"
+ wget https://dev.mysql.com/get/mysql-apt-config_0.8.35-1_all.deb -P "$VAR_PATH"
+ # resolve expired key issue
+ sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C
+ DEBIAN_FRONTEND="noninteractive" $SUDO dpkg -i "$VAR_PATH/mysql-apt-config_0.8.35-1_all.deb"
$SUDO apt-get update
DEBIAN_FRONTEND="noninteractive" $SUDO apt-get install -y mysql-server
fi
diff --git a/apps/installer/includes/os_configs/windows.sh b/apps/installer/includes/os_configs/windows.sh
index b99c2bfebf..cdba50c27f 100644
--- a/apps/installer/includes/os_configs/windows.sh
+++ b/apps/installer/includes/os_configs/windows.sh
@@ -24,7 +24,6 @@ fi
choco install -y --skip-checksums "${INSTALL_ARGS[@]}" cmake.install -y --installargs 'ADD_CMAKE_TO_PATH=System'
choco install -y --skip-checksums "${INSTALL_ARGS[@]}" visualstudio2022-workload-nativedesktop
-choco install -y --skip-checksums "${INSTALL_ARGS[@]}" openssl --force --version=3.5.3
+choco install -y --skip-checksums "${INSTALL_ARGS[@]}" openssl --force --version=3.5.4
choco install -y --skip-checksums "${INSTALL_ARGS[@]}" boost-msvc-14.3 --force --version=1.87.0
-choco install -y --skip-checksums "${INSTALL_ARGS[@]}" mysql --force --version=8.4.4
-
+choco install -y --skip-checksums "${INSTALL_ARGS[@]}" mysql --force --version=8.4.6
diff --git a/apps/installer/main.sh b/apps/installer/main.sh
index fea9dc3acf..a64787269e 100644
--- a/apps/installer/main.sh
+++ b/apps/installer/main.sh
@@ -45,6 +45,7 @@ menu_items=(
"docker|dr|Run docker tools"
"version|v|Show AzerothCore version"
"service-manager|sm|Run service manager to run authserver and worldserver in background"
+ "config|cf|Configuration manager"
"quit|q|Exit from this menu"
)
@@ -100,6 +101,9 @@ function handle_menu_command() {
bash "$AC_PATH_APPS/startup-scripts/src/service-manager.sh" "$@"
exit
;;
+ "config")
+ bash "$AC_PATH_APPS/installer/includes/config/config-main.sh" "$@"
+ ;;
"quit")
echo "Goodbye!"
exit
diff --git a/apps/installer/test/test_module_commands.bats b/apps/installer/test/test_module_commands.bats
index 1223a80a6d..d829c1a32b 100755
--- a/apps/installer/test/test_module_commands.bats
+++ b/apps/installer/test/test_module_commands.bats
@@ -751,5 +751,5 @@ EOF
run inst_module "unknown-command"
[ "$status" -eq 1 ]
- [[ "$output" =~ "Unknown module command" ]]
+ [[ "$output" =~ "Invalid option" ]]
} \ No newline at end of file
diff --git a/apps/startup-scripts/README.md b/apps/startup-scripts/README.md
index 0c7cb0940d..b99c38bd68 100644
--- a/apps/startup-scripts/README.md
+++ b/apps/startup-scripts/README.md
@@ -453,6 +453,40 @@ This is particularly useful for:
- **Multiple Projects**: Separate service configurations per project
- **Team Collaboration**: Share service setups across development teams
+#### Service Configuration Portability
+
+The service manager automatically stores binary and configuration paths as relative paths when they are located under the `AC_SERVICE_CONFIG_DIR`, making service configurations portable across environments:
+
+```bash
+# Set up a portable project structure
+export AC_SERVICE_CONFIG_DIR="/opt/myproject/services"
+mkdir -p "$AC_SERVICE_CONFIG_DIR"/{bin,etc}
+
+# Copy your binaries and configs
+cp /path/to/compiled/authserver "$AC_SERVICE_CONFIG_DIR/bin/"
+cp /path/to/authserver.conf "$AC_SERVICE_CONFIG_DIR/etc/"
+
+# Create service - paths under AC_SERVICE_CONFIG_DIR will be stored as relative
+./service-manager.sh create auth authserver \
+ --bin-path "$AC_SERVICE_CONFIG_DIR/bin" \
+ --server-config "$AC_SERVICE_CONFIG_DIR/etc/authserver.conf"
+
+# Registry will contain relative paths like "bin/authserver" and "etc/authserver.conf"
+# instead of absolute paths, making the entire directory portable
+```
+
+**Benefits:**
+- **Environment Independence**: Move the entire services directory between machines
+- **Container Friendly**: Perfect for Docker volumes and bind mounts
+- **Backup/Restore**: Archive and restore complete service configurations
+- **Development/Production Parity**: Same relative structure across environments
+
+**How it works:**
+- Paths under `AC_SERVICE_CONFIG_DIR` are automatically stored as relative paths
+- Paths outside `AC_SERVICE_CONFIG_DIR` are stored as absolute paths for safety
+- When services are restored or started, relative paths are resolved from `AC_SERVICE_CONFIG_DIR`
+- If `AC_SERVICE_CONFIG_DIR` is not set, all paths are stored as absolute paths (traditional behavior)
+
#### Migration from Legacy Format
If you have existing services in the old format, use the migration script:
diff --git a/apps/startup-scripts/src/run-engine b/apps/startup-scripts/src/run-engine
index 761e51b3d5..72a3f304ca 100755
--- a/apps/startup-scripts/src/run-engine
+++ b/apps/startup-scripts/src/run-engine
@@ -219,6 +219,13 @@ function parse_arguments() {
export PARSED_CONFIG_FILE="$config_file"
export PARSED_SERVERCONFIG="$serverconfig"
export PARSED_SESSION_MANAGER="$session_manager"
+
+ echo "Parsed arguments:"
+ echo " Mode: $PARSED_MODE"
+ echo " Server Binary: $PARSED_SERVERBIN"
+ echo " Config File: $PARSED_CONFIG_FILE"
+ echo " Server Config: $PARSED_SERVERCONFIG"
+ echo " Session Manager: $PARSED_SESSION_MANAGER"
}
# Start service (single run or with simple-restarter)
diff --git a/apps/startup-scripts/src/service-manager.sh b/apps/startup-scripts/src/service-manager.sh
index ccc4e8e359..4f597fbebe 100755
--- a/apps/startup-scripts/src/service-manager.sh
+++ b/apps/startup-scripts/src/service-manager.sh
@@ -16,6 +16,19 @@ ROOT_DIR="$(cd "$CURRENT_PATH/../../.." && pwd)"
# Configuration directory (can be overridden with AC_SERVICE_CONFIG_DIR)
CONFIG_DIR="${AC_SERVICE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/azerothcore/services}"
REGISTRY_FILE="$CONFIG_DIR/service_registry.json"
+export AC_SERVICE_CONFIG_DIR="${AC_SERVICE_CONFIG_DIR:-$CONFIG_DIR}"
+
+# Default values for variables that might be loaded from config files
+# This prevents "unbound variable" errors when sourcing configuration files
+RUN_ENGINE_CONFIG_FILE="${RUN_ENGINE_CONFIG_FILE:-}"
+SESSION_MANAGER="${SESSION_MANAGER:-}"
+SESSION_NAME="${SESSION_NAME:-}"
+BINPATH="${BINPATH:-}"
+SERVERBIN="${SERVERBIN:-}"
+CONFIG="${CONFIG:-}"
+RESTART_POLICY="${RESTART_POLICY:-}"
+GDB_ENABLED="${GDB_ENABLED:-}"
+SERVER_CONFIG="${SERVER_CONFIG:-}"
# Colors for output
readonly YELLOW='\033[1;33m'
@@ -32,6 +45,114 @@ if [ ! -f "$REGISTRY_FILE" ]; then
echo "[]" > "$REGISTRY_FILE"
fi
+# Path conversion utilities for portability
+# When AC_SERVICE_CONFIG_DIR (hence CONFIG_DIR) is set, always store paths
+# relative to it. Resolve relative paths back against CONFIG_DIR.
+function make_path_relative() {
+ local input="$1"
+
+ # Pass through empty or non-absolute inputs
+ if [ -z "$input" ] || [[ ! "$input" = /* ]]; then
+ echo "$input"
+ return
+ fi
+
+ # If AC_SERVICE_CONFIG_DIR is explicitly set, check if path is under it
+ if [ -n "${AC_SERVICE_CONFIG_DIR:-}" ]; then
+ local config_dir_abs
+ config_dir_abs="$(realpath -m "$AC_SERVICE_CONFIG_DIR" 2>/dev/null || echo "$AC_SERVICE_CONFIG_DIR")"
+ local rel_path=""
+
+ if command -v realpath >/dev/null 2>&1; then
+ rel_path="$(realpath --relative-to="$config_dir_abs" "$input" 2>/dev/null || true)"
+ if [ -z "$rel_path" ]; then
+ rel_path="$(realpath -m --relative-to="$config_dir_abs" "$input" 2>/dev/null || true)"
+ fi
+ fi
+
+ if [ -z "$rel_path" ] && command -v python3 >/dev/null 2>&1; then
+ rel_path="$(python3 -c 'import os,sys; print(os.path.relpath(sys.argv[1], sys.argv[2]))' "$input" "$config_dir_abs" 2>/dev/null || true)"
+ fi
+
+ if [ -n "$rel_path" ]; then
+ echo "$rel_path"
+ return
+ fi
+ fi
+
+ # If not under AC_SERVICE_CONFIG_DIR or AC_SERVICE_CONFIG_DIR not set, return absolute path unchanged
+ echo "$input"
+}
+
+function make_path_absolute() {
+ local input="$1"
+
+ # Already absolute
+ if [[ "$input" = /* ]]; then
+ echo "$input"
+ return
+ fi
+
+ # Resolve relative paths against AC_SERVICE_CONFIG_DIR when explicitly set
+ if [ -n "${AC_SERVICE_CONFIG_DIR:-}" ] && [ -n "$input" ]; then
+ local config_dir_abs
+ config_dir_abs="$(realpath "$AC_SERVICE_CONFIG_DIR" 2>/dev/null || echo "$AC_SERVICE_CONFIG_DIR")"
+ # Join and normalize; do not require the target to exist
+ realpath -m "$config_dir_abs/$input" 2>/dev/null || echo "$config_dir_abs/$input"
+ return
+ fi
+
+ # Fallback: try to normalize relative to current directory
+ realpath -m "$input" 2>/dev/null || echo "$input"
+}
+
+# Tokenize a shell command string without executing it. Supports basic quoting
+# and escaping rules so paths with spaces remain intact.
+# Serialize a command definition (binary + args) to JSON while converting paths
+# to be relative to CONFIG_DIR when possible.
+function serialize_exec_definition() {
+ local command_path="$1"
+ shift
+ local -a args=("$@")
+ local rel_command="$command_path"
+
+ if [[ -n "$command_path" ]]; then
+ rel_command="$(make_path_relative "$command_path")"
+ fi
+
+ local -a rel_args=()
+ local arg
+ for arg in "${args[@]}"; do
+ if [[ "$arg" == /* ]]; then
+ rel_args+=("$(make_path_relative "$arg")")
+ else
+ rel_args+=("$arg")
+ fi
+ done
+
+ local args_json
+ args_json=$(printf '%s\0' "${rel_args[@]}" | jq -R -s 'split("\u0000")[:-1]')
+
+ jq -n --arg command "$rel_command" --argjson args "$args_json" '{command: $command, args: $args}'
+}
+
+# Combine command + args into a shell-safe string suitable for pm2/systemd
+# ExecStart lines. Each token is bash-quoted to preserve spaces.
+function render_exec_command() {
+ local command_path="$1"
+ shift
+ local -a args=("$@")
+ local parts=()
+ local token
+
+ parts+=("$(printf '%q' "$command_path")")
+ for token in "${args[@]}"; do
+ parts+=("$(printf '%q' "$token")")
+ done
+
+ (IFS=' '; printf '%s' "${parts[*]}")
+}
+
# Check dependencies
check_dependencies() {
command -v jq >/dev/null 2>&1 || {
@@ -53,6 +174,18 @@ function add_service_to_registry() {
local gdb_enabled="$9"
local pm2_opts="${10}"
local server_config="${11}"
+ local exec_definition="${12:-}" # JSON payload describing command + args
+
+ # Convert paths to relative if possible for portability
+ local relative_bin_path="$(make_path_relative "$bin_path")"
+
+ # Convert server_config to relative if possible for portability
+ local relative_server_config=""
+ if [ -n "$server_config" ] && [ "$server_config" != "null" ]; then
+ relative_server_config="$(make_path_relative "$server_config")"
+ else
+ relative_server_config="$server_config"
+ fi
# Remove any existing entry with the same service name to avoid duplicates
local tmp_file
@@ -61,10 +194,14 @@ function add_service_to_registry() {
# Add the new entry to the registry
tmp_file=$(mktemp)
+ local exec_json_payload="null"
+ if [ -n "$exec_definition" ]; then
+ exec_json_payload="$exec_definition"
+ fi
jq --arg name "$service_name" \
--arg provider "$provider" \
--arg type "$service_type" \
- --arg bin_path "$bin_path" \
+ --arg bin_path "$relative_bin_path" \
--arg args "$args" \
--arg created "$(date -Iseconds)" \
--arg systemd_type "$systemd_type" \
@@ -72,8 +209,9 @@ function add_service_to_registry() {
--arg session_manager "$session_manager" \
--arg gdb_enabled "$gdb_enabled" \
--arg pm2_opts "$pm2_opts" \
- --arg server_config "$server_config" \
- '. += [{"name": $name, "provider": $provider, "type": $type, "bin_path": $bin_path, "args": $args, "created": $created, "status": "active", "systemd_type": $systemd_type, "restart_policy": $restart_policy, "session_manager": $session_manager, "gdb_enabled": $gdb_enabled, "pm2_opts": $pm2_opts, "server_config": $server_config}]' \
+ --arg server_config "$relative_server_config" \
+ --argjson exec "$exec_json_payload" \
+ '. += [{"name": $name, "provider": $provider, "type": $type, "bin_path": $bin_path, "exec": $exec, "args": $args, "created": $created, "status": "active", "systemd_type": $systemd_type, "restart_policy": $restart_policy, "session_manager": $session_manager, "gdb_enabled": $gdb_enabled, "pm2_opts": $pm2_opts, "server_config": $server_config}]' \
"$REGISTRY_FILE" > "$tmp_file" && mv "$tmp_file" "$REGISTRY_FILE"
echo -e "${GREEN}Service '$service_name' added to registry${NC}"
@@ -117,7 +255,7 @@ function restore_missing_services() {
local name=$(echo "$service" | jq -r '.name')
local provider=$(echo "$service" | jq -r '.provider')
local service_type=$(echo "$service" | jq -r '.type')
- local bin_path=$(echo "$service" | jq -r '.bin_path // "unknown"')
+ local bin_path_raw=$(echo "$service" | jq -r '.bin_path // "unknown"')
local args=$(echo "$service" | jq -r '.args // ""')
local status=$(echo "$service" | jq -r '.status // "active"')
local systemd_type=$(echo "$service" | jq -r '.systemd_type // "--user"')
@@ -125,11 +263,23 @@ function restore_missing_services() {
local session_manager=$(echo "$service" | jq -r '.session_manager // "none"')
local gdb_enabled=$(echo "$service" | jq -r '.gdb_enabled // "0"')
local pm2_opts=$(echo "$service" | jq -r '.pm2_opts // ""')
- local server_config=$(echo "$service" | jq -r '.server_config // ""')
+ local server_config_raw=$(echo "$service" | jq -r '.server_config // ""')
+
+ # Convert paths back to absolute for operation
+ local bin_path="$(make_path_absolute "$bin_path_raw")"
+ local server_config=""
+ if [ -n "$server_config_raw" ] && [ "$server_config_raw" != "null" ] && [ "$server_config_raw" != "" ]; then
+ server_config="$(make_path_absolute "$server_config_raw")"
+ else
+ server_config="$server_config_raw"
+ fi
local service_exists=false
if [ "$provider" = "pm2" ]; then
+ echo "Check if PM2 is installed..."
+ check_pm2 || { echo -e "${RED}PM2 is not installed. Cannot check service status.${NC}"; exit 1; }
+
if pm2 describe "$name" >/dev/null 2>&1; then
service_exists=true
fi
@@ -166,14 +316,61 @@ function restore_missing_services() {
local name=$(echo "$service" | jq -r '.name')
local provider=$(echo "$service" | jq -r '.provider')
local service_type=$(echo "$service" | jq -r '.type')
- local bin_path=$(echo "$service" | jq -r '.bin_path')
- local args=$(echo "$service" | jq -r '.args')
local systemd_type=$(echo "$service" | jq -r '.systemd_type // "--user"')
local restart_policy=$(echo "$service" | jq -r '.restart_policy // "always"')
local session_manager=$(echo "$service" | jq -r '.session_manager // "none"')
local gdb_enabled=$(echo "$service" | jq -r '.gdb_enabled // "0"')
local pm2_opts=$(echo "$service" | jq -r '.pm2_opts // ""')
- local server_config=$(echo "$service" | jq -r '.server_config // ""')
+ local status=$(echo "$service" | jq -r '.status // "active"')
+ local service_resolved="$(get_service_info_resolved "$name")"
+ local bin_path="$(echo "$service_resolved" | jq -r '.bin_path // "unknown"')"
+ local server_config="$(echo "$service_resolved" | jq -r '.server_config // ""')"
+ if [ "$server_config" = "null" ]; then
+ server_config=""
+ fi
+ local exec_definition_raw="$(echo "$service" | jq -c '.exec // null')"
+ local exec_command_abs="$(echo "$service_resolved" | jq -r '.exec.command // ""')"
+ local -a exec_args_abs=()
+ if [ -n "$exec_definition_raw" ] && [ "$exec_definition_raw" != "null" ]; then
+ while IFS= read -r arg; do
+ exec_args_abs+=("$arg")
+ done < <(echo "$service_resolved" | jq -r '.exec.args[]?')
+ fi
+ local exec_command_string=""
+ if [ -n "$exec_command_abs" ] && [ "$exec_command_abs" != "null" ]; then
+ exec_command_string="$(render_exec_command "$exec_command_abs" "${exec_args_abs[@]}")"
+ fi
+ local server_binary_path_for_configs="$bin_path"
+ local run_engine_config_path=""
+ if [ ${#exec_args_abs[@]} -gt 0 ]; then
+ if [ -n "${exec_args_abs[1]:-}" ] && [ "${exec_args_abs[1]}" != "null" ]; then
+ server_binary_path_for_configs="${exec_args_abs[1]}"
+ fi
+ local arg_index
+ for arg_index in "${!exec_args_abs[@]}"; do
+ if [ "${exec_args_abs[$arg_index]}" = "--config" ]; then
+ local next_index=$((arg_index + 1))
+ if [ $next_index -lt ${#exec_args_abs[@]} ]; then
+ run_engine_config_path="${exec_args_abs[$next_index]}"
+ fi
+ break
+ fi
+ done
+ fi
+ if [ -n "$server_binary_path_for_configs" ] && [ "$server_binary_path_for_configs" != "unknown" ] && [ "$server_binary_path_for_configs" != "null" ]; then
+ server_binary_path_for_configs="$(make_path_absolute "$server_binary_path_for_configs")"
+ fi
+ if [ -z "$run_engine_config_path" ] || [ "$run_engine_config_path" = "null" ]; then
+ run_engine_config_path="$CONFIG_DIR/$name-run-engine.conf"
+ else
+ run_engine_config_path="$(make_path_absolute "$run_engine_config_path")"
+ fi
+ if [ -d "$run_engine_config_path" ]; then
+ run_engine_config_path="$CONFIG_DIR/$name-run-engine.conf"
+ fi
+ if [[ "$run_engine_config_path" != /* ]]; then
+ run_engine_config_path="$(make_path_absolute "$run_engine_config_path")"
+ fi
echo ""
echo -e "${YELLOW}Service '$name' ($provider) is missing${NC}"
@@ -182,13 +379,20 @@ function restore_missing_services() {
if [ "$bin_path" = "unknown" ] || [ "$bin_path" = "null" ] || [ "$status" = "migrated" ]; then
echo " Binary: <needs manual configuration>"
- echo " Args: <needs manual configuration>"
+ echo " Exec: <needs manual configuration>"
echo ""
echo -e "${YELLOW}This service needs to be recreated manually:${NC}"
echo " $0 create $service_type $name --provider $provider --bin-path /path/to/your/bin"
else
echo " Binary: $bin_path"
- echo " Args: $args"
+ if [ -n "$exec_command_string" ]; then
+ echo " Exec: $exec_command_string"
+ else
+ echo " Exec: <unavailable>"
+ fi
+ if [ -n "$server_config" ]; then
+ echo " Server config: $server_config"
+ fi
fi
echo ""
@@ -204,19 +408,30 @@ function restore_missing_services() {
fi
else
echo -e "${BLUE}Recreating service '$name'...${NC}"
+ if ! ensure_service_configs_restored "$name" "$service_type" "$provider" "$server_binary_path_for_configs" "$server_config" "$restart_policy" "$session_manager" "$gdb_enabled" "$systemd_type" "$pm2_opts" "$run_engine_config_path" "false"; then
+ echo -e "${YELLOW}Warning: Unable to restore configuration files for '$name'${NC}"
+ fi
if [ "$provider" = "pm2" ]; then
- if [ "$args" != "null" ] && [ -n "$args" ]; then
- pm2_create_service "$name" "$bin_path $args" "$restart_policy" $pm2_opts
+ if [ -z "$exec_command_string" ] || [ "$exec_definition_raw" = "null" ]; then
+ echo -e "${RED}Cannot recreate PM2 service automatically: missing exec definition${NC}"
else
- pm2_create_service "$name" "$bin_path" "$restart_policy" $pm2_opts
+ if [ -n "$pm2_opts" ]; then
+ pm2_create_service "$name" "$exec_command_string" "$restart_policy" "$bin_path" "$server_config" "$exec_definition_raw" $pm2_opts
+ else
+ pm2_create_service "$name" "$exec_command_string" "$restart_policy" "$bin_path" "$server_config" "$exec_definition_raw"
+ fi
fi
elif [ "$provider" = "systemd" ]; then
echo -e "${BLUE}Attempting to recreate systemd service '$name' automatically...${NC}"
- if systemd_create_service "$name" "$bin_path $args" "$restart_policy" "$systemd_type" "$session_manager" "$gdb_enabled" "$server_config"; then
- echo -e "${GREEN}Systemd service '$name' recreated successfully${NC}"
+ if [ -z "$exec_command_string" ] || [ "$exec_definition_raw" = "null" ]; then
+ echo -e "${RED}Cannot recreate systemd service automatically: missing exec definition${NC}"
else
- echo -e "${RED}Failed to recreate systemd service '$name'. Please recreate manually.${NC}"
- echo " $0 create $name $service_type --provider systemd --bin-path $bin_path"
+ if systemd_create_service "$name" "$exec_command_string" "$restart_policy" "$bin_path" "$server_config" "$exec_definition_raw" "$systemd_type"; then
+ echo -e "${GREEN}Systemd service '$name' recreated successfully${NC}"
+ else
+ echo -e "${RED}Failed to recreate systemd service '$name'. Please recreate manually.${NC}"
+ echo " $0 create $name $service_type --provider systemd --bin-path $bin_path"
+ fi
fi
fi
fi
@@ -275,7 +490,7 @@ function print_help() {
echo " $base_name update <service-name> [options]"
echo " $base_name delete <service-name>"
echo " $base_name list [provider]"
- echo " $base_name restore"
+ echo " $base_name restore [--sync-only]"
echo " $base_name start|stop|restart|status <service-name>"
echo " $base_name logs <service-name> [--follow]"
echo " $base_name attach <service-name>"
@@ -342,6 +557,9 @@ function print_help() {
echo " # Restore missing services from registry"
echo " $base_name restore"
echo ""
+ echo " # Normalize registry paths to be relative to AC_SERVICE_CONFIG_DIR"
+ echo " $base_name registry-normalize"
+ echo ""
echo "Notes:"
echo " - Configuration editing modifies run-engine settings (GDB, session manager, etc.)"
echo " - Use --server-config for the actual server configuration file"
@@ -353,10 +571,13 @@ function print_help() {
echo " - attach command automatically detects the configured session manager and connects appropriately"
echo " - attach always provides interactive access to the server console"
echo " - Use 'logs' command to view service logs without interaction"
- echo " - restore command checks registry and helps recreate missing services"
+ echo " - restore command first normalizes registry paths, syncs config files, and then recreates missing services"
echo ""
echo "Environment Variables:"
echo " AC_SERVICE_CONFIG_DIR - Override default config directory for services registry"
+ echo " When set, binary and configuration paths under this directory"
+ echo " are stored as relative paths for improved portability."
+ echo " Use '$base_name registry-normalize' to rewrite existing entries."
}
@@ -442,19 +663,314 @@ function get_service_info() {
jq --arg name "$service_name" '.[] | select(.name == $name)' "$REGISTRY_FILE"
}
+# Get service info with resolved paths (relative paths converted to absolute)
+function get_service_info_resolved() {
+ local service_name="$1"
+ local service_info="$(get_service_info "$service_name")"
+
+ if [ -z "$service_info" ] || [ "$service_info" = "null" ]; then
+ echo ""
+ return
+ fi
+
+ # Extract paths and convert them
+ local bin_path_raw="$(echo "$service_info" | jq -r '.bin_path // ""')"
+ local server_config_raw="$(echo "$service_info" | jq -r '.server_config // ""')"
+ local exec_command_raw="$(echo "$service_info" | jq -r '.exec.command // ""')"
+ local exec_args_raw_json="$(echo "$service_info" | jq -c '.exec.args // []')"
+
+ local bin_path_resolved=""
+ local server_config_resolved=""
+ local exec_command_resolved=""
+ local -a exec_args_resolved=()
+
+ if [ -n "$bin_path_raw" ] && [ "$bin_path_raw" != "null" ] && [ "$bin_path_raw" != "" ]; then
+ bin_path_resolved="$(make_path_absolute "$bin_path_raw")"
+ else
+ bin_path_resolved="$bin_path_raw"
+ fi
+
+ if [ -n "$exec_command_raw" ] && [ "$exec_command_raw" != "null" ]; then
+ exec_command_resolved="$(make_path_absolute "$exec_command_raw")"
+ else
+ exec_command_resolved="$exec_command_raw"
+ fi
+
+ if [ -n "$exec_args_raw_json" ] && [ "$exec_args_raw_json" != "null" ]; then
+ local prev_arg=""
+ while IFS= read -r arg; do
+ if [[ -z "$arg" ]]; then
+ exec_args_resolved+=("$arg")
+ elif [ "$prev_arg" = "--config" ]; then
+ exec_args_resolved+=("$(make_path_absolute "$arg")")
+ elif [[ "$arg" == /* ]]; then
+ exec_args_resolved+=("$(make_path_absolute "$arg")")
+ elif [[ "$arg" == */* && "$arg" != -* ]]; then
+ exec_args_resolved+=("$(make_path_absolute "$arg")")
+ else
+ exec_args_resolved+=("$arg")
+ fi
+ prev_arg="$arg"
+ done < <(echo "$exec_args_raw_json" | jq -r '.[]?')
+ fi
+
+ if [ -n "$server_config_raw" ] && [ "$server_config_raw" != "null" ] && [ "$server_config_raw" != "" ]; then
+ server_config_resolved="$(make_path_absolute "$server_config_raw")"
+ else
+ server_config_resolved="$server_config_raw"
+ fi
+
+ local exec_args_resolved_json
+ exec_args_resolved_json=$(printf '%s\0' "${exec_args_resolved[@]}" | jq -R -s 'split("\u0000")[:-1]')
+ local exec_resolved_json
+ exec_resolved_json=$(jq -n --arg command "${exec_command_resolved:-}" --argjson args "$exec_args_resolved_json" '{command: $command, args: $args}')
+
+ # Return the service info with resolved paths
+ echo "$service_info" | jq --arg bin_path "$bin_path_resolved" \
+ --arg server_config "$server_config_resolved" \
+ --argjson exec "$exec_resolved_json" \
+ '.bin_path = $bin_path | .exec = $exec | .server_config = $server_config'
+}
+
+function ensure_service_configs_restored() {
+ local service_name="$1"
+ local service_type="$2"
+ local provider="$3"
+ local server_binary_path="$4"
+ local server_config_path="$5"
+ local restart_policy="$6"
+ local session_manager="$7"
+ local gdb_enabled="$8"
+ local systemd_type="$9"
+ local pm2_opts="${10:-}"
+ local run_engine_config_path="${11:-}"
+ local force_rewrite="${12:-false}"
+
+ if [ -z "$service_name" ]; then
+ return 1
+ fi
+
+ if [ -z "$server_binary_path" ] || [ "$server_binary_path" = "null" ] || [ "$server_binary_path" = "unknown" ]; then
+ return 0
+ fi
+
+ if [ "$server_config_path" = "null" ]; then
+ server_config_path=""
+ fi
+
+ if [ -z "$restart_policy" ] || [ "$restart_policy" = "null" ]; then
+ restart_policy="always"
+ fi
+
+ if [ -z "$session_manager" ] || [ "$session_manager" = "null" ]; then
+ session_manager="none"
+ fi
+
+ if [ -z "$gdb_enabled" ] || [ "$gdb_enabled" = "null" ]; then
+ gdb_enabled="0"
+ fi
+
+ if [ -z "$systemd_type" ] || [ "$systemd_type" = "null" ]; then
+ systemd_type="--user"
+ fi
+
+ pm2_opts="${pm2_opts:-}"
+ pm2_opts="${pm2_opts//$'\n'/ }"
+ pm2_opts="${pm2_opts#"${pm2_opts%%[![:space:]]*}"}"
+ pm2_opts="${pm2_opts%"${pm2_opts##*[![:space:]]}"}"
+
+ if [ -z "$run_engine_config_path" ] || [ "$run_engine_config_path" = "null" ]; then
+ run_engine_config_path="$CONFIG_DIR/$service_name-run-engine.conf"
+ fi
+
+ local service_conf_path="$CONFIG_DIR/$service_name.conf"
+ local server_binary_dir
+ local server_binary_name
+
+ server_binary_dir="$(dirname "$server_binary_path")"
+ server_binary_name="$(basename "$server_binary_path")"
+
+ mkdir -p "$(dirname "$run_engine_config_path")"
+
+ if [ "$force_rewrite" = "true" ] || [ ! -f "$run_engine_config_path" ]; then
+ echo -e "${BLUE}Restoring run-engine config: $run_engine_config_path${NC}"
+ cat > "$run_engine_config_path" << EOF
+# run-engine configuration for service: $service_name
+# Restored: $(date)
+
+# Enable/disable GDB execution
+export GDB_ENABLED=$gdb_enabled
+
+# Session manager (none|auto|tmux|screen)
+export SESSION_MANAGER="$session_manager"
+
+# Restart policy (on-failure|always)
+export RESTART_POLICY="$restart_policy"
+
+# Service mode - indicates this is running under a service manager (systemd/pm2)
+# When true, AC_DISABLE_INTERACTIVE will be set if no interactive session manager is used
+export SERVICE_MODE="true"
+
+# Session name for tmux/screen (optional)
+export SESSION_NAME="${service_name}"
+
+# Binary directory path
+export BINPATH="$server_binary_dir"
+
+# Server binary name
+export SERVERBIN="$server_binary_name"
+
+# Server configuration file path
+export CONFIG="$server_config_path"
+
+# Show console output for easier debugging
+export WITH_CONSOLE=1
+EOF
+ fi
+
+ if [ "$force_rewrite" = "true" ] || [ ! -f "$service_conf_path" ]; then
+ echo -e "${BLUE}Restoring service metadata: $service_conf_path${NC}"
+ cat > "$service_conf_path" << EOF
+# AzerothCore service configuration for $service_name
+# Restored: $(date)
+# Provider: $provider
+# Service Type: $service_type
+
+# run-engine configuration file
+RUN_ENGINE_CONFIG_FILE="$run_engine_config_path"
+
+# Restart policy
+RESTART_POLICY="$restart_policy"
+
+# Provider-specific options
+SYSTEMD_TYPE="$systemd_type"
+PM2_OPTS="$pm2_opts"
+EOF
+ fi
+
+ return 0
+}
+
+function sync_service_configs_from_registry() {
+ echo -e "${BLUE}Syncing service configuration files from registry...${NC}"
+
+ if [ ! -f "$REGISTRY_FILE" ] || [ ! -s "$REGISTRY_FILE" ]; then
+ echo -e "${YELLOW}No services registry found or empty${NC}"
+ return 0
+ fi
+
+ local services_count
+ services_count=$(jq length "$REGISTRY_FILE")
+ if [ "$services_count" -eq 0 ]; then
+ echo -e "${YELLOW}No services registered${NC}"
+ return 0
+ fi
+
+ local synced=0
+
+ for i in $(seq 0 $((services_count - 1))); do
+ local entry
+ entry=$(jq -c ".[$i]" "$REGISTRY_FILE")
+ [ -z "$entry" ] && continue
+
+ local name
+ name=$(echo "$entry" | jq -r '.name // empty')
+ [ -z "$name" ] && continue
+
+ local service_resolved
+ service_resolved="$(get_service_info_resolved "$name")"
+ [ -z "$service_resolved" ] && continue
+
+ local service_type provider restart_policy session_manager gdb_enabled systemd_type pm2_opts
+ service_type=$(echo "$entry" | jq -r '.type // ""')
+ provider=$(echo "$entry" | jq -r '.provider // ""')
+ restart_policy=$(echo "$entry" | jq -r '.restart_policy // "always"')
+ session_manager=$(echo "$entry" | jq -r '.session_manager // "none"')
+ gdb_enabled=$(echo "$entry" | jq -r '.gdb_enabled // "0"')
+ systemd_type=$(echo "$entry" | jq -r '.systemd_type // "--user"')
+ pm2_opts=$(echo "$entry" | jq -r '.pm2_opts // ""')
+
+ local server_binary_path
+ server_binary_path=$(echo "$service_resolved" | jq -r '.bin_path // ""')
+ if [ -z "$server_binary_path" ] || [ "$server_binary_path" = "null" ] || [ "$server_binary_path" = "unknown" ]; then
+ server_binary_path=""
+ fi
+
+ local server_config_path
+ server_config_path=$(echo "$service_resolved" | jq -r '.server_config // ""')
+ if [ -n "$server_config_path" ] && [ "$server_config_path" != "null" ]; then
+ server_config_path="$(make_path_absolute "$server_config_path")"
+ else
+ server_config_path=""
+ fi
+
+ local exec_definition
+ exec_definition=$(echo "$entry" | jq -c '.exec // null')
+ local -a exec_args=()
+ if [ -n "$exec_definition" ] && [ "$exec_definition" != "null" ]; then
+ while IFS= read -r arg; do
+ exec_args+=("$arg")
+ done < <(echo "$exec_definition" | jq -r '.args[]?')
+ fi
+
+ if { [ -z "$server_binary_path" ] || [ "$server_binary_path" = "null" ]; } && [ ${#exec_args[@]} -ge 2 ]; then
+ server_binary_path="$(make_path_absolute "${exec_args[1]}")"
+ fi
+
+ local run_engine_config_rel=""
+ for idx in "${!exec_args[@]}"; do
+ if [ "${exec_args[$idx]}" = "--config" ]; then
+ local next_idx=$((idx + 1))
+ if [ $next_idx -lt ${#exec_args[@]} ]; then
+ run_engine_config_rel="${exec_args[$next_idx]}"
+ fi
+ break
+ fi
+ done
+
+ if [ -z "$run_engine_config_rel" ] || [ "$run_engine_config_rel" = "null" ]; then
+ run_engine_config_rel="$name-run-engine.conf"
+ fi
+
+ local run_engine_config_path
+ if [[ "$run_engine_config_rel" = /* ]]; then
+ run_engine_config_path="$run_engine_config_rel"
+ else
+ run_engine_config_path="$CONFIG_DIR/$run_engine_config_rel"
+ fi
+ run_engine_config_path="$(realpath -m "$run_engine_config_path" 2>/dev/null || echo "$run_engine_config_path")"
+
+ if [ -n "$server_binary_path" ]; then
+ server_binary_path="$(make_path_absolute "$server_binary_path")"
+ fi
+
+ ensure_service_configs_restored "$name" "$service_type" "$provider" "$server_binary_path" "$server_config_path" "$restart_policy" "$session_manager" "$gdb_enabled" "$systemd_type" "$pm2_opts" "$run_engine_config_path" "true"
+ synced=$((synced + 1))
+ done
+
+ if [ "$synced" -gt 0 ]; then
+ echo -e "${GREEN}Synchronized $synced service configuration file(s)${NC}"
+ else
+ echo -e "${YELLOW}No service configuration files required synchronization${NC}"
+ fi
+}
+
# PM2 service management functions
function pm2_create_service() {
local service_name="$1"
- local command="$2"
+ local command_string="$2"
local restart_policy="$3"
- shift 3
+ local real_bin_path="$4"
+ local server_config_path="$5"
+ local exec_definition="$6"
+ shift 6
check_pm2 || return 1
# Parse additional PM2 options
local max_memory=""
local max_restarts=""
- local additional_args=""
+ local -a extra_pm2_args=()
while [[ $# -gt 0 ]]; do
case "$1" in
@@ -467,39 +983,79 @@ function pm2_create_service() {
shift 2
;;
*)
- additional_args+=" $1"
+ extra_pm2_args+=("$1")
shift
;;
esac
done
-
+
+ if [ -z "$exec_definition" ] || [ "$exec_definition" = "null" ]; then
+ echo -e "${RED}Error: Missing exec definition for PM2 service '$service_name'${NC}"
+ return 1
+ fi
+
+ local exec_command_rel
+ exec_command_rel=$(echo "$exec_definition" | jq -r '.command // empty')
+ if [ -z "$exec_command_rel" ] || [ "$exec_command_rel" = "null" ]; then
+ echo -e "${RED}Error: Exec command not defined for service '$service_name'${NC}"
+ return 1
+ fi
+
+ local exec_command_abs
+ exec_command_abs="$(make_path_absolute "$exec_command_rel")"
+ local -a exec_args_abs=()
+ local prev_arg=""
+ while IFS= read -r arg; do
+ if [[ -z "$arg" ]]; then
+ exec_args_abs+=("$arg")
+ elif [ "$prev_arg" = "--config" ]; then
+ exec_args_abs+=("$(make_path_absolute "$arg")")
+ elif [[ "$arg" == /* ]]; then
+ exec_args_abs+=("$(make_path_absolute "$arg")")
+ elif [[ "$arg" == */* && "$arg" != -* ]]; then
+ exec_args_abs+=("$(make_path_absolute "$arg")")
+ else
+ exec_args_abs+=("$arg")
+ fi
+ prev_arg="$arg"
+ done < <(echo "$exec_definition" | jq -r '.args[]?')
# Set stop exit codes based on restart policy
- local stop_exit_codes=""
+ local -a pm2_args=(start "$exec_command_abs" --interpreter none --name "$service_name")
if [ "$restart_policy" = "always" ]; then
# PM2 will restart on any exit code (including 0)
- stop_exit_codes=""
+ :
else
# PM2 will not restart on clean shutdown (exit code 0)
- stop_exit_codes=" --stop-exit-codes 0"
+ pm2_args+=("--stop-exit-codes" "0")
fi
- # Build PM2 start command with AzerothCore environment variable
- local pm2_cmd="AC_LAUNCHED_BY_PM2=1 pm2 start '$command$additional_args' --name '$service_name'$stop_exit_codes"
# Add memory limit if specified
if [ -n "$max_memory" ]; then
- pm2_cmd+=" --max-memory-restart $max_memory"
+ pm2_args+=("--max-memory-restart" "$max_memory")
fi
# Add max restarts if specified
if [ -n "$max_restarts" ]; then
- pm2_cmd+=" --max-restarts $max_restarts"
+ pm2_args+=("--max-restarts" "$max_restarts")
+ fi
+
+ if [ ${#extra_pm2_args[@]} -gt 0 ]; then
+ pm2_args+=("${extra_pm2_args[@]}")
+ fi
+
+ if [ ${#exec_args_abs[@]} -gt 0 ]; then
+ pm2_args+=("--")
+ pm2_args+=("${exec_args_abs[@]}")
fi
# Execute command
echo -e "${YELLOW}Creating PM2 service: $service_name${NC}"
+ if [ -n "$command_string" ]; then
+ echo " Exec: $command_string"
+ fi
- if eval "$pm2_cmd"; then
+ if AC_LAUNCHED_BY_PM2=1 pm2 "${pm2_args[@]}"; then
echo -e "${GREEN}PM2 service '$service_name' created successfully${NC}"
pm2 save
@@ -507,9 +1063,13 @@ function pm2_create_service() {
echo -e "${BLUE}Configuring PM2 startup for persistence...${NC}"
pm2 startup --auto >/dev/null 2>&1 || true
- # Add to registry (extract command and args from the full command)
- local clean_command="$command$additional_args"
- add_service_to_registry "$service_name" "pm2" "executable" "$command" "$additional_args" "" "$restart_policy" "none" "0" "$max_memory $max_restarts" ""
+ # Add to registry (use real binary path instead of command for portability)
+ local extra_args_serialized=""
+ if [ ${#extra_pm2_args[@]} -gt 0 ]; then
+ extra_args_serialized="$(printf '%s ' "${extra_pm2_args[@]}")"
+ extra_args_serialized="${extra_args_serialized% }"
+ fi
+ add_service_to_registry "$service_name" "pm2" "executable" "$real_bin_path" "$extra_args_serialized" "" "$restart_policy" "none" "0" "$max_memory $max_restarts" "$server_config_path" "$exec_definition"
return 0
else
@@ -552,12 +1112,12 @@ function pm2_remove_service() {
pm2 save
echo -e "${GREEN}PM2 service '$service_name' stopped and removed${NC}"
- # Remove from registry
- remove_service_from_registry "$service_name"
+ # Note: Registry removal is handled by the caller (delete_service)
+ return 0
else
- echo -e "${YELLOW}PM2 service '$service_name' not found or already removed${NC}"
- # Still try to remove from registry in case it's orphaned
- remove_service_from_registry "$service_name"
+ echo -e "${YELLOW}PM2 service '$service_name' not found${NC}"
+ # Service not found in PM2 - let caller decide about registry
+ return 0
fi
return 0
@@ -602,11 +1162,14 @@ function systemd_create_service() {
local service_name="$1"
local command="$2"
local restart_policy="$3"
+ local real_bin_path="$4"
+ local server_config_path="$5"
+ local exec_definition="$6"
local systemd_type="--user"
local bin_path=""
local gdb_enabled="0"
local server_config=""
- shift 3
+ shift 6
check_systemd || return 1
@@ -743,7 +1306,7 @@ EOF
echo -e "${GREEN}Systemd service '$service_name' created successfully with session manager '$session_manager'${NC}"
# Add to registry
- add_service_to_registry "$service_name" "systemd" "service" "$command" "" "$systemd_type" "$restart_policy" "$session_manager" "$gdb_enabled" "" "$server_config"
+ add_service_to_registry "$service_name" "systemd" "service" "$real_bin_path" "" "$systemd_type" "$restart_policy" "$session_manager" "$gdb_enabled" "" "$server_config_path" "$exec_definition"
return 0
}
@@ -805,9 +1368,7 @@ function systemd_remove_service() {
echo -e "${YELLOW}Note: Service may still be running but configuration was removed${NC}"
fi
- # Remove from registry
- remove_service_from_registry "$service_name"
-
+ # Note: Registry removal is handled by the caller (delete_service)
return 0
else
echo -e "${RED}Failed to remove systemd service file '$service_file'${NC}"
@@ -1051,24 +1612,29 @@ SYSTEMD_TYPE="$systemd_type"
PM2_OPTS="$pm2_opts"
EOF
- # Build run-engine command
- local run_engine_cmd="$SCRIPT_DIR/run-engine start $server_binary_path --config $run_engine_config"
+ # Build run-engine command definition
+ local run_engine_path="$SCRIPT_DIR/run-engine"
+ local -a run_engine_args=("start" "$server_binary_path" "--config" "$run_engine_config")
+ local run_engine_cmd
+ run_engine_cmd="$(render_exec_command "$run_engine_path" "${run_engine_args[@]}")"
+ local exec_definition
+ exec_definition="$(serialize_exec_definition "$run_engine_path" "${run_engine_args[@]}")"
# Create the actual service
local service_creation_success=false
if [ "$provider" = "pm2" ]; then
if [ -n "$pm2_opts" ]; then
- if pm2_create_service "$service_name" "$run_engine_cmd" "$restart_policy" $pm2_opts; then
+ if pm2_create_service "$service_name" "$run_engine_cmd" "$restart_policy" "$server_binary_path" "$server_config" "$exec_definition" $pm2_opts; then
service_creation_success=true
fi
else
- if pm2_create_service "$service_name" "$run_engine_cmd" "$restart_policy"; then
+ if pm2_create_service "$service_name" "$run_engine_cmd" "$restart_policy" "$server_binary_path" "$server_config" "$exec_definition"; then
service_creation_success=true
fi
fi
elif [ "$provider" = "systemd" ]; then
- if systemd_create_service "$service_name" "$run_engine_cmd" "$restart_policy" "$systemd_type"; then
+ if systemd_create_service "$service_name" "$run_engine_cmd" "$restart_policy" "$server_binary_path" "$server_config" "$exec_definition" "$systemd_type"; then
service_creation_success=true
fi
fi
@@ -1124,10 +1690,10 @@ function update_service() {
source "$config_file"
# Load current run-engine configuration
- if [ -f "$RUN_ENGINE_CONFIG_FILE" ]; then
+ if [ -n "${RUN_ENGINE_CONFIG_FILE:-}" ] && [ -f "$RUN_ENGINE_CONFIG_FILE" ]; then
source "$RUN_ENGINE_CONFIG_FILE"
else
- echo -e "${YELLOW}Warning: Run-engine configuration file not found: $RUN_ENGINE_CONFIG_FILE${NC}"
+ echo -e "${YELLOW}Warning: Run-engine configuration file not found: ${RUN_ENGINE_CONFIG_FILE:-<unset>}${NC}"
fi
# Parse options to update
@@ -1284,8 +1850,11 @@ function delete_service() {
fi
if [ "$removal_success" = "true" ]; then
+ # Remove from registry only after successful service removal
+ remove_service_from_registry "$service_name"
+
# Remove run-engine configuration file
- if [ -n "$RUN_ENGINE_CONFIG_FILE" ] && [ -f "$RUN_ENGINE_CONFIG_FILE" ]; then
+ if [ -n "${RUN_ENGINE_CONFIG_FILE:-}" ] && [ -f "$RUN_ENGINE_CONFIG_FILE" ]; then
rm -f "$RUN_ENGINE_CONFIG_FILE"
echo -e "${GREEN}Removed run-engine config: $RUN_ENGINE_CONFIG_FILE${NC}"
fi
@@ -1296,6 +1865,7 @@ function delete_service() {
echo -e "${GREEN}Service '$service_name' deleted successfully${NC}"
else
echo -e "${RED}Failed to remove service '$service_name' from $provider${NC}"
+ echo -e "${YELLOW}Registry entry preserved. Service may need manual cleanup.${NC}"
return 1
fi
}
@@ -1416,7 +1986,11 @@ function edit_config() {
# Open run-engine configuration file in editor
echo -e "${YELLOW}Editing run-engine configuration for: $service_name${NC}"
- echo -e "${BLUE}File: $RUN_ENGINE_CONFIG_FILE${NC}"
+ echo -e "${BLUE}File: ${RUN_ENGINE_CONFIG_FILE:-<unset>}${NC}"
+ if [ -z "${RUN_ENGINE_CONFIG_FILE:-}" ] || [ ! -f "$RUN_ENGINE_CONFIG_FILE" ]; then
+ echo -e "${RED}Error: Run-engine configuration file not found: ${RUN_ENGINE_CONFIG_FILE:-<unset>}${NC}"
+ return 1
+ fi
${EDITOR:-nano} "$RUN_ENGINE_CONFIG_FILE"
echo -e "${GREEN}Configuration updated. Run '$0 restart $service_name' to apply changes.${NC}"
@@ -1445,16 +2019,12 @@ function attach_to_service() {
source "$config_file"
# Load run-engine configuration
- if [ ! -f "$RUN_ENGINE_CONFIG_FILE" ]; then
- echo -e "${RED}Error: Run-engine configuration file not found: $RUN_ENGINE_CONFIG_FILE${NC}"
+ if [ -z "${RUN_ENGINE_CONFIG_FILE:-}" ] || [ ! -f "$RUN_ENGINE_CONFIG_FILE" ]; then
+ echo -e "${RED}Error: Run-engine configuration file not found: ${RUN_ENGINE_CONFIG_FILE:-<unset>}${NC}"
return 1
fi
- if [ ! -f "$RUN_ENGINE_CONFIG_FILE" ]; then
- echo -e "${RED}Error: Run-engine configuration file not found: $RUN_ENGINE_CONFIG_FILE${NC}"
- return 1
- fi
-
+ # Now safe to source
source "$RUN_ENGINE_CONFIG_FILE"
# Auto-detect session manager and attach accordingly
@@ -1721,6 +2291,9 @@ function wait_service_uptime() {
sleep 1
waited=$((waited + 1))
done
+ # show service logs for debugging
+ echo -e "${YELLOW}Service logs for '$service_name':${NC}"
+ service_logs "$service_name" true
echo -e "${RED}Timeout: $service_name did not reach ${min_seconds}s uptime within ${timeout}s${NC}" >&2
return 1
}
@@ -1770,8 +2343,8 @@ function attach_interactive_shell() {
source "$config_file"
# Check if RUN_ENGINE_CONFIG_FILE exists before sourcing
- if [ ! -f "$RUN_ENGINE_CONFIG_FILE" ]; then
- echo -e "${RED}Error: Run-engine configuration file not found: $RUN_ENGINE_CONFIG_FILE${NC}"
+ if [ -z "${RUN_ENGINE_CONFIG_FILE:-}" ] || [ ! -f "$RUN_ENGINE_CONFIG_FILE" ]; then
+ echo -e "${RED}Error: Run-engine configuration file not found: ${RUN_ENGINE_CONFIG_FILE:-<unset>}${NC}"
return 1
fi
@@ -1796,6 +2369,96 @@ function attach_interactive_shell() {
return 1
}
+# Normalize existing registry entries to use relative paths for fields that
+# represent filesystem paths (bin_path, server_config) and refresh exec command
+# definitions to the new schema.
+function normalize_registry_paths() {
+ if [ ! -f "$REGISTRY_FILE" ]; then
+ echo -e "${YELLOW}No registry file found at: $REGISTRY_FILE${NC}"
+ return 0
+ fi
+
+ # Validate JSON
+ if ! jq empty "$REGISTRY_FILE" >/dev/null 2>&1; then
+ echo -e "${RED}Error: Invalid JSON in $REGISTRY_FILE${NC}"
+ return 1
+ fi
+
+ local tmp_file
+ tmp_file=$(mktemp)
+ echo "[]" > "$tmp_file"
+
+ local count=0
+ while IFS= read -r entry; do
+ [ -z "$entry" ] && continue
+
+ # Extract raw values (empty if null or missing)
+ local bin_path_raw server_config_raw
+ bin_path_raw=$(echo "$entry" | jq -r '.bin_path // empty')
+ server_config_raw=$(echo "$entry" | jq -r '.server_config // empty')
+
+ # Compute normalized values
+ local bin_path_new server_config_new
+ if [ -n "$bin_path_raw" ]; then
+ bin_path_new="$(make_path_relative "$bin_path_raw")"
+ else
+ bin_path_new=""
+ fi
+ if [ -n "$server_config_raw" ]; then
+ server_config_new="$(make_path_relative "$server_config_raw")"
+ else
+ server_config_new=""
+ fi
+
+ local exec_command_raw
+ exec_command_raw=$(echo "$entry" | jq -r '.exec.command // empty')
+ local exec_args_array=()
+ local exec_json_new="null"
+ if [ -n "$exec_command_raw" ]; then
+ while IFS= read -r arg; do
+ exec_args_array+=("$arg")
+ done < <(echo "$entry" | jq -r '.exec.args[]?')
+ exec_json_new="$(serialize_exec_definition "$exec_command_raw" "${exec_args_array[@]}")"
+ fi
+
+ # Update entry (only override when non-empty; preserve nulls)
+ local updated
+ if [ "$exec_json_new" != "null" ]; then
+ updated=$(echo "$entry" | jq \
+ --arg bin "$bin_path_new" \
+ --arg srv "$server_config_new" \
+ --argjson exec "$exec_json_new" \
+ '
+ (.bin_path) |= (if $bin != "" then $bin else . end) |
+ (.server_config) |= (if $srv != "" then $srv else . end) |
+ (.exec) = $exec |
+ del(.real_bin_path)
+ ')
+ else
+ updated=$(echo "$entry" | jq \
+ --arg bin "$bin_path_new" \
+ --arg srv "$server_config_new" \
+ '
+ (.bin_path) |= (if $bin != "" then $bin else . end) |
+ (.server_config) |= (if $srv != "" then $srv else . end) |
+ del(.real_bin_path)
+ ')
+ fi
+
+ # Append to new array
+ if ! jq --argjson e "$updated" '. += [$e]' "$tmp_file" > "$tmp_file.new"; then
+ rm -f "$tmp_file" "$tmp_file.new"
+ echo -e "${RED}Error: Failed updating registry${NC}"
+ return 1
+ fi
+ mv "$tmp_file.new" "$tmp_file"
+ count=$((count+1))
+ done < <(jq -c '.[]?' "$REGISTRY_FILE")
+
+ mv "$tmp_file" "$REGISTRY_FILE"
+ echo -e "${GREEN}Normalized $count registry entrie(s) to relative paths${NC}"
+}
+
function attach_tmux_session() {
local service_name="$1"
local provider="$2"
@@ -1852,10 +2515,12 @@ function attach_screen_session() {
# Main execution
-check_dependencies
+# Only run the main logic if this script is executed directly (not sourced)
+if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
+ check_dependencies
-# Main command processing
-case "${1:-help}" in
+ # Main command processing
+ case "${1:-help}" in
create)
if [ $# -lt 3 ]; then
echo -e "${RED}Error: Not enough arguments for create command${NC}"
@@ -1884,7 +2549,26 @@ case "${1:-help}" in
list_services "${2:-}"
;;
restore)
- restore_missing_services
+ sync_only=false
+ if [ $# -gt 1 ]; then
+ for opt in "${@:2}"; do
+ case "$opt" in
+ --sync-only)
+ sync_only=true
+ ;;
+ *)
+ echo -e "${RED}Error: Unknown option for restore command: $opt${NC}"
+ print_help
+ exit 1
+ ;;
+ esac
+ done
+ fi
+ normalize_registry_paths
+ sync_service_configs_from_registry
+ if [ "$sync_only" = "false" ]; then
+ restore_missing_services
+ fi
;;
start|stop|restart|status)
if [ $# -lt 2 ]; then
@@ -1900,12 +2584,15 @@ case "${1:-help}" in
print_help
exit 1
fi
- if [ "$3" = "--follow" ]; then
+ if [ "${3:-}" = "--follow" ]; then
service_logs "$2" "true"
else
service_logs "$2" "false"
fi
;;
+ registry-normalize)
+ normalize_registry_paths
+ ;;
edit-config)
if [ $# -lt 2 ]; then
echo -e "${RED}Error: Service name required for edit-config command${NC}"
@@ -1977,3 +2664,5 @@ case "${1:-help}" in
exit 1
;;
esac
+
+fi # End of main execution block
diff --git a/apps/startup-scripts/src/simple-restarter b/apps/startup-scripts/src/simple-restarter
index a158b38bef..1865eaa87d 100755
--- a/apps/startup-scripts/src/simple-restarter
+++ b/apps/startup-scripts/src/simple-restarter
@@ -50,6 +50,8 @@ fi
# Main restart loop
while true; do
STARTING_TIME=$(date +%s)
+
+ echo "AC_CONFIG_POLICY: $AC_CONFIG_POLICY"
# Use starter script to launch the binary with all parameters
"$STARTER_SCRIPT" "$BINPATH" "$BINFILE" "$GDB_FILE" "$CONFIG" "$SYSLOG" "$SYSERR" "$GDB_ENABLED" "$CRASHES_PATH"
diff --git a/apps/startup-scripts/test/test_startup_scripts.bats b/apps/startup-scripts/test/test_startup_scripts.bats
index 119a8c80cc..c7a90c7f61 100644..100755
--- a/apps/startup-scripts/test/test_startup_scripts.bats
+++ b/apps/startup-scripts/test/test_startup_scripts.bats
@@ -160,7 +160,19 @@ teardown() {
# Create registry with pm2 provider service
cat > "$AC_SERVICE_CONFIG_DIR/service_registry.json" << 'EOF'
[
- {"name":"test-world","provider":"pm2","type":"service","bin_path":"/bin/worldserver","args":"","systemd_type":"--user","restart_policy":"always"}
+ {
+ "name":"test-world",
+ "provider":"pm2",
+ "type":"service",
+ "bin_path":"/bin/worldserver",
+ "args":"",
+ "systemd_type":"--user",
+ "restart_policy":"always",
+ "exec":{
+ "command":"/bin/true",
+ "args":[]
+ }
+ }
]
EOF
# Create minimal service config and run-engine config files required by 'send'
@@ -215,7 +227,19 @@ EOF
# Create registry and config as in previous test
cat > "$AC_SERVICE_CONFIG_DIR/service_registry.json" << 'EOF'
[
- {"name":"test-world","provider":"pm2","type":"service","bin_path":"/bin/worldserver","args":"","systemd_type":"--user","restart_policy":"always"}
+ {
+ "name":"test-world",
+ "provider":"pm2",
+ "type":"service",
+ "bin_path":"/bin/worldserver",
+ "args":"",
+ "systemd_type":"--user",
+ "restart_policy":"always",
+ "exec":{
+ "command":"/bin/true",
+ "args":[]
+ }
+ }
]
EOF
echo "RUN_ENGINE_CONFIG_FILE=\"$AC_SERVICE_CONFIG_DIR/test-world-run-engine.conf\"" > "$AC_SERVICE_CONFIG_DIR/test-world.conf"
@@ -258,6 +282,31 @@ EOF
[ "$status" -eq 0 ]
}
+@test "service-manager: restore helper recreates missing configs" {
+ command -v jq >/dev/null 2>&1 || skip "jq not installed"
+ export AC_SERVICE_CONFIG_DIR="$TEST_DIR/services"
+ mkdir -p "$AC_SERVICE_CONFIG_DIR"
+ source "$SCRIPT_DIR/service-manager.sh"
+
+ local service_name="restore-test"
+ local run_engine_config="$AC_SERVICE_CONFIG_DIR/$service_name-run-engine.conf"
+ local service_conf="$AC_SERVICE_CONFIG_DIR/$service_name.conf"
+ rm -f "$run_engine_config" "$service_conf"
+
+ mkdir -p "$TEST_DIR/bin" "$TEST_DIR/etc"
+ touch "$TEST_DIR/bin/worldserver"
+ touch "$TEST_DIR/etc/worldserver.conf"
+
+ ensure_service_configs_restored "$service_name" "world" "systemd" "$TEST_DIR/bin/worldserver" "$TEST_DIR/etc/worldserver.conf" "always" "none" "0" "--user" "" "$run_engine_config"
+
+ [ -f "$run_engine_config" ]
+ [ -f "$service_conf" ]
+ grep -Fq 'export SESSION_MANAGER="none"' "$run_engine_config"
+ grep -Fq 'export BINPATH="'$TEST_DIR'/bin"' "$run_engine_config"
+ grep -Fq "RUN_ENGINE_CONFIG_FILE=\"$run_engine_config\"" "$service_conf"
+ grep -Fq 'RESTART_POLICY="always"' "$service_conf"
+}
+
@test "service-manager: wait-uptime times out for unknown service" {
command -v jq >/dev/null 2>&1 || skip "jq not installed"
export AC_SERVICE_CONFIG_DIR="$TEST_DIR/services"
@@ -279,6 +328,136 @@ EOF
[[ "$output" =~ "Configuration file not found" ]]
}
+# ===== PATH PORTABILITY TESTS =====
+
+@test "service-manager: path conversion functions work correctly" {
+ # Source the service-manager script to access helper functions
+ source "$SCRIPT_DIR/service-manager.sh"
+
+ # Test make_path_relative without AC_SERVICE_CONFIG_DIR
+ unset AC_SERVICE_CONFIG_DIR
+ result=$(make_path_relative "/absolute/path/test")
+ [[ "$result" == "/absolute/path/test" ]]
+
+ # Test make_path_relative with AC_SERVICE_CONFIG_DIR
+ export AC_SERVICE_CONFIG_DIR="/tmp/test-config"
+ mkdir -p "$AC_SERVICE_CONFIG_DIR/subdir"
+
+ result=$(make_path_relative "$AC_SERVICE_CONFIG_DIR/subdir/binary")
+ [[ "$result" == "subdir/binary" ]]
+
+ result=$(make_path_relative "/opt/bin/authserver")
+ [[ "$result" == "../../opt/bin/authserver" ]]
+
+ # Test make_path_absolute
+ result=$(make_path_absolute "subdir/binary")
+ [[ "$result" == "$AC_SERVICE_CONFIG_DIR/subdir/binary" ]]
+
+ result=$(make_path_absolute "../../opt/bin/authserver")
+ [[ "$result" == "/opt/bin/authserver" ]]
+
+ # Test absolute path stays absolute
+ result=$(make_path_absolute "/absolute/path")
+ [[ "$result" == "/absolute/path" ]]
+
+ # Cleanup
+ rm -rf "$AC_SERVICE_CONFIG_DIR"
+ unset AC_SERVICE_CONFIG_DIR
+}
+
+@test "service-manager: registry stores relative paths when possible" {
+ # Set up test environment
+ export AC_SERVICE_CONFIG_DIR="$TEST_DIR/service-config"
+ mkdir -p "$AC_SERVICE_CONFIG_DIR"
+
+ # Create a temporary service registry in our test directory
+ local test_registry="$AC_SERVICE_CONFIG_DIR/test_registry.json"
+ echo "[]" > "$test_registry"
+
+ # Source the service-manager and override REGISTRY_FILE
+ source "$SCRIPT_DIR/service-manager.sh"
+ REGISTRY_FILE="$test_registry"
+
+ # Create test binary directory under config dir
+ mkdir -p "$AC_SERVICE_CONFIG_DIR/bin"
+
+ # Test that paths under AC_SERVICE_CONFIG_DIR are stored as relative
+ add_service_to_registry "test-service" "pm2" "auth" "$AC_SERVICE_CONFIG_DIR/bin/authserver" "--config test.conf" "" "always" "none" "0" "" "$AC_SERVICE_CONFIG_DIR/etc/test.conf"
+
+ # Check that paths were stored as relative
+ local stored_bin_path=$(jq -r '.[0].bin_path' "$test_registry")
+ local stored_config_path=$(jq -r '.[0].server_config' "$test_registry")
+
+ [[ "$stored_bin_path" == "bin/authserver" ]]
+ [[ "$stored_config_path" == "etc/test.conf" ]]
+
+ # Test that absolute paths outside config dir are stored as absolute
+ add_service_to_registry "test-service2" "pm2" "auth" "/opt/azerothcore/bin/authserver" "--config test.conf" "" "always" "none" "0" "" "/opt/azerothcore/etc/test.conf"
+
+ local stored_bin_path2=$(jq -r '.[1].bin_path' "$test_registry")
+ local stored_config_path2=$(jq -r '.[1].server_config' "$test_registry")
+
+ local expected_bin_rel=$(make_path_relative "/opt/azerothcore/bin/authserver")
+ local expected_cfg_rel=$(make_path_relative "/opt/azerothcore/etc/test.conf")
+
+ [[ "$stored_bin_path2" == "$expected_bin_rel" ]]
+ [[ "$stored_config_path2" == "$expected_cfg_rel" ]]
+
+ # Cleanup
+ rm -rf "$AC_SERVICE_CONFIG_DIR"
+ unset AC_SERVICE_CONFIG_DIR
+}
+
+@test "service-manager: restore --sync-only recreates config files" {
+ command -v jq >/dev/null 2>&1 || skip "jq not installed"
+ export AC_SERVICE_CONFIG_DIR="$TEST_DIR/services"
+ mkdir -p "$AC_SERVICE_CONFIG_DIR"
+
+ cat > "$AC_SERVICE_CONFIG_DIR/service_registry.json" <<'EOF'
+[
+ {
+ "name": "sync-test",
+ "provider": "pm2",
+ "type": "auth",
+ "bin_path": "bin/authserver",
+ "exec": {
+ "command": "../src/run-engine",
+ "args": [
+ "start",
+ "bin/authserver",
+ "--config",
+ "sync-test-run-engine.conf"
+ ]
+ },
+ "args": "",
+ "created": "2025-10-12T20:00:54+02:00",
+ "status": "active",
+ "systemd_type": "--user",
+ "restart_policy": "always",
+ "session_manager": "none",
+ "gdb_enabled": "0",
+ "pm2_opts": " ",
+ "server_config": "etc/authserver.conf"
+ }
+]
+EOF
+
+ rm -f "$AC_SERVICE_CONFIG_DIR/sync-test.conf" "$AC_SERVICE_CONFIG_DIR/sync-test-run-engine.conf"
+
+ mkdir -p "$AC_SERVICE_CONFIG_DIR/bin" "$AC_SERVICE_CONFIG_DIR/etc"
+ touch "$AC_SERVICE_CONFIG_DIR/bin/authserver"
+ touch "$AC_SERVICE_CONFIG_DIR/etc/authserver.conf"
+
+ run "$SCRIPT_DIR/service-manager.sh" restore --sync-only
+ debug_on_failure
+ [ "$status" -eq 0 ]
+
+ [ -f "$AC_SERVICE_CONFIG_DIR/sync-test.conf" ]
+ [ -f "$AC_SERVICE_CONFIG_DIR/sync-test-run-engine.conf" ]
+ grep -Fq "RUN_ENGINE_CONFIG_FILE=\"$AC_SERVICE_CONFIG_DIR/sync-test-run-engine.conf\"" "$AC_SERVICE_CONFIG_DIR/sync-test.conf"
+ grep -Fq "export BINPATH=\"$AC_SERVICE_CONFIG_DIR/bin\"" "$AC_SERVICE_CONFIG_DIR/sync-test-run-engine.conf"
+}
+
@test "examples: restarter-auth should show configuration error" {
run "$SCRIPT_DIR/examples/restarter-auth.sh"
[[ "$output" =~ "Configuration file not found" ]]