diff options
Diffstat (limited to 'apps/startup-scripts')
-rw-r--r-- | apps/startup-scripts/README.md | 95 | ||||
-rwxr-xr-x | apps/startup-scripts/src/migrate-registry.sh | 144 | ||||
-rwxr-xr-x | apps/startup-scripts/src/service-manager.sh | 407 |
3 files changed, 561 insertions, 85 deletions
diff --git a/apps/startup-scripts/README.md b/apps/startup-scripts/README.md index 7b057581d7..176ec3ed6c 100644 --- a/apps/startup-scripts/README.md +++ b/apps/startup-scripts/README.md @@ -312,6 +312,9 @@ Services support two restart policies: # Edit configuration ./service-manager.sh edit world + +# Restore missing services from registry +./service-manager.sh restore ``` ## 🌍 Multiple Realms Setup @@ -384,29 +387,85 @@ cp examples/restarter-world.sh restarter-realm2.sh ## 🛠️ Service Management -### PM2 Services +### Service Registry and Persistence -When using PM2 as the service provider: +The service manager includes a comprehensive registry system that tracks all created services and enables automatic restoration: + +#### Service Registry Features + +- **Automatic Tracking**: All services are automatically registered when created +- **Cross-Reboot Persistence**: PM2 services are configured with startup persistence +- **Service Restoration**: Missing services can be detected and restored from registry +- **Migration Support**: Legacy service configurations can be migrated to the new format + +#### Using the Registry + +```bash +# Check for missing services and restore them +./service-manager.sh restore + +# List all registered services (includes status) +./service-manager.sh list + +# Services are automatically added to registry on creation +./service-manager.sh create auth authserver --bin-path /path/to/bin +``` + +#### Custom Configuration Directories + +You can customize where service configurations and PM2/systemd files are stored: + +```bash +# Set custom directories +export AC_SERVICE_CONFIG_DIR="/path/to/your/project/services" + +# Now all service operations will use these custom directories +./service-manager.sh create auth authserver --bin-path /path/to/bin +``` + +This is particularly useful for: +- **Version Control**: Keep service configurations in your project repository +- **Multiple Projects**: Separate service configurations per project +- **Team Collaboration**: Share service setups across development teams + +#### Migration from Legacy Format + +If you have existing services in the old format, use the migration script: ```bash -# PM2-specific commands -pm2 list # List all PM2 processes -pm2 logs auth # View logs -pm2 monit # Real-time monitoring -pm2 restart auth # Restart service -pm2 delete auth # Remove service +# Migrate existing registry to new format +./migrate-registry.sh -# Save PM2 configuration -pm2 save -pm2 startup # Auto-start on boot +# The script will: +# - Detect old format automatically +# - Create a backup of the old registry +# - Convert to new format with proper tracking +# - Preserve all existing service information ``` +### PM2 Services + +When using PM2 as the service provider: + +* [PM2 CLI Documentation](https://pm2.io/docs/runtime/reference/pm2-cli/) + +**Automatic PM2 Persistence**: The service manager automatically configures PM2 for persistence across reboots by: +- Running `pm2 startup` to set up the startup script +- Running `pm2 save` after each service creation/modification +- This ensures your services automatically start when the system reboots + NOTE: pm2 cannot run tmux/screen sessions, but you can always use the `attach` command to connect to the service console because pm2 supports interactive mode. ### Environment Variables The startup scripts recognize several environment variables for configuration and runtime behavior: +#### Configuration Directory Variables + +- **`AC_SERVICE_CONFIG_DIR`**: Override the default configuration directory for services registry and configurations + - Default: `${XDG_CONFIG_HOME:-$HOME/.config}/azerothcore/services` + - Used for storing service registry and run-engine configurations + #### Service Detection Variables - **`AC_LAUNCHED_BY_PM2`**: Set to `1` when launched by PM2 (automatically set by service-manager) @@ -551,4 +610,18 @@ npm install -g pm2 sudo npm install -g pm2 ``` +#### 7. Registry Out of Sync +```bash +# If the service registry shows services that don't actually exist +``` +**Solution**: Use registry sync or restore +```bash +# Check and restore missing services (also cleans up orphaned entries) +./service-manager.sh restore + +# If you have a very old registry format, migrate it +./migrate-registry.sh +``` + + diff --git a/apps/startup-scripts/src/migrate-registry.sh b/apps/startup-scripts/src/migrate-registry.sh new file mode 100755 index 0000000000..c5898794f5 --- /dev/null +++ b/apps/startup-scripts/src/migrate-registry.sh @@ -0,0 +1,144 @@ +#!/usr/bin/env bash + +# One-time migration script for service registry +# Converts old format to new format + +set -euo pipefail # Strict error handling + +CONFIG_DIR="${AC_SERVICE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/azerothcore/services}" +REGISTRY_FILE="$CONFIG_DIR/service_registry.json" +BACKUP_FILE="$CONFIG_DIR/service_registry.json.backup" + +# Colors +readonly YELLOW='\033[1;33m' +readonly GREEN='\033[0;32m' +readonly RED='\033[0;31m' +readonly BLUE='\033[0;34m' +readonly NC='\033[0m' + +echo -e "${BLUE}AzerothCore Service Registry Migration Tool${NC}" +echo "==============================================" + +# Check dependencies +if ! command -v jq >/dev/null 2>&1; then + echo -e "${RED}Error: jq is required but not installed. Please install jq package.${NC}" + exit 1 +fi + +# Create config directory if it doesn't exist +mkdir -p "$CONFIG_DIR" + +# Check if registry exists +if [ ! -f "$REGISTRY_FILE" ]; then + echo -e "${YELLOW}No registry file found. Nothing to migrate.${NC}" + exit 0 +fi + +# Validate JSON format +if ! jq empty "$REGISTRY_FILE" >/dev/null 2>&1; then + echo -e "${RED}Error: Registry file contains invalid JSON.${NC}" + echo "Please check the file: $REGISTRY_FILE" + exit 1 +fi + +# Check if it's already new format +if jq -e 'type == "array" and (length == 0 or .[0] | has("bin_path"))' "$REGISTRY_FILE" >/dev/null 2>&1; then + echo -e "${GREEN}Registry is already in new format. No migration needed.${NC}" + exit 0 +fi + +# Check if it's old format +if ! jq -e 'type == "array" and (length == 0 or .[0] | has("config"))' "$REGISTRY_FILE" >/dev/null 2>&1; then + echo -e "${YELLOW}Registry format not recognized. Manual review needed.${NC}" + echo "Current registry content:" + cat "$REGISTRY_FILE" + exit 1 +fi + +echo -e "${YELLOW}Old format detected. Starting migration...${NC}" + +# Create backup +if ! cp "$REGISTRY_FILE" "$BACKUP_FILE"; then + echo -e "${RED}Error: Failed to create backup file.${NC}" + exit 1 +fi +echo -e "${BLUE}Backup created: $BACKUP_FILE${NC}" + +# Convert to new format +echo "[]" > "$REGISTRY_FILE.new" + +services_migrated=0 +while IFS= read -r service; do + if [ -n "$service" ] && [ "$service" != "null" ]; then + name=$(echo "$service" | jq -r '.name // ""') + provider=$(echo "$service" | jq -r '.provider // ""') + type=$(echo "$service" | jq -r '.type // ""') + config=$(echo "$service" | jq -r '.config // ""') + + # Validate required fields + if [ -z "$name" ] || [ -z "$provider" ] || [ -z "$type" ]; then + echo -e "${YELLOW}Skipping invalid service entry: $service${NC}" + continue + fi + + echo -e "${YELLOW}Migrating service: $name${NC}" + + # Create new format entry with all required fields + new_entry=$(jq -n \ + --arg name "$name" \ + --arg provider "$provider" \ + --arg type "$type" \ + --arg bin_path "unknown" \ + --arg args "" \ + --arg created "$(date -Iseconds)" \ + --arg status "migrated" \ + --arg systemd_type "--user" \ + --arg restart_policy "always" \ + --arg session_manager "none" \ + --arg gdb_enabled "0" \ + --arg pm2_opts "" \ + --arg server_config "" \ + --arg legacy_config "$config" \ + '{ + name: $name, + provider: $provider, + type: $type, + bin_path: $bin_path, + args: $args, + created: $created, + status: $status, + systemd_type: $systemd_type, + restart_policy: $restart_policy, + session_manager: $session_manager, + gdb_enabled: $gdb_enabled, + pm2_opts: $pm2_opts, + server_config: $server_config, + legacy_config: $legacy_config + }') + + # Add to new registry with error checking + if ! jq --argjson entry "$new_entry" '. += [$entry]' "$REGISTRY_FILE.new" > "$REGISTRY_FILE.new.tmp"; then + echo -e "${RED}Error: Failed to add service $name to new registry${NC}" + rm -f "$REGISTRY_FILE.new" "$REGISTRY_FILE.new.tmp" + exit 1 + fi + mv "$REGISTRY_FILE.new.tmp" "$REGISTRY_FILE.new" + + services_migrated=$((services_migrated + 1)) + fi +done < <(jq -c '.[]?' "$BACKUP_FILE" 2>/dev/null || echo "") + +# Replace old registry with new one +if ! mv "$REGISTRY_FILE.new" "$REGISTRY_FILE"; then + echo -e "${RED}Error: Failed to replace old registry with new one${NC}" + exit 1 +fi + +echo -e "${GREEN}Migration completed successfully!${NC}" +echo -e "${BLUE}Services migrated: $services_migrated${NC}" +echo -e "${BLUE}Use 'service-manager.sh restore' to review and update services.${NC}" +echo -e "${YELLOW}Note: Migrated services have bin_path='unknown' and need manual recreation.${NC}" +echo "" +echo -e "${BLUE}To recreate services, use commands like:${NC}" +echo " ./service-manager.sh create auth authserver --provider pm2 --bin-path /path/to/your/bin" +echo " ./service-manager.sh create world worldserver --provider systemd --bin-path /path/to/your/bin" diff --git a/apps/startup-scripts/src/service-manager.sh b/apps/startup-scripts/src/service-manager.sh index 980dfbb3a0..34dc4c4d5a 100755 --- a/apps/startup-scripts/src/service-manager.sh +++ b/apps/startup-scripts/src/service-manager.sh @@ -4,6 +4,8 @@ # A unified interface for managing AzerothCore services with PM2 or systemd # This script provides commands to create, update, delete, and manage server instances +set -euo pipefail # Strict error handling + # Script location CURRENT_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" @@ -11,16 +13,16 @@ SCRIPT_DIR="$CURRENT_PATH" ROOT_DIR="$(cd "$CURRENT_PATH/../../.." && pwd)" -# Configuration directory -CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/azerothcore/services" +# 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" # Colors for output -YELLOW='\033[1;33m' -GREEN='\033[0;32m' -RED='\033[0;31m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color +readonly YELLOW='\033[1;33m' +readonly GREEN='\033[0;32m' +readonly RED='\033[0;31m' +readonly BLUE='\033[0;34m' +readonly NC='\033[0m' # No Color # Create config directory if it doesn't exist mkdir -p "$CONFIG_DIR" @@ -38,6 +40,198 @@ check_dependencies() { } } +# Registry management functions +function add_service_to_registry() { + local service_name="$1" + local provider="$2" + local service_type="$3" + local bin_path="$4" + local args="$5" + local systemd_type="$6" + local restart_policy="$7" + local session_manager="$8" + local gdb_enabled="$9" + local pm2_opts="${10}" + local server_config="${11}" + + # Remove any existing entry with the same service name to avoid duplicates + local tmp_file + tmp_file=$(mktemp) + jq --arg name "$service_name" 'map(select(.name != $name))' "$REGISTRY_FILE" > "$tmp_file" && mv "$tmp_file" "$REGISTRY_FILE" + + # Add the new entry to the registry + tmp_file=$(mktemp) + jq --arg name "$service_name" \ + --arg provider "$provider" \ + --arg type "$service_type" \ + --arg bin_path "$bin_path" \ + --arg args "$args" \ + --arg created "$(date -Iseconds)" \ + --arg systemd_type "$systemd_type" \ + --arg restart_policy "$restart_policy" \ + --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}]' \ + "$REGISTRY_FILE" > "$tmp_file" && mv "$tmp_file" "$REGISTRY_FILE" + + echo -e "${GREEN}Service '$service_name' added to registry${NC}" +} + +function remove_service_from_registry() { + local service_name="$1" + + if [ -f "$REGISTRY_FILE" ]; then + local tmp_file + tmp_file=$(mktemp) + jq --arg name "$service_name" \ + 'map(select(.name != $name))' \ + "$REGISTRY_FILE" > "$tmp_file" && mv "$tmp_file" "$REGISTRY_FILE" + echo -e "${GREEN}Service '$service_name' removed from registry${NC}" + fi +} + +function restore_missing_services() { + echo -e "${BLUE}Checking for missing services...${NC}" + + if [ ! -f "$REGISTRY_FILE" ] || [ ! -s "$REGISTRY_FILE" ]; then + echo -e "${YELLOW}No services registry found or empty${NC}" + return 0 + fi + + local missing_services=() + 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 + + echo -e "${BLUE}Found $services_count registered services. Checking status...${NC}" + + # Check each service + for i in $(seq 0 $((services_count-1))); do + local service=$(jq -r ".[$i]" "$REGISTRY_FILE") + 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 args=$(echo "$service" | jq -r '.args // ""') + local status=$(echo "$service" | jq -r '.status // "active"') + 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 service_exists=false + + if [ "$provider" = "pm2" ]; then + if pm2 describe "$name" >/dev/null 2>&1; then + service_exists=true + fi + elif [ "$provider" = "systemd" ]; then + local user_unit="${XDG_CONFIG_HOME:-$HOME/.config}/systemd/user/$name.service" + local system_unit="/etc/systemd/system/$name.service" + if [ -f "$user_unit" ] || [ -f "$system_unit" ]; then + # Unit file present, you can also check if it is active + service_exists=true + else + # Unit file missing: service needs to be recreated! + service_exists=false + fi + fi + + if [ "$service_exists" = false ]; then + missing_services+=("$i") + echo -e "${YELLOW}Missing service: $name ($provider)${NC}" + else + echo -e "${GREEN}✓ Service $name ($provider) exists${NC}" + fi + done + + # Handle missing services + if [ ${#missing_services[@]} -eq 0 ]; then + echo -e "${GREEN}All registered services are present${NC}" + return 0 + fi + + echo -e "${YELLOW}Found ${#missing_services[@]} missing services${NC}" + + for index in "${missing_services[@]}"; do + local service=$(jq -r ".[$index]" "$REGISTRY_FILE") + 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 // ""') + + echo "" + echo -e "${YELLOW}Service '$name' ($provider) is missing${NC}" + echo " Type: $service_type" + echo " Status: $status" + + if [ "$bin_path" = "unknown" ] || [ "$bin_path" = "null" ] || [ "$status" = "migrated" ]; then + echo " Binary: <needs manual configuration>" + echo " Args: <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" + fi + echo "" + + read -p "Do you want to (r)ecreate, (d)elete from registry, or (s)kip? [r/d/s]: " choice + + case "$choice" in + r|R|recreate) + if [ "$bin_path" = "unknown" ] || [ "$status" = "migrated" ]; then + echo -e "${YELLOW}Please recreate manually with full create command${NC}" + read -p "Remove this entry from registry? [y/n]: " remove_entry + if [[ "$remove_entry" =~ ^[Yy]$ ]]; then + remove_service_from_registry "$name" + fi + else + echo -e "${BLUE}Recreating service '$name'...${NC}" + if [ "$provider" = "pm2" ]; then + if [ "$args" != "null" ] && [ -n "$args" ]; then + pm2_create_service "$name" "$bin_path $args" "$restart_policy" $pm2_opts + else + pm2_create_service "$name" "$bin_path" "$restart_policy" $pm2_opts + 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}" + 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 + ;; + d|D|delete) + echo -e "${BLUE}Removing '$name' from registry...${NC}" + remove_service_from_registry "$name" + ;; + s|S|skip|*) + echo -e "${BLUE}Skipping '$name'${NC}" + ;; + esac + done +} + # Check if PM2 is installed check_pm2() { if ! command -v pm2 >/dev/null 2>&1; then @@ -81,6 +275,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 start|stop|restart|status <service-name>" echo " $base_name logs <service-name> [--follow]" echo " $base_name attach <service-name>" @@ -139,6 +334,9 @@ function print_help() { echo " $base_name attach worldserver-realm1" echo " $base_name list pm2" echo "" + echo " # Restore missing services from registry" + echo " $base_name restore" + echo "" echo "Notes:" echo " - Configuration editing modifies run-engine settings (GDB, session manager, etc.)" echo " - Use --server-config for the actual server configuration file" @@ -150,26 +348,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 "" + echo "Environment Variables:" + echo " AC_SERVICE_CONFIG_DIR - Override default config directory for services registry" } -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" @@ -210,47 +395,42 @@ function validate_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) + if [ ! -f "$REGISTRY_FILE" ] || [ ! -s "$REGISTRY_FILE" ]; then + echo -e "${YELLOW}No services registry found or empty${NC}" + return 0 + fi - # Initialize with empty array + local services_count=$(jq length "$REGISTRY_FILE") + if [ "$services_count" -eq 0 ]; then + echo -e "${YELLOW}No services registered${NC}" + return 0 + fi + + local tmp_file=$(mktemp) 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 + for i in $(seq 0 $((services_count-1))); do + local service=$(jq -r ".[$i]" "$REGISTRY_FILE") + local name=$(echo "$service" | jq -r '.name') + local provider=$(echo "$service" | jq -r '.provider') + + if validate_service_exists "$name" "$provider"; then + # Service exists, add it to the new registry + jq --argjson service "$service" '. += [$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 - done <<< "$services" + done # 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" @@ -317,6 +497,15 @@ function pm2_create_service() { if eval "$pm2_cmd"; then echo -e "${GREEN}PM2 service '$service_name' created successfully${NC}" pm2 save + + # Setup PM2 startup for persistence across reboots + 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" "" + return 0 else echo -e "${RED}Failed to create PM2 service '$service_name'${NC}" @@ -334,8 +523,8 @@ function pm2_remove_service() { # Stop the service if it's running if pm2 describe "$service_name" >/dev/null 2>&1; then - pm2 stop "$service_name" 2>/dev/null || true - pm2 delete "$service_name" 2>/dev/null + pm2 stop "$service_name" 2>&1 || true + pm2 delete "$service_name" 2>&1 || true # Wait for PM2 to process the stop/delete command with timeout local timeout=10 @@ -357,8 +546,13 @@ 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" 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" fi return 0 @@ -391,6 +585,7 @@ function pm2_service_logs() { # Systemd service management functions function get_systemd_dir() { local type="$1" + if [ "$type" = "--system" ]; then echo "/etc/systemd/system" else @@ -403,17 +598,32 @@ function systemd_create_service() { local command="$2" local restart_policy="$3" local systemd_type="--user" + local bin_path="" + local gdb_enabled="0" + local server_config="" shift 3 check_systemd || return 1 - # Parse systemd type + # Parse systemd type and extract additional parameters while [[ $# -gt 0 ]]; do case "$1" in --system|--user) systemd_type="$1" shift ;; + --bin-path) + bin_path="$2" + shift 2 + ;; + --gdb-enabled) + gdb_enabled="$2" + shift 2 + ;; + --server-config) + server_config="$2" + shift 2 + ;; *) command+=" $1" shift @@ -421,6 +631,18 @@ function systemd_create_service() { esac done + # If bin_path is not provided, try to extract from command + if [ -z "$bin_path" ]; then + # Try to extract bin path from run-engine command + if [[ "$command" =~ run-engine[[:space:]]+start[[:space:]]+([^[:space:]]+) ]]; then + local binary_path="${BASH_REMATCH[1]}" + bin_path="$(dirname "$binary_path")" + else + # Fallback to current directory + bin_path="$(pwd)" + fi + fi + local systemd_dir=$(get_systemd_dir "$systemd_type") local service_file="$systemd_dir/$service_name.service" @@ -457,6 +679,11 @@ function systemd_create_service() { # Create service file echo -e "${YELLOW}Creating systemd service: $service_name${NC}" + # Ensure bin_path is absolute + if [[ ! "$bin_path" = /* ]]; then + bin_path="$(realpath "$bin_path")" + fi + if [ "$systemd_type" = "--system" ]; then # System service template (with User directive) cat > "$service_file" << EOF @@ -471,7 +698,7 @@ Restart=$restart_policy RestartSec=3 User=$(whoami) Group=$(id -gn) -WorkingDirectory=$(realpath "$bin_path") +WorkingDirectory=$bin_path StandardOutput=journal+console StandardError=journal+console @@ -490,7 +717,7 @@ Type=${service_type} ExecStart=$command Restart=$restart_policy RestartSec=3 -WorkingDirectory=$(realpath "$bin_path") +WorkingDirectory=$bin_path StandardOutput=journal+console StandardError=journal+console @@ -498,10 +725,6 @@ StandardError=journal+console 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 @@ -513,6 +736,10 @@ EOF fi echo -e "${GREEN}Systemd service '$service_name' created successfully${NC}" + + # Add to registry + add_service_to_registry "$service_name" "systemd" "service" "$command" "" "$systemd_type" "$restart_policy" "$session_manager" "$gdb_enabled" "" "$server_config" + return 0 } @@ -572,6 +799,10 @@ function systemd_remove_service() { if [ "$removal_failed" = "true" ]; then echo -e "${YELLOW}Note: Service may still be running but configuration was removed${NC}" fi + + # Remove from registry + remove_service_from_registry "$service_name" + return 0 else echo -e "${RED}Failed to remove systemd service file '$service_file'${NC}" @@ -659,7 +890,7 @@ function create_service() { # Default values for run-engine configuration local provider="auto" - local bin_path="$BINPATH/bin" # get from config or environment + local bin_path="${BINPATH:-$ROOT_DIR/bin}" # get from config or environment local server_config="" local session_manager="none" local gdb_enabled="0" @@ -839,8 +1070,6 @@ EOF # 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}" @@ -880,14 +1109,20 @@ function update_service() { # 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') + local config_file="$CONFIG_DIR/$service_name.conf" # Load current configuration + if [ ! -f "$config_file" ]; then + echo -e "${RED}Error: Service configuration file not found: $config_file${NC}" + return 1 + fi source "$config_file" # Load current run-engine configuration if [ -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}" fi # Parse options to update @@ -1020,11 +1255,13 @@ function delete_service() { # Extract provider and config local provider=$(echo "$service_info" | jq -r '.provider') - local config_file=$(echo "$service_info" | jq -r '.config') + local config_file="$CONFIG_DIR/$service_name.conf" # Load configuration to get run-engine config file if [ -f "$config_file" ]; then source "$config_file" + else + echo -e "${YELLOW}Warning: Service configuration file not found: $config_file${NC}" fi echo -e "${YELLOW}Deleting service '$service_name' (provider: $provider)...${NC}" @@ -1048,8 +1285,9 @@ function delete_service() { echo -e "${GREEN}Removed run-engine config: $RUN_ENGINE_CONFIG_FILE${NC}" fi - # Unregister service - unregister_service "$service_name" + # Remove configuration file + rm -f "$config_file" + echo -e "${GREEN}Service '$service_name' deleted successfully${NC}" else echo -e "${RED}Failed to remove service '$service_name' from $provider${NC}" @@ -1166,7 +1404,7 @@ function edit_config() { fi # Get configuration file path - local config_file=$(echo "$service_info" | jq -r '.config') + local config_file="$CONFIG_DIR/$service_name.conf" # Load configuration to get run-engine config file source "$config_file" @@ -1191,7 +1429,7 @@ function attach_to_service() { # Extract provider local provider=$(echo "$service_info" | jq -r '.provider') - local config_file=$(echo "$service_info" | jq -r '.config') + local config_file="$CONFIG_DIR/$service_name.conf" # Load configuration to get run-engine config file if [ ! -f "$config_file" ]; then @@ -1206,6 +1444,11 @@ function attach_to_service() { echo -e "${RED}Error: Run-engine configuration file not found: $RUN_ENGINE_CONFIG_FILE${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 source "$RUN_ENGINE_CONFIG_FILE" @@ -1264,9 +1507,22 @@ function attach_interactive_shell() { # For systemd without session manager, show helpful message local service_info=$(get_service_info "$service_name") - local config_file=$(echo "$service_info" | jq -r '.config') + local config_file="$CONFIG_DIR/$service_name.conf" + + # Check if config file exists before sourcing + if [ ! -f "$config_file" ]; then + echo -e "${RED}Error: Service configuration file not found: $config_file${NC}" + return 1 + fi 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}" + return 1 + fi + source "$RUN_ENGINE_CONFIG_FILE" echo -e "${RED}Error: Cannot attach to systemd service '$service_name'${NC}" @@ -1375,6 +1631,9 @@ case "${1:-help}" in list) list_services "$2" ;; + restore) + restore_missing_services + ;; start|stop|restart|status) if [ $# -lt 2 ]; then echo -e "${RED}Error: Service name required for $1 command${NC}" |