diff --git a/README.md b/README.md index 7d17269..0e3017d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# **opensimMULTITOOL II V25.5** +# **opensimMULTITOOL II V26.4** Ein Bash-Skript zum Verwalten von OpenSim-Grids (Starten, Stoppen, Bereinigen, Installation). @@ -30,6 +30,100 @@ Ein Bash-Skript zum Verwalten von OpenSim-Grids (Starten, Stoppen, Bereinigen, I --- +## **Neu: Modulare Struktur (osmtool_backup_*)** + +Fuer die Aufteilung in einzelne Aufgaben gibt es jetzt eine modulare Basis: + +- `osmtool_main.sh` (zentraler Starter, CLI + UI) +- `modular/osmtool_install.sh` (Installation) +- `modular/osmtool_startstop.sh` (Start/Stop/Restart) +- `modular/osmtool_cleanup.sh` (Bereinigungen + Reboot) +- `modular/osmtool_data.sh` (DB/OAR Backups) +- `modular/osmtool_core.sh` (gemeinsame Funktionen) + +### Empfehlung UI: whiptail oder dialog? + +- Empfehlung: **whiptail als Default**, weil es auf Ubuntu meist schon vorhanden ist und weniger Abhaengigkeiten hat. +- Fallback: **dialog**, falls whiptail nicht installiert ist. +- Beide Wege sind im Starter beruecksichtigt: `whiptail -> dialog -> CLI`. + +### Beispiele + +```bash +# CLI: zentrale Server-Vorbereitung als Pflichtschritt +bash osmtool_main.sh --module install --action bootstrap-server --workdir /opt --profile grid-sim + +# CLI: Ubuntu vorbereiten +bash osmtool_main.sh --module install --action prepare-ubuntu + +# CLI: OpenSim klonen, Zusatzmodule einbinden und bauen +bash osmtool_main.sh --module install --action install-opensim --workdir /opt --profile grid-sim + +# CLI: OpenSim Runtime-INI aus Beispieldateien erzeugen +bash osmtool_main.sh --module install --action configure-opensim --workdir /opt --profile grid-sim + +# CLI: MariaDB Datenbanken/Benutzer/Rechte fuer robust/sim* anlegen +bash osmtool_main.sh --module install --action configure-database --workdir /opt --profile grid-sim --db-user opensim --db-pass 'OpenSim#2026' + +# CLI: OpenSim bauen ohne Verteilung in Laufzeitordner +bash osmtool_main.sh --module install --action install-opensim --workdir /opt --profile grid-sim --deploy-binaries false + +# CLI: komplettes Grid neustarten +bash osmtool_main.sh --module startstop --target grid --action restart --workdir /opt + +# CLI: Janus Gateway neustarten +bash osmtool_main.sh --module startstop --target janus --action restart --workdir /opt + +# CLI: Janus zuerst kompilieren +bash osmtool_main.sh --module install --action compile-janus --workdir /opt --profile grid-sim + +# CLI: Janus danach konfigurieren +bash osmtool_main.sh --module install --action configure-janus --workdir /opt --profile grid-sim --public-host 127.0.0.1 + +# CLI: Janus kompiliert + konfiguriert in einem Lauf +bash osmtool_main.sh --module install --action install-janus --workdir /opt --profile grid-sim --public-host 127.0.0.1 + +# CLI: Robust DB sichern +bash osmtool_main.sh --module backup --action db-backup --db-user root --db-pass secret --db-name opensim --workdir /opt + +# CLI: OAR Export in laufende sim1 Session senden +bash osmtool_main.sh --module backup --action oar-backup --workdir /opt --region sim1 --session sim1 + +# CLI: Full Backup (MariaDB + OAR Command) mit gzip-Kompression +bash osmtool_main.sh --module backup --action full-backup --workdir /opt --db-user root --db-pass secret --db-name opensim --region sim1 --session sim1 --compress true + +# CLI: OSMTool Cronjobs installieren +bash osmtool_main.sh --module cron --action install --workdir /opt --db-user root --db-name opensim --region sim1 --session sim1 + +# CLI: OSMTool Cronjobs anzeigen/entfernen +bash osmtool_main.sh --module cron --action list --workdir /opt +bash osmtool_main.sh --module cron --action remove --workdir /opt + +# UI Modus +bash osmtool_main.sh --mode ui +``` + +Hinweis Janus: + +- `bootstrap-server` ist Pflicht vor `install-opensim`, `compile-janus`, `configure-janus` und `install-janus`. +- `configure-opensim` erzeugt fehlende Runtime-INI aus den Beispielvorlagen. +- `configure-database` erzeugt MariaDB Datenbanken fuer `robust` und gefundene `sim*` Verzeichnisse sowie Benutzer/Grants. +- Datenbankbasis ist MariaDB; `server-check` prueft `mysqldump` als Pflichttool fuer Backups. +- `install-opensim` klont oder aktualisiert `opensim`, `opensim-tsassets`, `opensimcurrencyserver-dotnet` und `os-data-backup`, fuehrt `runprebuild.sh` aus und baut `OpenSim.sln`. +- Wenn `robust/bin`, `standalone/bin` oder `sim*/bin` existieren, verteilt `install-opensim` die gebauten Binaries dorthin, sofern `--deploy-binaries` nicht auf `false` gesetzt ist. +- Janus muss vor Start/Restart zuerst kompiliert und konfiguriert sein. +- `startstop --target janus` prueft Binary und Konfiguration und blockiert sonst mit klarer Fehlermeldung. + +### Sinnvolle weitere Skripte + +- `osmtool_backup_health.sh` (Healthcheck fuer screen sessions, ports, DB) +- `osmtool_backup_restore.sh` (SQL Restore + OAR Restore mit Dry-Run) +- `osmtool_backup_update.sh` (OpenSim/Janus Update-Workflow mit Rollback) +- `osmtool_backup_cron.sh` (Cronjobs validieren und automatisch setzen) +- `osmtool_backup_config.sh` (INI/JSON Validierung und Patchen) + +--- + ## **📋 Inhaltsverzeichnis** 1. [OpenSim starten/stoppen](#-opensim-starten-stoppen-und-neustarten) diff --git a/lang/de.lang b/lang/de.lang new file mode 100644 index 0000000..3dc0603 --- /dev/null +++ b/lang/de.lang @@ -0,0 +1,60 @@ +APP_TITLE=OSMTool Backup +MENU_CHOOSE_MODULE=Modul waehlen +MENU_INSTALL=Installation +MENU_STARTSTOP=Start/Stop/Neustart +MENU_CLEANUP=Bereinigung und Reboot +MENU_HEALTH=Healthcheck +MENU_BACKUP=Sicherung DB/OAR +MENU_RESTORE=Wiederherstellung DB/OAR +MENU_UPDATE=Aktualisierung mit Ruecksetzpunkt +MENU_CONFIG=Konfigurationspruefung +MENU_REPORT=Tagesreport +MENU_SMOKE=Schnelltest +MENU_CRON=Cronjobs +MENU_CHOOSE_ACTION=Aktion waehlen +MENU_CHOOSE_TARGET=Ziel waehlen +MENU_CHOOSE_PROFILE=Profil waehlen +ACTION_START=Starten +ACTION_STOP=Stoppen +ACTION_RESTART=Neustarten +ACTION_CACHE_CLEAN=Cache-Ordner loeschen +ACTION_LOG_CLEAN=Logs loeschen +ACTION_TMP_CLEAN=Temp-Dateien loeschen +ACTION_REBOOT=Server neu starten +ACTION_HEALTH_RUN=System-Healthcheck ausfuehren +ACTION_DB_BACKUP=Robust Datenbank sichern +ACTION_OAR_BACKUP=Region als OAR sichern +ACTION_FULL_BACKUP=Datenbank und optional OAR sichern +ACTION_RESTORE_LIST=Verfuegbare Backups anzeigen +ACTION_RESTORE_DB=Datenbank wiederherstellen +ACTION_RESTORE_OAR=Region aus OAR wiederherstellen +ACTION_RESTORE_FULL=Datenbank und optional OAR wiederherstellen +ACTION_UPDATE_ROLLBACK=Ruecksetzpunkt erstellen +ACTION_UPDATE_LIST=Ruecksetzpunkte anzeigen +ACTION_UPDATE_OPENSIM=OpenSim aktualisieren +ACTION_UPDATE_JANUS=Janus aktualisieren +ACTION_UPDATE_FULL=OpenSim und Janus aktualisieren +ACTION_CONFIG_FILE=Einzelne Config-Datei pruefen +ACTION_CONFIG_RUNTIME=Runtime-Configs pruefen +ACTION_REPORT_GENERATE=Tagesreport erzeugen +ACTION_SMOKE_RUN=Start/Neustart/Stop Schnelltest ausfuehren +ACTION_CRON_INSTALL=Cron-Jobs installieren/aktualisieren +ACTION_CRON_LIST=Cron-Jobs anzeigen +ACTION_CRON_REMOVE=Cron-Jobs entfernen +TARGET_GRID=Komplettes Grid +TARGET_ROBUST=Nur Robust +TARGET_SIM1=Nur Primaerregion +TARGET_STANDALONE=Standalone Instanz +TARGET_JANUS=Janus Gateway Server +PROFILE_GRID_SIM=Grid Simulatoren +PROFILE_ROBUST=Robust Dienst +PROFILE_STANDALONE=Standalone Betrieb +ERR_UNKNOWN_MODULE=Unbekanntes Modul +ERR_UNKNOWN_UI_MODULE=Unbekanntes UI-Modul +ERR_MISSING_MODULE=Fehlendes --module im CLI Modus +ERR_INVALID_PROFILE=Ungueltiges Profil +ERR_ACTION_NOT_ALLOWED=Aktion fuer dieses Profil nicht erlaubt +ERR_TARGET_NOT_ALLOWED=Ziel fuer dieses Profil nicht erlaubt +ERR_NO_UI_BACKEND=Kein UI Backend gefunden. Installiere whiptail oder dialog oder nutze CLI Parameter. +HELP_LANG=Sprache (de|en|fr|es), Standard: de +HELP_PROFILE=Profil (grid-sim|robust|standalone), Standard: grid-sim diff --git a/lang/en.lang b/lang/en.lang new file mode 100644 index 0000000..5ae6d17 --- /dev/null +++ b/lang/en.lang @@ -0,0 +1,60 @@ +APP_TITLE=OSMTool Backup +MENU_CHOOSE_MODULE=Choose module +MENU_INSTALL=Installation +MENU_STARTSTOP=Start/stop/restart +MENU_CLEANUP=Cleanup and reboot +MENU_HEALTH=Health check +MENU_BACKUP=Backup DB/OAR +MENU_RESTORE=Restore DB/OAR +MENU_UPDATE=Update with rollback +MENU_CONFIG=Config validation +MENU_REPORT=Daily report +MENU_SMOKE=Smoke test +MENU_CRON=Cron jobs +MENU_CHOOSE_ACTION=Choose action +MENU_CHOOSE_TARGET=Choose target +MENU_CHOOSE_PROFILE=Choose profile +ACTION_START=Start +ACTION_STOP=Stop +ACTION_RESTART=Restart +ACTION_CACHE_CLEAN=Delete cache folders +ACTION_LOG_CLEAN=Delete logs +ACTION_TMP_CLEAN=Delete temp files +ACTION_REBOOT=Reboot server +ACTION_HEALTH_RUN=Run system health check +ACTION_DB_BACKUP=Backup robust database +ACTION_OAR_BACKUP=Backup region as OAR +ACTION_FULL_BACKUP=Backup database and optional OAR +ACTION_RESTORE_LIST=List available backups +ACTION_RESTORE_DB=Restore database +ACTION_RESTORE_OAR=Restore region from OAR +ACTION_RESTORE_FULL=Restore database and optional OAR +ACTION_UPDATE_ROLLBACK=Create rollback point +ACTION_UPDATE_LIST=List rollback points +ACTION_UPDATE_OPENSIM=Update OpenSim +ACTION_UPDATE_JANUS=Update Janus +ACTION_UPDATE_FULL=Update OpenSim and Janus +ACTION_CONFIG_FILE=Validate single config file +ACTION_CONFIG_RUNTIME=Validate runtime configs +ACTION_REPORT_GENERATE=Generate daily report +ACTION_SMOKE_RUN=Run start/restart/stop smoke test +ACTION_CRON_INSTALL=Install/update cron jobs +ACTION_CRON_LIST=List cron jobs +ACTION_CRON_REMOVE=Remove cron jobs +TARGET_GRID=Whole grid +TARGET_ROBUST=Robust only +TARGET_SIM1=Primary region only +TARGET_STANDALONE=Standalone instance +TARGET_JANUS=Janus gateway server +PROFILE_GRID_SIM=Grid simulators +PROFILE_ROBUST=Robust service +PROFILE_STANDALONE=Standalone mode +ERR_UNKNOWN_MODULE=Unknown module +ERR_UNKNOWN_UI_MODULE=Unknown UI module +ERR_MISSING_MODULE=Missing --module in CLI mode +ERR_INVALID_PROFILE=Invalid profile +ERR_ACTION_NOT_ALLOWED=Action not allowed for this profile +ERR_TARGET_NOT_ALLOWED=Target not allowed for this profile +ERR_NO_UI_BACKEND=No UI backend found. Install whiptail or dialog, or use CLI parameters. +HELP_LANG=Language (de|en|fr|es), default: de +HELP_PROFILE=Profile (grid-sim|robust|standalone), default: grid-sim diff --git a/lang/es.lang b/lang/es.lang new file mode 100644 index 0000000..4159607 --- /dev/null +++ b/lang/es.lang @@ -0,0 +1,60 @@ +APP_TITLE=OSMTool Backup +MENU_CHOOSE_MODULE=Elegir modulo +MENU_INSTALL=Instalacion +MENU_STARTSTOP=Iniciar/detener/reiniciar +MENU_CLEANUP=Limpieza y reinicio +MENU_HEALTH=Comprobacion de salud +MENU_BACKUP=Copia BD/OAR +MENU_RESTORE=Restauracion BD/OAR +MENU_UPDATE=Actualizacion con punto de restauracion +MENU_CONFIG=Validacion de configuracion +MENU_REPORT=Reporte diario +MENU_SMOKE=Prueba rapida +MENU_CRON=Tareas cron +MENU_CHOOSE_ACTION=Elegir accion +MENU_CHOOSE_TARGET=Elegir destino +MENU_CHOOSE_PROFILE=Elegir perfil +ACTION_START=Iniciar +ACTION_STOP=Detener +ACTION_RESTART=Reiniciar +ACTION_CACHE_CLEAN=Eliminar carpetas de cache +ACTION_LOG_CLEAN=Eliminar logs +ACTION_TMP_CLEAN=Eliminar archivos temporales +ACTION_REBOOT=Reiniciar servidor +ACTION_HEALTH_RUN=Ejecutar comprobacion de salud +ACTION_DB_BACKUP=Copia de base Robust +ACTION_OAR_BACKUP=Copia de region en OAR +ACTION_FULL_BACKUP=Copia de base y OAR opcional +ACTION_RESTORE_LIST=Listar copias disponibles +ACTION_RESTORE_DB=Restaurar base de datos +ACTION_RESTORE_OAR=Restaurar region desde OAR +ACTION_RESTORE_FULL=Restaurar base y OAR opcional +ACTION_UPDATE_ROLLBACK=Crear punto de restauracion +ACTION_UPDATE_LIST=Listar puntos de rollback +ACTION_UPDATE_OPENSIM=Actualizar OpenSim +ACTION_UPDATE_JANUS=Actualizar Janus +ACTION_UPDATE_FULL=Actualizar OpenSim y Janus +ACTION_CONFIG_FILE=Validar un archivo de configuracion +ACTION_CONFIG_RUNTIME=Validar configuraciones runtime +ACTION_REPORT_GENERATE=Generar reporte diario +ACTION_SMOKE_RUN=Ejecutar prueba rapida iniciar/reiniciar/detener +ACTION_CRON_INSTALL=Instalar/actualizar tareas cron +ACTION_CRON_LIST=Listar tareas cron +ACTION_CRON_REMOVE=Eliminar tareas cron +TARGET_GRID=Grid completo +TARGET_ROBUST=Solo Robust +TARGET_SIM1=Solo region primaria +TARGET_STANDALONE=Instancia standalone +TARGET_JANUS=Servidor pasarela Janus +PROFILE_GRID_SIM=Simuladores de grid +PROFILE_ROBUST=Servicio Robust +PROFILE_STANDALONE=Modo standalone +ERR_UNKNOWN_MODULE=Modulo desconocido +ERR_UNKNOWN_UI_MODULE=Modulo UI desconocido +ERR_MISSING_MODULE=Falta --module en modo CLI +ERR_INVALID_PROFILE=Perfil invalido +ERR_ACTION_NOT_ALLOWED=Accion no permitida para este perfil +ERR_TARGET_NOT_ALLOWED=Destino no permitido para este perfil +ERR_NO_UI_BACKEND=No se encontro backend UI. Instala whiptail o dialog, o usa parametros CLI. +HELP_LANG=Idioma (de|en|fr|es), por defecto: de +HELP_PROFILE=Perfil (grid-sim|robust|standalone), por defecto: grid-sim diff --git a/lang/fr.lang b/lang/fr.lang new file mode 100644 index 0000000..5d672dd --- /dev/null +++ b/lang/fr.lang @@ -0,0 +1,60 @@ +APP_TITLE=OSMTool Backup +MENU_CHOOSE_MODULE=Choisir un module +MENU_INSTALL=Installation +MENU_STARTSTOP=Demarrer/arreter/redemarrer +MENU_CLEANUP=Nettoyage et redemarrage +MENU_HEALTH=Verification de sante +MENU_BACKUP=Sauvegarde BD/OAR +MENU_RESTORE=Restauration BD/OAR +MENU_UPDATE=Mise a jour avec retour arriere +MENU_CONFIG=Validation de configuration +MENU_REPORT=Rapport quotidien +MENU_SMOKE=Test de fumee +MENU_CRON=Taches cron +MENU_CHOOSE_ACTION=Choisir une action +MENU_CHOOSE_TARGET=Choisir une cible +MENU_CHOOSE_PROFILE=Choisir un profil +ACTION_START=Demarrer +ACTION_STOP=Arreter +ACTION_RESTART=Redemarrer +ACTION_CACHE_CLEAN=Supprimer les dossiers de cache +ACTION_LOG_CLEAN=Supprimer les journaux +ACTION_TMP_CLEAN=Supprimer les fichiers temporaires +ACTION_REBOOT=Redemarrer le serveur +ACTION_HEALTH_RUN=Executer la verification de sante +ACTION_DB_BACKUP=Sauvegarder la base Robust +ACTION_OAR_BACKUP=Sauvegarder la region en OAR +ACTION_FULL_BACKUP=Sauvegarder la base et OAR optionnel +ACTION_RESTORE_LIST=Afficher les sauvegardes disponibles +ACTION_RESTORE_DB=Restaurer la base de donnees +ACTION_RESTORE_OAR=Restaurer la region depuis OAR +ACTION_RESTORE_FULL=Restaurer la base et OAR optionnel +ACTION_UPDATE_ROLLBACK=Creer un point de retour arriere +ACTION_UPDATE_LIST=Afficher les points de rollback +ACTION_UPDATE_OPENSIM=Mettre a jour OpenSim +ACTION_UPDATE_JANUS=Mettre a jour Janus +ACTION_UPDATE_FULL=Mettre a jour OpenSim et Janus +ACTION_CONFIG_FILE=Valider un fichier de configuration +ACTION_CONFIG_RUNTIME=Valider les configurations runtime +ACTION_REPORT_GENERATE=Generer le rapport quotidien +ACTION_SMOKE_RUN=Executer le test de fumee demarrer/redemarrer/arreter +ACTION_CRON_INSTALL=Installer/mettre a jour les taches cron +ACTION_CRON_LIST=Afficher les taches cron +ACTION_CRON_REMOVE=Supprimer les taches cron +TARGET_GRID=Grille complete +TARGET_ROBUST=Robust uniquement +TARGET_SIM1=Region primaire uniquement +TARGET_STANDALONE=Instance standalone +TARGET_JANUS=Serveur passerelle Janus +PROFILE_GRID_SIM=Simulateurs de grille +PROFILE_ROBUST=Service Robust +PROFILE_STANDALONE=Mode standalone +ERR_UNKNOWN_MODULE=Module inconnu +ERR_UNKNOWN_UI_MODULE=Module UI inconnu +ERR_MISSING_MODULE=Option --module manquante en mode CLI +ERR_INVALID_PROFILE=Profil invalide +ERR_ACTION_NOT_ALLOWED=Action non autorisee pour ce profil +ERR_TARGET_NOT_ALLOWED=Cible non autorisee pour ce profil +ERR_NO_UI_BACKEND=Aucun backend UI trouve. Installez whiptail ou dialog, ou utilisez les parametres CLI. +HELP_LANG=Langue (de|en|fr|es), defaut: de +HELP_PROFILE=Profil (grid-sim|robust|standalone), defaut: grid-sim diff --git a/modular/osmtool_cleanup.sh b/modular/osmtool_cleanup.sh new file mode 100644 index 0000000..51ac331 --- /dev/null +++ b/modular/osmtool_cleanup.sh @@ -0,0 +1,125 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck source=./osmtool_core.sh +source "$SCRIPT_DIR/osmtool_core.sh" + +usage() { + cat <<'EOF' +Usage: + osmtool_cleanup.sh [--workdir ] --action [--dry-run ] + +Examples: + osmtool_cleanup.sh --action log-clean --workdir /opt --dry-run true + osmtool_cleanup.sh --action cache-clean --workdir /opt/opensim +EOF + print_usage_common +} + +realpath_safe() { + local p="$1" + python3 -c 'import os,sys; print(os.path.realpath(sys.argv[1]))' "$p" 2>/dev/null || echo "$p" +} + +assert_safe_workdir() { + local rp + rp="$(realpath_safe "$WORKDIR")" + + [[ -n "$rp" ]] || die "Invalid workdir" + [[ "$rp" != "/" ]] || die "Refusing cleanup on root directory" + [[ "$rp" != "/opt" ]] || die "Refusing cleanup on /opt root; choose a dedicated OpenSim workdir" + [[ -d "$rp" ]] || die "Workdir not found: $rp" + + WORKDIR="$rp" +} + +run_delete() { + local path="$1" + + if [[ "$DRY_RUN" == "true" ]]; then + log INFO "[DRY-RUN] Would remove: $path" + else + rm -rf "$path" + log INFO "Removed: $path" + fi +} + +run_delete_file() { + local path="$1" + + if [[ "$DRY_RUN" == "true" ]]; then + log INFO "[DRY-RUN] Would delete file: $path" + else + rm -f "$path" + log INFO "Deleted file: $path" + fi +} + +cache_clean() { + log INFO "Cleaning map tiles and cache under $WORKDIR" + local d + while IFS= read -r d; do + [[ -n "$d" ]] || continue + run_delete "$d" + done < <(find "$WORKDIR" -mindepth 2 -type d \( -name "assetcache" -o -name "MapTiles" -o -name "cache" \)) +} + +log_clean() { + log INFO "Cleaning log files under $WORKDIR" + local f + while IFS= read -r f; do + [[ -n "$f" ]] || continue + run_delete_file "$f" + done < <(find "$WORKDIR" -type f \( -name "*.log" -o -name "OpenSim.log" -o -name "ProblemRestarts.log" \)) +} + +tmp_clean() { + log INFO "Cleaning temporary files under $WORKDIR" + local f + while IFS= read -r f; do + [[ -n "$f" ]] || continue + run_delete_file "$f" + done < <(find "$WORKDIR" -type f \( -name "*.tmp" -o -name "*.temp" \)) +} + +safe_reboot() { + if confirm "Reboot" "Reboot server now?"; then + log INFO "Reboot requested" + sudo reboot + else + log INFO "Reboot canceled" + fi +} + +WORKDIR="$DEFAULT_WORKDIR" +ACTION="" +PROFILE="grid-sim" +DRY_RUN="false" + +while [[ $# -gt 0 ]]; do + case "$1" in + --workdir) WORKDIR="$2"; shift 2 ;; + --profile) PROFILE="$2"; shift 2 ;; + --action) ACTION="$2"; shift 2 ;; + --dry-run) DRY_RUN="$2"; shift 2 ;; + --help|-h) usage; exit 0 ;; + *) die "Unknown option: $1" ;; + esac +done + +[[ -n "$ACTION" ]] || die "Missing --action" +validate_profile "$PROFILE" +ensure_profile_action_allowed cleanup "$PROFILE" "$ACTION" +case "$DRY_RUN" in true|false) ;; *) die "--dry-run must be true or false" ;; esac +assert_safe_workdir +log INFO "Using profile: $PROFILE" +log INFO "Dry-run mode: $DRY_RUN" + +case "$ACTION" in + cache-clean) cache_clean ;; + log-clean) log_clean ;; + tmp-clean) tmp_clean ;; + reboot) safe_reboot ;; + *) die "Unsupported action: $ACTION" ;; +esac diff --git a/modular/osmtool_config.sh b/modular/osmtool_config.sh new file mode 100644 index 0000000..05f5ece --- /dev/null +++ b/modular/osmtool_config.sh @@ -0,0 +1,207 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck source=./osmtool_core.sh +source "$SCRIPT_DIR/osmtool_core.sh" + +usage() { + cat <<'EOF' +Usage: + osmtool_config.sh [--workdir ] --action + [--file ] [--strict ] + +Examples: + osmtool_config.sh --action validate-file --file /opt/sim1/bin/OpenSim.ini + osmtool_config.sh --action validate-runtime --profile grid-sim --workdir /opt +EOF + print_usage_common +} + +trim() { + local value="$1" + value="${value#"${value%%[![:space:]]*}"}" + value="${value%"${value##*[![:space:]]}"}" + printf '%s' "$value" +} + +check_runtime_file() { + local file="$1" + local errors_before="$VALIDATION_ERRORS" + + if [[ ! -f "$file" ]]; then + log ERROR "Missing config file: $file" + VALIDATION_ERRORS=$((VALIDATION_ERRORS + 1)) + return 1 + fi + + validate_file "$file" + if [[ "$VALIDATION_ERRORS" -gt "$errors_before" ]]; then + return 1 + fi + + return 0 +} + +validate_ini_file() { + local file="$1" + local line_number=0 + local line trimmed seen_section=false + + while IFS= read -r line || [[ -n "$line" ]]; do + line_number=$((line_number + 1)) + trimmed="$(trim "$line")" + + [[ -z "$trimmed" ]] && continue + case "$trimmed" in + ';'*|'#'*) continue ;; + '['*']') + seen_section=true + continue + ;; + *=*) + continue + ;; + .include*) + continue + ;; + *) + log ERROR "Invalid INI syntax in $file:$line_number -> $line" + VALIDATION_ERRORS=$((VALIDATION_ERRORS + 1)) + ;; + esac + done < "$file" + + if [[ "$STRICT" == "true" && "$seen_section" == "false" ]]; then + log ERROR "INI file has no section headers: $file" + VALIDATION_ERRORS=$((VALIDATION_ERRORS + 1)) + fi +} + +validate_json_file() { + local file="$1" + + if command -v jq >/dev/null 2>&1; then + if ! jq empty "$file" >/dev/null 2>&1; then + log ERROR "Invalid JSON syntax: $file" + VALIDATION_ERRORS=$((VALIDATION_ERRORS + 1)) + fi + return 0 + fi + + if command -v python3 >/dev/null 2>&1; then + if ! python3 -m json.tool "$file" >/dev/null 2>&1; then + log ERROR "Invalid JSON syntax: $file" + VALIDATION_ERRORS=$((VALIDATION_ERRORS + 1)) + fi + return 0 + fi + + die "validate-json requires jq or python3" +} + +validate_file() { + local file="$1" + + [[ -f "$file" ]] || die "Config file not found: $file" + + case "$file" in + *.ini|*.INI) + log INFO "Validating INI file: $file" + validate_ini_file "$file" + ;; + *.json|*.JSON) + log INFO "Validating JSON file: $file" + validate_json_file "$file" + ;; + *) + die "Unsupported config file type: $file" + ;; + esac +} + +validate_grid_runtime() { + local sim_ini + local sim_count=0 + + check_runtime_file "$WORKDIR/robust/bin/Robust.ini" + + shopt -s nullglob + for sim_ini in "$WORKDIR"/sim*/bin/OpenSim.ini; do + sim_count=$((sim_count + 1)) + check_runtime_file "$sim_ini" + done + shopt -u nullglob + + if [[ "$sim_count" -eq 0 ]]; then + log ERROR "No region runtime configs found under $WORKDIR/sim*/bin/OpenSim.ini" + VALIDATION_ERRORS=$((VALIDATION_ERRORS + 1)) + fi +} + +validate_robust_runtime() { + check_runtime_file "$WORKDIR/robust/bin/Robust.ini" +} + +validate_standalone_runtime() { + check_runtime_file "$WORKDIR/standalone/bin/OpenSim.ini" +} + +validate_runtime() { + case "$PROFILE" in + grid-sim) validate_grid_runtime ;; + robust) validate_robust_runtime ;; + standalone) validate_standalone_runtime ;; + esac + + if [[ "$VALIDATION_ERRORS" -gt 0 ]]; then + die "Configuration validation failed with $VALIDATION_ERRORS issue(s)" + fi + + log INFO "Configuration validation passed" +} + +WORKDIR="$DEFAULT_WORKDIR" +ACTION="" +PROFILE="grid-sim" +FILE="" +STRICT="false" +VALIDATION_ERRORS=0 + +while [[ $# -gt 0 ]]; do + case "$1" in + --workdir) WORKDIR="$2"; shift 2 ;; + --profile) PROFILE="$2"; shift 2 ;; + --action) ACTION="$2"; shift 2 ;; + --file) FILE="$2"; shift 2 ;; + --strict) STRICT="$2"; shift 2 ;; + --help|-h) usage; exit 0 ;; + *) die "Unknown option: $1" ;; + esac +done + +[[ -n "$ACTION" ]] || die "Missing --action" +validate_profile "$PROFILE" +ensure_profile_action_allowed config "$PROFILE" "$ACTION" + +case "$STRICT" in + true|false) ;; + *) die "--strict must be true or false" ;; +esac + +log INFO "Using profile: $PROFILE" + +case "$ACTION" in + validate-file) + [[ -n "$FILE" ]] || die "validate-file requires --file" + validate_file "$FILE" + if [[ "$VALIDATION_ERRORS" -gt 0 ]]; then + die "Configuration validation failed with $VALIDATION_ERRORS issue(s)" + fi + log INFO "Configuration validation passed" + ;; + validate-runtime) + validate_runtime + ;; + *) die "Unsupported action: $ACTION" ;; +esac \ No newline at end of file diff --git a/modular/osmtool_core.sh b/modular/osmtool_core.sh new file mode 100644 index 0000000..c77fc16 --- /dev/null +++ b/modular/osmtool_core.sh @@ -0,0 +1,255 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +export DEFAULT_WORKDIR="/opt" +LANG_DIR="$ROOT_DIR/lang" +APP_LANG="${OSM_LANG:-de}" + +LOG_FILE="${LOG_FILE:-$ROOT_DIR/osmtool_backup.log}" + +declare -A I18N_CURRENT=() +declare -A I18N_EN=() + +log() { + local level="$1" + shift + printf '[%s] [%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$level" "$*" | tee -a "$LOG_FILE" +} + +die() { + log ERROR "$*" + exit 1 +} + +need_cmd() { + command -v "$1" >/dev/null 2>&1 || die "Missing required command: $1" +} + +normalize_lang() { + case "$1" in + de|en|fr|es) echo "$1" ;; + *) echo "de" ;; + esac +} + +validate_profile() { + case "$1" in + grid-sim|robust|standalone) return 0 ;; + *) die "$(msg ERR_INVALID_PROFILE): $1" ;; + esac +} + +profile_action_allowed() { + local module="$1" + local profile="$2" + local action="$3" + + case "$module:$profile" in + install:grid-sim|install:robust|install:standalone) + case "$action" in bootstrap-server|server-check|prepare-ubuntu|install-opensim-deps|install-dotnet8|install-opensim|configure-opensim|configure-database|compile-janus|configure-janus|install-janus) return 0 ;; esac + ;; + startstop:grid-sim|startstop:robust|startstop:standalone) + case "$action" in start|stop|restart) return 0 ;; esac + ;; + cleanup:grid-sim|cleanup:robust|cleanup:standalone) + case "$action" in cache-clean|log-clean|tmp-clean|reboot) return 0 ;; esac + ;; + health:grid-sim|health:robust|health:standalone) + case "$action" in run) return 0 ;; esac + ;; + backup:grid-sim) + case "$action" in db-backup|oar-backup|full-backup) return 0 ;; esac + ;; + backup:robust) + case "$action" in db-backup|full-backup) return 0 ;; esac + ;; + backup:standalone) + case "$action" in oar-backup|full-backup) return 0 ;; esac + ;; + cron:grid-sim|cron:robust|cron:standalone) + case "$action" in install|list|remove) return 0 ;; esac + ;; + restore:grid-sim) + case "$action" in db-restore|oar-restore|full-restore|list-backups) return 0 ;; esac + ;; + restore:robust) + case "$action" in db-restore|full-restore|list-backups) return 0 ;; esac + ;; + restore:standalone) + case "$action" in oar-restore|full-restore|list-backups) return 0 ;; esac + ;; + update:grid-sim|update:robust|update:standalone) + case "$action" in create-rollback|list-rollbacks|update-opensim|update-janus|full-update) return 0 ;; esac + ;; + config:grid-sim|config:robust|config:standalone) + case "$action" in validate-file|validate-runtime) return 0 ;; esac + ;; + report:grid-sim|report:robust|report:standalone) + case "$action" in generate) return 0 ;; esac + ;; + smoke:grid-sim|smoke:robust|smoke:standalone) + case "$action" in run) return 0 ;; esac + ;; + esac + + return 1 +} + +ensure_profile_action_allowed() { + local module="$1" + local profile="$2" + local action="$3" + + if ! profile_action_allowed "$module" "$profile" "$action"; then + die "$(msg ERR_ACTION_NOT_ALLOWED): module=$module profile=$profile action=$action" + fi +} + +profile_target_allowed() { + local profile="$1" + local target="$2" + + case "$profile" in + grid-sim) + case "$target" in grid|robust|sim1|region|janus) return 0 ;; esac + ;; + robust) + case "$target" in robust|janus) return 0 ;; esac + ;; + standalone) + case "$target" in standalone|janus) return 0 ;; esac + ;; + esac + + return 1 +} + +ensure_profile_target_allowed() { + local profile="$1" + local target="$2" + + if ! profile_target_allowed "$profile" "$target"; then + die "$(msg ERR_TARGET_NOT_ALLOWED): profile=$profile target=$target" + fi +} + +load_lang_file() { + local lang_file="$1" + local target_array_name="$2" + local line key value + + [[ -f "$lang_file" ]] || return 1 + + while IFS= read -r line || [[ -n "$line" ]]; do + [[ -z "$line" || "${line:0:1}" == "#" ]] && continue + [[ "$line" == *=* ]] || continue + key="${line%%=*}" + value="${line#*=}" + if [[ "$target_array_name" == "I18N_EN" ]]; then + I18N_EN["$key"]="$value" + else + I18N_CURRENT["$key"]="$value" + fi + done < "$lang_file" + return 0 +} + +init_i18n() { + APP_LANG="$(normalize_lang "$APP_LANG")" + + I18N_CURRENT=() + I18N_EN=() + + load_lang_file "$LANG_DIR/en.lang" I18N_EN || true + load_lang_file "$LANG_DIR/${APP_LANG}.lang" I18N_CURRENT || true +} + +set_language() { + APP_LANG="$(normalize_lang "$1")" + export OSM_LANG="$APP_LANG" + init_i18n +} + +msg() { + local key="$1" + if [[ -n "${I18N_CURRENT[$key]:-}" ]]; then + printf '%s' "${I18N_CURRENT[$key]}" + return + fi + + if [[ -n "${I18N_EN[$key]:-}" ]]; then + printf '%s' "${I18N_EN[$key]}" + return + fi + + printf '%s' "$key" +} + +ui_backend() { + if command -v whiptail >/dev/null 2>&1; then + echo "whiptail" + return + fi + + if command -v dialog >/dev/null 2>&1; then + echo "dialog" + return + fi + + echo "none" +} + +show_menu() { + local title="$1" + local text="$2" + shift 2 + local backend + backend="$(ui_backend)" + + case "$backend" in + whiptail) + whiptail --title "$title" --menu "$text" 20 80 12 "$@" 3>&1 1>&2 2>&3 + ;; + dialog) + dialog --clear --stdout --title "$title" --menu "$text" 20 80 12 "$@" + ;; + none) + die "$(msg ERR_NO_UI_BACKEND)" + ;; + esac +} + +confirm() { + local title="$1" + local text="$2" + local backend + backend="$(ui_backend)" + + case "$backend" in + whiptail) + whiptail --title "$title" --yesno "$text" 10 70 + ;; + dialog) + dialog --clear --title "$title" --yesno "$text" 10 70 + ;; + none) + return 1 + ;; + esac +} + +print_usage_common() { + cat <<'EOF' +Global options: + --workdir Base path (default: /opt) + --mode Run in CLI mode (default) or menu mode + --lang Language selection + --profile Runtime profile (grid-sim|robust|standalone) + --help Show help +EOF +} + +# Ensure translations are available in every script that sources this core. +init_i18n diff --git a/modular/osmtool_cron.sh b/modular/osmtool_cron.sh new file mode 100644 index 0000000..b4f00bc --- /dev/null +++ b/modular/osmtool_cron.sh @@ -0,0 +1,203 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +# shellcheck source=./osmtool_core.sh +source "$SCRIPT_DIR/osmtool_core.sh" + +usage() { + cat <<'EOF' +Usage: + osmtool_cron.sh [--workdir ] --action + [--backup-schedule ""] [--restart-schedule ""] [--check-schedule ""] [--report-schedule ""] [--smoke-schedule ""] + [--db-user ] [--db-pass ] [--db-name ] + [--region ] [--session ] [--compress ] + +Examples: + osmtool_cron.sh --action list + osmtool_cron.sh --action install --backup-schedule "0 3 * * *" --db-user root --db-pass secret --db-name opensim --region sim1 + osmtool_cron.sh --action remove +EOF + print_usage_common +} + +need_crontab() { + need_cmd crontab +} + +quote_sq() { + local s="$1" + s="${s//\'/\'\"\'\"\'}" + printf "'%s'" "$s" +} + +build_backup_cmd() { + local cmd + cmd="bash $ROOT_DIR/osmtool_main.sh --module backup --profile $PROFILE --workdir $WORKDIR --action full-backup --db-user $DB_USER --db-name $DB_NAME --region $REGION --session $SESSION_NAME --compress $COMPRESS" + + if [[ -n "$DB_PASS" ]]; then + cmd+=" --db-pass $DB_PASS" + fi + + printf '%s' "$cmd" +} + +build_restart_cmd() { + printf '%s' "bash $ROOT_DIR/osmtool_main.sh --module startstop --profile $PROFILE --workdir $WORKDIR --target grid --action restart" +} + +build_check_cmd() { + printf '%s' "bash $ROOT_DIR/osmtool_main.sh --module install --profile $PROFILE --workdir $WORKDIR --action server-check" +} + +build_report_cmd() { + local cmd + cmd="bash $ROOT_DIR/osmtool_main.sh --module report --profile $PROFILE --workdir $WORKDIR --action generate --db-user $DB_USER --db-name $DB_NAME" + + if [[ -n "$DB_PASS" ]]; then + cmd+=" --db-pass $DB_PASS" + fi + + printf '%s' "$cmd" +} + +build_smoke_cmd() { + printf '%s' "bash $ROOT_DIR/osmtool_main.sh --module smoke --profile $PROFILE --workdir $WORKDIR --action run" +} + +cron_block_start="# OSMTOOL_CRON_BEGIN" +cron_block_end="# OSMTOOL_CRON_END" + +emit_cron_block() { + local backup_cmd restart_cmd check_cmd report_cmd smoke_cmd + backup_cmd="$(build_backup_cmd)" + restart_cmd="$(build_restart_cmd)" + check_cmd="$(build_check_cmd)" + report_cmd="$(build_report_cmd)" + smoke_cmd="$(build_smoke_cmd)" + + echo "$cron_block_start" + echo "SHELL=/bin/bash" + echo "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + echo "$BACKUP_SCHEDULE $backup_cmd >> $ROOT_DIR/osmtool_backup.log 2>&1" + echo "$RESTART_SCHEDULE $restart_cmd >> $ROOT_DIR/osmtool_backup.log 2>&1" + echo "$CHECK_SCHEDULE $check_cmd >> $ROOT_DIR/osmtool_backup.log 2>&1" + echo "$REPORT_SCHEDULE $report_cmd >> $ROOT_DIR/osmtool_backup.log 2>&1" + echo "$SMOKE_SCHEDULE $smoke_cmd >> $ROOT_DIR/osmtool_backup.log 2>&1" + echo "$cron_block_end" +} + +existing_crontab() { + crontab -l 2>/dev/null || true +} + +strip_osmtool_block() { + awk -v start="$cron_block_start" -v end="$cron_block_end" ' + $0 == start {inblock=1; next} + $0 == end {inblock=0; next} + !inblock {print} + ' +} + +install_cron() { + local current tmp + [[ -n "$DB_USER" && -n "$DB_NAME" ]] || die "install cron requires --db-user and --db-name" + + current="$(existing_crontab)" + tmp="$(mktemp)" + + printf '%s\n' "$current" | strip_osmtool_block > "$tmp" + if [[ -s "$tmp" ]]; then + echo >> "$tmp" + fi + emit_cron_block >> "$tmp" + + crontab "$tmp" + rm -f "$tmp" + log INFO "OSMTool cron jobs installed/updated" +} + +list_cron() { + local current + current="$(existing_crontab)" + + if printf '%s\n' "$current" | grep -q "^$cron_block_start$"; then + printf '%s\n' "$current" | awk -v start="$cron_block_start" -v end="$cron_block_end" ' + $0 == start {inblock=1; print; next} + $0 == end {print; inblock=0; next} + inblock {print} + ' + else + log INFO "No OSMTool cron block configured" + fi +} + +remove_cron() { + local current tmp + current="$(existing_crontab)" + tmp="$(mktemp)" + + printf '%s\n' "$current" | strip_osmtool_block > "$tmp" + crontab "$tmp" + rm -f "$tmp" + log INFO "OSMTool cron block removed" +} + +WORKDIR="$DEFAULT_WORKDIR" +ACTION="" +PROFILE="grid-sim" + +BACKUP_SCHEDULE="0 3 * * *" +RESTART_SCHEDULE="0 5 * * 1" +CHECK_SCHEDULE="*/15 * * * *" +REPORT_SCHEDULE="30 6 * * *" +SMOKE_SCHEDULE="45 6 * * *" + +DB_USER="root" +DB_PASS="" +DB_NAME="opensim" +REGION="sim1" +SESSION_NAME="sim1" +COMPRESS="true" + +while [[ $# -gt 0 ]]; do + case "$1" in + --workdir) WORKDIR="$2"; shift 2 ;; + --profile) PROFILE="$2"; shift 2 ;; + --action) ACTION="$2"; shift 2 ;; + --backup-schedule) BACKUP_SCHEDULE="$2"; shift 2 ;; + --restart-schedule) RESTART_SCHEDULE="$2"; shift 2 ;; + --check-schedule) CHECK_SCHEDULE="$2"; shift 2 ;; + --report-schedule) REPORT_SCHEDULE="$2"; shift 2 ;; + --smoke-schedule) SMOKE_SCHEDULE="$2"; shift 2 ;; + --db-user) DB_USER="$2"; shift 2 ;; + --db-pass) DB_PASS="$2"; shift 2 ;; + --db-name) DB_NAME="$2"; shift 2 ;; + --region) REGION="$2"; shift 2 ;; + --session) SESSION_NAME="$2"; shift 2 ;; + --compress) COMPRESS="$2"; shift 2 ;; + --help|-h) usage; exit 0 ;; + *) die "Unknown option: $1" ;; + esac +done + +[[ -n "$ACTION" ]] || die "Missing --action" +validate_profile "$PROFILE" +need_crontab + +case "$ACTION" in + install|list|remove) ;; + *) die "Unsupported action: $ACTION" ;; +esac + +case "$COMPRESS" in + true|false) ;; + *) die "--compress must be true or false" ;; +esac + +case "$ACTION" in + install) install_cron ;; + list) list_cron ;; + remove) remove_cron ;; +esac diff --git a/modular/osmtool_data.sh b/modular/osmtool_data.sh new file mode 100644 index 0000000..68c7e68 --- /dev/null +++ b/modular/osmtool_data.sh @@ -0,0 +1,170 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck source=./osmtool_core.sh +source "$SCRIPT_DIR/osmtool_core.sh" + +usage() { + cat <<'EOF' +Usage: + osmtool_data.sh [--workdir ] --action + [--db-user ] [--db-pass ] [--db-name ] + [--region ] [--session ] [--compress ] + +Examples: + osmtool_data.sh --action db-backup --db-user opensim --db-pass secret --db-name robust + osmtool_data.sh --action oar-backup --region sim1 --session sim1 --workdir /opt +EOF + print_usage_common +} + +timestamp() { + date '+%Y%m%d_%H%M%S' +} + +backup_dir() { + printf '%s' "$WORKDIR/backup" +} + +ensure_backup_dir() { + local dir + dir="$(backup_dir)" + + if mkdir -p "$dir" 2>/dev/null; then + return 0 + fi + + sudo mkdir -p "$dir" + sudo chown "$(id -u):$(id -g)" "$dir" +} + +compress_if_enabled() { + local file_path="$1" + + if [[ "$COMPRESS" != "true" ]]; then + printf '%s' "$file_path" + return 0 + fi + + need_cmd gzip + gzip -f "$file_path" + printf '%s.gz' "$file_path" +} + +resolve_default_session() { + if [[ -n "$SESSION_NAME" ]]; then + printf '%s' "$SESSION_NAME" + return 0 + fi + + if [[ -n "$REGION" ]]; then + printf '%s' "$REGION" + return 0 + fi + + printf 'sim1' +} + +screen_session_exists() { + local name="$1" + screen -list 2>/dev/null | grep -qE "\\.${name}[[:space:]]" +} + +send_to_screen() { + local session_name="$1" + local command_text="$2" + + screen -S "$session_name" -p 0 -X stuff "${command_text}^M" +} + +db_backup_mysql() { + local out + need_cmd mysqldump + [[ -n "$DB_USER" && -n "$DB_PASS" && -n "$DB_NAME" ]] || die "db-backup(mysql) requires --db-user --db-pass --db-name" + + ensure_backup_dir + out="$(backup_dir)/robust_${DB_NAME}_$(timestamp).sql" + log INFO "Creating MySQL backup: $out" + mysqldump -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" > "$out" + out="$(compress_if_enabled "$out")" + log INFO "DB backup created: $out" +} + +db_backup() { + db_backup_mysql +} + +oar_backup() { + local session_name out command_text + + [[ -n "$REGION" ]] || die "oar-backup requires --region" + session_name="$(resolve_default_session)" + + ensure_backup_dir + out="$(backup_dir)/${REGION}_$(timestamp).oar" + + if ! screen_session_exists "$session_name"; then + die "Screen session not running: $session_name" + fi + + # OpenSim console command to export current region as OAR. + command_text="save oar $out" + log INFO "Sending OAR export command to screen session '$session_name': $command_text" + send_to_screen "$session_name" "$command_text" + + log INFO "OAR export command sent. Validate completion in the session log/output." +} + +full_backup() { + db_backup + if [[ -n "$REGION" ]]; then + oar_backup + else + log INFO "No --region provided. DB backup only in full-backup mode." + fi +} + +WORKDIR="$DEFAULT_WORKDIR" +ACTION="" +DB_USER="" +DB_PASS="" +DB_NAME="" +REGION="" +SESSION_NAME="" +COMPRESS="false" +PROFILE="grid-sim" + +while [[ $# -gt 0 ]]; do + case "$1" in + --workdir) WORKDIR="$2"; shift 2 ;; + --profile) PROFILE="$2"; shift 2 ;; + --action) ACTION="$2"; shift 2 ;; + --db-user) DB_USER="$2"; shift 2 ;; + --db-pass) DB_PASS="$2"; shift 2 ;; + --db-name) DB_NAME="$2"; shift 2 ;; + --region) REGION="$2"; shift 2 ;; + --session) SESSION_NAME="$2"; shift 2 ;; + --compress) COMPRESS="$2"; shift 2 ;; + --help|-h) usage; exit 0 ;; + *) die "Unknown option: $1" ;; + esac +done + +[[ -n "$ACTION" ]] || die "Missing --action" +validate_profile "$PROFILE" +ensure_profile_action_allowed backup "$PROFILE" "$ACTION" + +case "$COMPRESS" in + true|false) ;; + *) die "--compress must be true or false" ;; +esac + +log INFO "Using profile: $PROFILE" + +case "$ACTION" in + db-backup) db_backup ;; + oar-backup) oar_backup ;; + full-backup) full_backup ;; + *) die "Unsupported action: $ACTION" ;; +esac diff --git a/modular/osmtool_health.sh b/modular/osmtool_health.sh new file mode 100644 index 0000000..c03bd0b --- /dev/null +++ b/modular/osmtool_health.sh @@ -0,0 +1,268 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +# shellcheck source=./osmtool_core.sh +source "$SCRIPT_DIR/osmtool_core.sh" + +usage() { + cat <<'EOF' +Usage: + osmtool_health.sh [--workdir ] --action + [--expected-screens ] [--ports ] [--db-user ] [--db-pass ] [--db-name ] + [--log-file ] [--warning-threshold ] + +Examples: + osmtool_health.sh --action run --workdir /opt + osmtool_health.sh --action run --workdir /opt --expected-screens robustserver,sim1 --ports 9000,8002,8088 +EOF + print_usage_common +} + +split_csv() { + local csv="$1" + local token + local IFS=',' + read -r -a _TOKENS <<< "$csv" + for token in "${_TOKENS[@]}"; do + token="${token## }" + token="${token%% }" + [[ -n "$token" ]] && printf '%s\n' "$token" + done +} + +report_ok() { + log INFO "[OK] $1" +} + +report_warn() { + log INFO "[WARN] $1" +} + +report_err() { + log ERROR "[FAIL] $1" +} + +check_screens() { + local fail_count=0 + local screen_name + + if [[ -z "$EXPECTED_SCREENS" ]]; then + report_warn "Screen check skipped (no expected screens configured)" + return 0 + fi + + while IFS= read -r screen_name; do + [[ -n "$screen_name" ]] || continue + if screen -list 2>/dev/null | grep -qE "\\.${screen_name}[[:space:]]"; then + report_ok "Screen session running: $screen_name" + else + report_err "Screen session missing: $screen_name" + fail_count=$((fail_count + 1)) + fi + done < <(split_csv "$EXPECTED_SCREENS") + + return "$fail_count" +} + +check_ports() { + local fail_count=0 + local port + local cmd_ss=0 + + if [[ -z "$PORTS" ]]; then + report_warn "Port check skipped (no ports configured)" + return 0 + fi + + if command -v ss >/dev/null 2>&1; then + cmd_ss=1 + elif ! command -v netstat >/dev/null 2>&1; then + report_warn "Port check skipped (neither ss nor netstat available)" + return 0 + fi + + while IFS= read -r port; do + [[ -n "$port" ]] || continue + if [[ "$cmd_ss" -eq 1 ]]; then + if ss -ltnH | awk '{print $4}' | grep -qE "[:.]${port}$"; then + report_ok "Port listening: $port" + else + report_err "Port not listening: $port" + fail_count=$((fail_count + 1)) + fi + else + if netstat -ltn 2>/dev/null | awk '{print $4}' | grep -qE "[:.]${port}$"; then + report_ok "Port listening: $port" + else + report_err "Port not listening: $port" + fail_count=$((fail_count + 1)) + fi + fi + done < <(split_csv "$PORTS") + + return "$fail_count" +} + +check_database() { + local fail_count=0 + + if [[ -z "$DB_USER" || -z "$DB_NAME" ]]; then + report_warn "Database check skipped (missing --db-user/--db-name)" + return 0 + fi + + if ! command -v mariadb >/dev/null 2>&1; then + report_err "Database check failed: mariadb client missing" + return 1 + fi + + if [[ -n "$DB_PASS" ]]; then + if mariadb -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" -e "SELECT 1" >/dev/null 2>&1; then + report_ok "Database reachable: $DB_NAME" + else + report_err "Database not reachable: $DB_NAME" + fail_count=$((fail_count + 1)) + fi + else + if mariadb -u"$DB_USER" "$DB_NAME" -e "SELECT 1" >/dev/null 2>&1; then + report_ok "Database reachable: $DB_NAME" + else + report_err "Database not reachable: $DB_NAME" + fail_count=$((fail_count + 1)) + fi + fi + + return "$fail_count" +} + +count_log_warnings() { + local file="$1" + grep -Eic 'error|warn|exception|fatal' "$file" 2>/dev/null || true +} + +check_logs() { + local warn_count total=0 + local default_logs=() + + if [[ -n "$LOG_FILE_PATH" ]]; then + default_logs+=("$LOG_FILE_PATH") + else + default_logs+=("$ROOT_DIR/osmtool_backup.log") + default_logs+=("$WORKDIR/robust/bin/OpenSim.log") + fi + + for logf in "${default_logs[@]}"; do + if [[ -f "$logf" ]]; then + warn_count="$(count_log_warnings "$logf")" + report_ok "Log scan: $logf -> $warn_count matches" + total=$((total + warn_count)) + else + report_warn "Log file not found, skipped: $logf" + fi + done + + if [[ "$total" -gt "$WARNING_THRESHOLD" ]]; then + report_err "Log warning threshold exceeded: $total > $WARNING_THRESHOLD" + return 1 + fi + + report_ok "Log warning threshold OK: $total <= $WARNING_THRESHOLD" + return 0 +} + +default_screens_for_profile() { + case "$1" in + grid-sim) echo "robustserver,sim1" ;; + robust) echo "robustserver" ;; + standalone) echo "standalone" ;; + esac +} + +default_ports_for_profile() { + case "$1" in + grid-sim) echo "8002,9000,9001,8088" ;; + robust) echo "8002,9000,9001" ;; + standalone) echo "9000" ;; + esac +} + +run_health() { + local fails=0 + + log INFO "Running health checks for profile: $PROFILE" + log INFO "Using workdir: $WORKDIR" + + if ! check_screens; then + fails=$((fails + 1)) + fi + + if ! check_ports; then + fails=$((fails + 1)) + fi + + if ! check_database; then + fails=$((fails + 1)) + fi + + if ! check_logs; then + fails=$((fails + 1)) + fi + + if [[ "$fails" -eq 0 ]]; then + report_ok "Healthcheck PASSED" + return 0 + fi + + report_err "Healthcheck FAILED with $fails failing check group(s)" + return 1 +} + +WORKDIR="$DEFAULT_WORKDIR" +ACTION="" +PROFILE="grid-sim" +EXPECTED_SCREENS="" +PORTS="" +DB_USER="" +DB_PASS="" +DB_NAME="" +LOG_FILE_PATH="" +WARNING_THRESHOLD=50 + +while [[ $# -gt 0 ]]; do + case "$1" in + --workdir) WORKDIR="$2"; shift 2 ;; + --profile) PROFILE="$2"; shift 2 ;; + --action) ACTION="$2"; shift 2 ;; + --expected-screens) EXPECTED_SCREENS="$2"; shift 2 ;; + --ports) PORTS="$2"; shift 2 ;; + --db-user) DB_USER="$2"; shift 2 ;; + --db-pass) DB_PASS="$2"; shift 2 ;; + --db-name) DB_NAME="$2"; shift 2 ;; + --log-file) LOG_FILE_PATH="$2"; shift 2 ;; + --warning-threshold) WARNING_THRESHOLD="$2"; shift 2 ;; + --help|-h) usage; exit 0 ;; + *) die "Unknown option: $1" ;; + esac +done + +[[ -n "$ACTION" ]] || die "Missing --action" +validate_profile "$PROFILE" +ensure_profile_action_allowed health "$PROFILE" "$ACTION" + +if [[ -z "$EXPECTED_SCREENS" ]]; then + EXPECTED_SCREENS="$(default_screens_for_profile "$PROFILE")" +elif [[ "$EXPECTED_SCREENS" == "none" ]]; then + EXPECTED_SCREENS="" +fi +if [[ -z "$PORTS" ]]; then + PORTS="$(default_ports_for_profile "$PROFILE")" +elif [[ "$PORTS" == "none" ]]; then + PORTS="" +fi + +case "$ACTION" in + run) run_health ;; + *) die "Unsupported action: $ACTION" ;; +esac diff --git a/modular/osmtool_install.sh b/modular/osmtool_install.sh new file mode 100644 index 0000000..1577485 --- /dev/null +++ b/modular/osmtool_install.sh @@ -0,0 +1,1022 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck source=./osmtool_core.sh +source "$SCRIPT_DIR/osmtool_core.sh" + +usage() { + cat <<'EOF' +Usage: + osmtool_install.sh [--workdir ] --action + [--opensim-dir ] [--opensim-repo ] [--opensim-branch ] [--repo-mode ] + [--tsassets-dir ] [--currency-dir ] [--data-backup-dir ] + [--deploy-binaries ] [--legacy-patch-dir ] + [--db-root-user ] [--db-root-pass ] [--db-user ] [--db-pass ] + [--janus-prefix ] [--janus-src ] [--public-host ] [--http-port ] + [--admin-port ] [--rtp-range ] [--enable-admin ] + [--api-secret ] [--admin-secret ] + +Actions: + bootstrap-server Server zentral vorbereiten (Pflicht vor Build/Install) + server-check Validate server readiness and required dependencies + prepare-ubuntu Basic apt update/upgrade and base packages + install-opensim-deps Install OpenSim runtime dependencies + install-dotnet8 Install .NET SDK 8.0 for OpenSim build/runtime + install-opensim Clone/update, enrich, build and optionally deploy OpenSim + configure-opensim Generate required runtime ini files from example templates + configure-database Create MariaDB databases/users/grants for robust and sim* runtimes + compile-janus Build and install Janus Gateway + configure-janus Configure Janus for OpenSim usage + install-janus Compile + configure Janus in one run + +Examples: + osmtool_install.sh --action bootstrap-server --workdir /opt + osmtool_install.sh --action configure-opensim --profile grid-sim --workdir /opt + osmtool_install.sh --action configure-database --workdir /opt --db-user opensim --db-pass secret +EOF + print_usage_common +} + +has_dotnet8_sdk() { + if ! command -v dotnet >/dev/null 2>&1; then + return 1 + fi + + dotnet --list-sdks 2>/dev/null | awk '{print $1}' | grep -Eq '^8\.' +} + +package_available_in_apt() { + local pkg="$1" + apt-cache show "$pkg" >/dev/null 2>&1 +} + +resolve_package_token() { + local token="$1" + local candidate + local IFS='|' + read -r -a candidates <<< "$token" + + for candidate in "${candidates[@]}"; do + if dpkg -s "$candidate" >/dev/null 2>&1; then + echo "$candidate" + return 0 + fi + done + + for candidate in "${candidates[@]}"; do + if package_available_in_apt "$candidate"; then + echo "$candidate" + return 0 + fi + done + + return 1 +} + +ensure_user_writable_dir() { + local dir="$1" + + if mkdir -p "$dir" 2>/dev/null; then + return 0 + fi + + sudo mkdir -p "$dir" + sudo chown "$(id -u):$(id -g)" "$dir" +} + +ensure_user_owns_path() { + local path="$1" + + if [[ -e "$path" && ! -w "$path" ]]; then + sudo chown -R "$(id -u):$(id -g)" "$path" + fi +} + +sync_git_repo() { + local repo_url="$1" + local repo_dir="$2" + local branch="$3" + local repo_mode="$4" + local parent_dir + local active_branch + + parent_dir="$(dirname "$repo_dir")" + if [[ ! -d "$parent_dir" ]]; then + ensure_user_writable_dir "$parent_dir" + fi + + if [[ "$repo_mode" == "fresh" && -e "$repo_dir" ]]; then + log INFO "Removing existing repository for fresh checkout: $repo_dir" + rm -rf "$repo_dir" 2>/dev/null || sudo rm -rf "$repo_dir" + fi + + if [[ -d "$repo_dir/.git" ]]; then + ensure_user_owns_path "$repo_dir" + log INFO "Updating repository: $repo_dir" + git -C "$repo_dir" fetch --all --prune + active_branch="$branch" + if [[ -z "$active_branch" ]]; then + active_branch="$(git -C "$repo_dir" branch --show-current)" + fi + if [[ -z "$active_branch" ]]; then + active_branch="$(git -C "$repo_dir" symbolic-ref --quiet --short refs/remotes/origin/HEAD 2>/dev/null | sed 's|^origin/||')" + fi + if [[ -n "$active_branch" ]]; then + git -C "$repo_dir" checkout "$active_branch" + git -C "$repo_dir" pull --ff-only origin "$active_branch" + else + git -C "$repo_dir" pull --ff-only + fi + return 0 + fi + + if [[ -d "$repo_dir" && -z "$(ls -A "$repo_dir" 2>/dev/null)" ]]; then + rmdir "$repo_dir" 2>/dev/null || sudo rmdir "$repo_dir" + fi + + if [[ -e "$repo_dir" ]]; then + die "Path exists but is not a git repository: $repo_dir" + fi + + if [[ ! -w "$parent_dir" ]]; then + sudo mkdir -p "$repo_dir" + sudo chown "$(id -u):$(id -g)" "$repo_dir" + fi + + log INFO "Cloning repository $repo_url -> $repo_dir" + if [[ -n "$branch" ]]; then + git clone --branch "$branch" --single-branch "$repo_url" "$repo_dir" + else + git clone "$repo_url" "$repo_dir" + fi +} + +sync_support_repo() { + local repo_name="$1" + local repo_url="$2" + local repo_dir="$3" + + log INFO "Preparing support repository: $repo_name" + sync_git_repo "$repo_url" "$repo_dir" "" "$REPO_MODE" +} + +copy_tree_contents() { + local src_dir="$1" + local dst_dir="$2" + local label="$3" + + if [[ ! -d "$src_dir" ]]; then + log INFO "Skipping missing source for $label: $src_dir" + return 0 + fi + + mkdir -p "$dst_dir" + # Exclude .git to avoid permission issues on repeated runs + if command -v rsync >/dev/null 2>&1; then + rsync -a --exclude='.git' "$src_dir/" "$dst_dir/" + else + find "$src_dir" -maxdepth 1 -mindepth 1 ! -name '.git' \ + -exec cp -a {} "$dst_dir/" \; + fi + log INFO "Copied $label: $src_dir -> $dst_dir" +} + +apply_legacy_patches() { + local patch_dir="$1" + local patch_file + + [[ -n "$patch_dir" ]] || return 0 + + if [[ ! -d "$patch_dir" ]]; then + die "Legacy patch directory not found: $patch_dir" + fi + + shopt -s nullglob + local patches=("$patch_dir"/*.patch) + shopt -u nullglob + + if [[ ${#patches[@]} -eq 0 ]]; then + log INFO "No legacy patches found in $patch_dir" + return 0 + fi + + for patch_file in "${patches[@]}"; do + if git -C "$OPENSIM_DIR" apply --reverse --check "$patch_file" >/dev/null 2>&1; then + log INFO "Legacy patch already applied: $(basename "$patch_file")" + continue + fi + + log INFO "Applying legacy patch: $(basename "$patch_file")" + if ! git -C "$OPENSIM_DIR" apply "$patch_file"; then + git -C "$OPENSIM_DIR" apply --ignore-space-change --ignore-whitespace "$patch_file" \ + || die "Failed to apply legacy patch: $patch_file" + fi + done +} + +deploy_opensim_binaries() { + local src_bin="$OPENSIM_DIR/bin" + local deployed=0 + local sim_dir + + [[ -d "$src_bin" ]] || die "OpenSim build output missing: $src_bin" + + if [[ -d "$WORKDIR/robust/bin" ]]; then + copy_tree_contents "$src_bin" "$WORKDIR/robust/bin" "robust binaries" + deployed=1 + fi + + if [[ -d "$WORKDIR/standalone/bin" ]]; then + copy_tree_contents "$src_bin" "$WORKDIR/standalone/bin" "standalone binaries" + deployed=1 + fi + + shopt -s nullglob + for sim_dir in "$WORKDIR"/sim*; do + if [[ -d "$sim_dir/bin" ]]; then + copy_tree_contents "$src_bin" "$sim_dir/bin" "region binaries" + deployed=1 + fi + done + shopt -u nullglob + + if [[ "$deployed" -eq 0 ]]; then + log INFO "No target runtime directories found for binary deployment under $WORKDIR" + else + log INFO "OpenSim binaries deployed to available runtime directories" + fi +} + +install_dotnet8() { + need_cmd apt-get + need_cmd sudo + need_cmd wget + + if has_dotnet8_sdk; then + log INFO "dotnet SDK 8.x already installed" + return 0 + fi + + log INFO "Installing .NET SDK 8.0" + sudo apt-get update + sudo apt-get install -y apt-transport-https ca-certificates gnupg + + if ! apt-cache policy dotnet-sdk-8.0 | grep -q "Candidate:"; then + local version_id pkg_url pkg_file + version_id="$(. /etc/os-release && echo "${VERSION_ID}")" + pkg_url="https://packages.microsoft.com/config/ubuntu/${version_id}/packages-microsoft-prod.deb" + pkg_file="/tmp/packages-microsoft-prod.deb" + + log INFO "Adding Microsoft package repository for Ubuntu ${version_id}" + wget -q -O "$pkg_file" "$pkg_url" + sudo dpkg -i "$pkg_file" + rm -f "$pkg_file" + sudo apt-get update + fi + + sudo apt-get install -y dotnet-sdk-8.0 + + if has_dotnet8_sdk; then + log INFO "dotnet SDK 8.x installed successfully" + return 0 + fi + + die "dotnet SDK 8.x installation failed" +} + +install_opensim_deps() { + local tokens=( + git zip screen libgdiplus zlib1g libunwind8 libgssapi-krb5-2 libstdc++6 + "mariadb-client|default-mysql-client|mysql-client" + "mariadb-server|mysql-server" + "libgcc-s1|libgcc1" + "libssl3|libssl1.1" + "libicu74|libicu72|libicu71|libicu70|libicu67" + "liblttng-ust1|liblttng-ust0" + ) + local to_install=() + local token resolved + + need_cmd apt-get + need_cmd sudo + + for token in "${tokens[@]}"; do + if resolve_package_token "$token" >/dev/null 2>&1; then + resolved="$(resolve_package_token "$token")" + if ! dpkg -s "$resolved" >/dev/null 2>&1; then + to_install+=("$resolved") + fi + else + die "No installable package found for token: $token" + fi + done + + if [[ ${#to_install[@]} -eq 0 ]]; then + log INFO "OpenSim runtime dependencies already installed" + return 0 + fi + + log INFO "Installing OpenSim dependencies: ${to_install[*]}" + sudo apt-get update + sudo apt-get install -y "${to_install[@]}" +} + +ensure_mariadb_service() { + need_cmd sudo + + if ! command -v mariadb >/dev/null 2>&1; then + die "MariaDB client not found after dependency install" + fi + + if command -v systemctl >/dev/null 2>&1; then + if systemctl list-unit-files 2>/dev/null | grep -q '^mariadb\.service'; then + log INFO "Ensuring mariadb.service is enabled and running" + sudo systemctl enable --now mariadb + if ! systemctl is-active --quiet mariadb; then + die "MariaDB service could not be started" + fi + return 0 + fi + fi + + if command -v service >/dev/null 2>&1; then + if service --status-all 2>/dev/null | grep -q mariadb; then + log INFO "Ensuring MariaDB service is running" + sudo service mariadb start + return 0 + fi + fi + + log INFO "No controllable MariaDB service unit detected; skipping service activation" +} + +bootstrap_server() { + log INFO "Starting central server bootstrap" + prepare_ubuntu + install_opensim_deps + install_dotnet8 + ensure_mariadb_service + server_check + mark_server_prepared + log INFO "Central server bootstrap completed" +} + +copy_ini_if_missing() { + local bin_dir="$1" + local src_name="$2" + local dst_name="$3" + local src dst + + src="$bin_dir/$src_name" + dst="$bin_dir/$dst_name" + + [[ -d "$bin_dir" ]] || return 0 + + if [[ -f "$dst" ]]; then + log INFO "Keeping existing config: $dst" + return 0 + fi + + if [[ -f "$src" ]]; then + cp -a "$src" "$dst" + log INFO "Created config from template: $dst" + else + log INFO "Template not found, skipping: $src" + fi +} + +copy_example_if_missing() { + local bin_dir="$1" + local base_name="$2" + copy_ini_if_missing "$bin_dir" "${base_name}.example" "$base_name" +} + +set_ini_key_value() { + local file="$1" + local key="$2" + local value="$3" + local escaped + + [[ -f "$file" ]] || return 0 + escaped="${value//\\/\\\\}" + escaped="${escaped//&/\\&}" + sed -i -E "s|^([[:space:]]*${key}[[:space:]]*=[[:space:]]*).*$|\\1\"${escaped}\"|" "$file" +} + +set_architecture_include() { + local file="$1" + local include_value="$2" + + [[ -f "$file" ]] || return 0 + + if grep -qE '^[[:space:]]*Include-Architecture[[:space:]]*=' "$file"; then + sed -i -E "s|^([[:space:]]*Include-Architecture[[:space:]]*=[[:space:]]*).*$|\\1\"${include_value}\"|" "$file" + else + { + echo "" + echo "[Architecture]" + echo "Include-Architecture = \"${include_value}\"" + } >> "$file" + fi +} + +read_userinfo_credential() { + local file="$1" + local key="$2" + + [[ -f "$file" ]] || return 1 + awk -F '=' -v key="$key" ' + $1 ~ "^[[:space:]]*" key "[[:space:]]*$" { + sub(/^[[:space:]]+/, "", $2) + sub(/[[:space:]]+$/, "", $2) + print $2 + exit + } + ' "$file" +} + +configure_opensim_runtime() { + local sim_dir cfg_user cfg_pass userinfo_file sim_arch + + log INFO "Configuring OpenSim runtime ini files" + + copy_ini_if_missing "$WORKDIR/robust/bin" "Robust.HG.ini.example" "Robust.ini" + copy_ini_if_missing "$WORKDIR/robust/bin" "Robust.ini.example" "Robust.ini" + copy_ini_if_missing "$WORKDIR/robust/bin" "MoneyServer.ini.example" "MoneyServer.ini" + copy_example_if_missing "$WORKDIR/robust/bin/config-include" "GridCommon.ini" + copy_example_if_missing "$WORKDIR/robust/bin/config-include" "osslEnable.ini" + + copy_ini_if_missing "$WORKDIR/standalone/bin" "OpenSim.ini.example" "OpenSim.ini" + copy_example_if_missing "$WORKDIR/standalone/bin/config-include" "StandaloneCommon.ini" + copy_example_if_missing "$WORKDIR/standalone/bin/config-include" "osslEnable.ini" + + sim_arch="config-include/Grid.ini" + if [[ "$PROFILE" == "standalone" ]]; then + sim_arch="config-include/Standalone.ini" + fi + + shopt -s nullglob + for sim_dir in "$WORKDIR"/sim*/bin; do + copy_ini_if_missing "$sim_dir" "OpenSim.ini.example" "OpenSim.ini" + copy_example_if_missing "$sim_dir/config-include" "GridCommon.ini" + copy_example_if_missing "$sim_dir/config-include" "osslEnable.ini" + set_architecture_include "$sim_dir/OpenSim.ini" "$sim_arch" + done + shopt -u nullglob + + cfg_user="$DB_USER" + cfg_pass="$DB_PASS" + userinfo_file="$WORKDIR/UserInfo.ini" + + if [[ -z "$cfg_user" ]]; then + cfg_user="$(read_userinfo_credential "$userinfo_file" "DB_Benutzername" || true)" + fi + if [[ -z "$cfg_pass" ]]; then + cfg_pass="$(read_userinfo_credential "$userinfo_file" "DB_Passwort" || true)" + fi + + if [[ -n "$cfg_user" && -n "$cfg_pass" ]]; then + set_ini_key_value "$WORKDIR/robust/bin/MoneyServer.ini" "username" "$cfg_user" + set_ini_key_value "$WORKDIR/robust/bin/MoneyServer.ini" "password" "$cfg_pass" + log INFO "Updated MoneyServer.ini database credentials from configured values" + else + log INFO "MoneyServer.ini credentials unchanged (no DB credentials available in parameters/UserInfo.ini)" + fi + + log INFO "OpenSim runtime ini configuration completed" +} + +sql_escape() { + local s="$1" + s="${s//\\/\\\\}" + s="${s//\'/\'\'}" + printf '%s' "$s" +} + +run_mariadb_sql() { + local sql="$1" + + if [[ -n "$DB_ROOT_PASS" ]]; then + mariadb -u"$DB_ROOT_USER" -p"$DB_ROOT_PASS" -e "$sql" + else + mariadb -u"$DB_ROOT_USER" -e "$sql" + fi +} + +discover_sim_databases() { + local sim_bin sim_name + local sim_dbs=() + + shopt -s nullglob + for sim_bin in "$WORKDIR"/sim*/bin; do + sim_name="$(basename "$(dirname "$sim_bin")")" + sim_dbs+=("$sim_name") + done + shopt -u nullglob + + printf '%s\n' "${sim_dbs[@]}" +} + +write_db_credentials_file() { + local file="$WORKDIR/UserInfo.ini" + + mkdir -p "$WORKDIR" 2>/dev/null || sudo mkdir -p "$WORKDIR" + if [[ ! -f "$file" ]]; then + : > "$file" 2>/dev/null || sudo touch "$file" + fi + + { + echo "[DatabaseData]" + echo "DB_Benutzername = $DB_USER" + echo "DB_Passwort = $DB_PASS" + } | sudo tee "$file" >/dev/null + + log INFO "Database credentials written: $file" +} + +configure_database() { + local escaped_user escaped_pass db_name + local db_ident + local sim_db_list + + need_cmd mariadb + need_cmd sudo + ensure_mariadb_service + + [[ -n "$DB_USER" ]] || die "--db-user is required for configure-database" + [[ -n "$DB_PASS" ]] || die "--db-pass is required for configure-database" + + escaped_user="$(sql_escape "$DB_USER")" + escaped_pass="$(sql_escape "$DB_PASS")" + + if ! run_mariadb_sql "SELECT 1" >/dev/null 2>&1; then + die "Cannot connect to MariaDB as '$DB_ROOT_USER'. Check --db-root-user/--db-root-pass and server state." + fi + + log INFO "Configuring MariaDB databases and grants" + + if [[ -d "$WORKDIR/robust" ]]; then + run_mariadb_sql "CREATE DATABASE IF NOT EXISTS robust CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;" + log INFO "Ensured database: robust" + fi + + sim_db_list="$(discover_sim_databases)" + if [[ -n "$sim_db_list" ]]; then + while IFS= read -r db_name; do + [[ -n "$db_name" ]] || continue + db_ident="\`$db_name\`" + run_mariadb_sql "CREATE DATABASE IF NOT EXISTS $db_ident CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;" + log INFO "Ensured database: $db_name" + done <<< "$sim_db_list" + fi + + run_mariadb_sql "CREATE USER IF NOT EXISTS '$escaped_user'@'localhost' IDENTIFIED BY '$escaped_pass';" + run_mariadb_sql "ALTER USER '$escaped_user'@'localhost' IDENTIFIED BY '$escaped_pass';" + + if [[ -d "$WORKDIR/robust" ]]; then + run_mariadb_sql "GRANT ALL PRIVILEGES ON robust.* TO '$escaped_user'@'localhost';" + fi + + if [[ -n "$sim_db_list" ]]; then + while IFS= read -r db_name; do + [[ -n "$db_name" ]] || continue + db_ident="\`$db_name\`" + run_mariadb_sql "GRANT ALL PRIVILEGES ON $db_ident.* TO '$escaped_user'@'localhost';" + done <<< "$sim_db_list" + fi + + run_mariadb_sql "FLUSH PRIVILEGES;" + + write_db_credentials_file + log INFO "MariaDB database setup completed" +} + +server_check() { + local fail=0 + local os_id="" + local free_kb="0" + local missing_pkgs=() + local required_janus_pkgs=( + build-essential cmake git pkg-config automake libtool gengetopt + libmicrohttpd-dev libjansson-dev libssl-dev libsofia-sip-ua-dev libglib2.0-dev + libopus-dev libogg-dev libcurl4-openssl-dev liblua5.3-dev libconfig-dev + libwebsockets-dev libsrtp2-dev libnice-dev libsqlite3-dev + ) + local required_opensim_tokens=( + git zip screen libgdiplus zlib1g libunwind8 libgssapi-krb5-2 libstdc++6 + "mariadb-client|default-mysql-client|mysql-client" + "mariadb-server|mysql-server" + "libgcc-s1|libgcc1" + "libssl3|libssl1.1" + "libicu74|libicu72|libicu71|libicu70|libicu67" + "liblttng-ust1|liblttng-ust0" + ) + local pkg token + + log INFO "Running server readiness check" + + if [[ -f /etc/os-release ]]; then + # shellcheck disable=SC1091 + source /etc/os-release + os_id="${ID:-unknown}" + log INFO "Detected OS: ${PRETTY_NAME:-unknown}" + fi + + if [[ "$os_id" != "ubuntu" && "$os_id" != "debian" ]]; then + log ERROR "Unsupported OS for this automation: $os_id" + fail=1 + fi + + if command -v sudo >/dev/null 2>&1; then + if sudo -n true >/dev/null 2>&1; then + log INFO "sudo non-interactive check: OK" + else + log INFO "sudo available but password will be required" + fi + else + log ERROR "sudo not found" + fail=1 + fi + + if command -v apt-get >/dev/null 2>&1; then + log INFO "apt-get available" + else + log ERROR "apt-get missing" + fail=1 + fi + + if command -v curl >/dev/null 2>&1; then + if curl -Is https://github.com >/dev/null 2>&1; then + log INFO "Network check to github.com: OK" + else + log ERROR "Network check to github.com failed" + fail=1 + fi + else + log ERROR "curl missing" + fail=1 + fi + + free_kb="$(df -Pk /opt | awk 'NR==2 {print $4}')" + if [[ -n "$free_kb" && "$free_kb" -gt 2097152 ]]; then + log INFO "Disk space in /opt is sufficient" + else + log ERROR "Low disk space in /opt (need at least 2GB free)" + fail=1 + fi + + for pkg in "${required_janus_pkgs[@]}"; do + if ! dpkg -s "$pkg" >/dev/null 2>&1; then + missing_pkgs+=("$pkg") + fi + done + + for token in "${required_opensim_tokens[@]}"; do + if ! resolve_package_token "$token" >/dev/null 2>&1; then + missing_pkgs+=("$token") + fi + done + + if [[ ${#missing_pkgs[@]} -eq 0 ]]; then + log INFO "All required Janus/OpenSim dependency packages are installed" + else + log ERROR "Missing packages: ${missing_pkgs[*]}" + log INFO "Run: sudo apt-get update && sudo apt-get install -y ${missing_pkgs[*]}" + fail=1 + fi + + if has_dotnet8_sdk; then + log INFO "dotnet SDK 8.x found" + else + log ERROR "dotnet SDK 8.x missing (required for OpenSim build/runtime)" + fail=1 + fi + + if command -v mysqldump >/dev/null 2>&1; then + log INFO "mysqldump found (MariaDB backup tooling ready)" + else + log ERROR "mysqldump missing (MariaDB client tools are required)" + fail=1 + fi + + if [[ "$fail" -eq 0 ]]; then + log INFO "Server readiness check PASSED" + return 0 + fi + + die "Server readiness check FAILED" +} + +upsert_key() { + local file="$1" + local key="$2" + local value="$3" + + if grep -Eq "^[[:space:]]*;?[[:space:]]*${key}[[:space:]]*=" "$file"; then + sudo sed -i -E "s|^[[:space:]]*;?[[:space:]]*(${key})[[:space:]]*=.*|\1 = $value|" "$file" + else + echo "$key = $value" | sudo tee -a "$file" >/dev/null + fi +} + +generate_alnum_token() { + local length="${1:-32}" + if command -v openssl >/dev/null 2>&1; then + openssl rand -hex "$((length / 2))" | cut -c1-"$length" + return + fi + tr -dc 'A-Za-z0-9' /dev/null || sudo mkdir -p "$WORKDIR" + cat </dev/null +PREPARED_AT=$(date '+%Y-%m-%d %H:%M:%S') +WORKDIR=$WORKDIR +PROFILE=$PROFILE +DOTNET_REQUIRED=8.0 +EOF + log INFO "Server bootstrap marker created: $marker" +} + +ensure_server_prepared() { + local marker + marker="$(server_prep_marker)" + [[ -f "$marker" ]] || die "Server preinstallation missing. Run install action bootstrap-server first." +} + +compile_janus() { + need_cmd apt-get + need_cmd git + + log INFO "Installing Janus build dependencies" + sudo apt-get update + sudo apt-get install -y \ + build-essential cmake git pkg-config automake libtool gengetopt \ + libmicrohttpd-dev libjansson-dev libssl-dev libsofia-sip-ua-dev libglib2.0-dev \ + libopus-dev libogg-dev libcurl4-openssl-dev liblua5.3-dev libconfig-dev \ + libwebsockets-dev libsrtp2-dev libnice-dev libsqlite3-dev + + if [[ -d "$JANUS_SRC/.git" ]]; then + log INFO "Updating existing Janus source at $JANUS_SRC" + git -C "$JANUS_SRC" pull --ff-only + else + if [[ -e "$JANUS_SRC" ]]; then + die "Janus source path exists but is not a git repository: $JANUS_SRC" + fi + log INFO "Cloning Janus source to $JANUS_SRC" + git clone https://github.com/meetecho/janus-gateway.git "$JANUS_SRC" + fi + + log INFO "Building Janus" + ( + cd "$JANUS_SRC" + sh autogen.sh + ./configure --prefix="$JANUS_PREFIX" + make -j"$(nproc)" + sudo make install + sudo make configs + ) + + log INFO "Janus compiled and installed to $JANUS_PREFIX" +} + +configure_janus() { + local conf_dir core_cfg http_cfg api_secret admin_secret + conf_dir="$JANUS_PREFIX/etc/janus" + + [[ -d "$conf_dir" ]] || die "Janus config dir not found: $conf_dir" + + core_cfg="$conf_dir/janus.jcfg" + [[ -f "$core_cfg" ]] || core_cfg="$conf_dir/janus.cfg" + + http_cfg="$conf_dir/janus.transport.http.jcfg" + [[ -f "$http_cfg" ]] || http_cfg="$conf_dir/janus.transport.http.cfg" + + [[ -f "$core_cfg" ]] || die "Missing janus core config in $conf_dir" + [[ -f "$http_cfg" ]] || die "Missing janus HTTP transport config in $conf_dir" + + api_secret="$API_SECRET" + [[ -n "$api_secret" ]] || api_secret="$(generate_alnum_token 32)" + + admin_secret="$ADMIN_SECRET" + [[ -n "$admin_secret" ]] || admin_secret="$(generate_alnum_token 32)" + + sudo cp "$core_cfg" "$core_cfg.bak-$(date +%Y%m%d-%H%M%S)" + sudo cp "$http_cfg" "$http_cfg.bak-$(date +%Y%m%d-%H%M%S)" + + upsert_key "$core_cfg" "rtp_port_range" "\"$RTP_RANGE\"" + upsert_key "$http_cfg" "http" "true" + upsert_key "$http_cfg" "port" "$HTTP_PORT" + upsert_key "$http_cfg" "api_secret" "\"$api_secret\"" + + if [[ "$ENABLE_ADMIN" == "true" ]]; then + upsert_key "$http_cfg" "admin_http" "true" + upsert_key "$http_cfg" "admin_port" "$ADMIN_PORT" + upsert_key "$http_cfg" "admin_secret" "\"$admin_secret\"" + else + upsert_key "$http_cfg" "admin_http" "false" + fi + + local snippet_file + snippet_file="$WORKDIR/opensim-janus-config.generated.ini" + + mkdir -p "$WORKDIR" 2>/dev/null || sudo mkdir -p "$WORKDIR" + cat </dev/null +[JanusWebRtcVoice] + JanusGatewayURI = http://$PUBLIC_HOST:$HTTP_PORT/janus + APIToken = $api_secret +EOF + + if [[ "$ENABLE_ADMIN" == "true" ]]; then + cat </dev/null + JanusGatewayAdminURI = http://$PUBLIC_HOST:$ADMIN_PORT/admin + AdminAPIToken = $admin_secret +EOF + fi + + cat <<'EOF' | sudo tee -a "$snippet_file" >/dev/null + MessageDetails = false +EOF + + cat </dev/null +JANUS_PREFIX=$JANUS_PREFIX +HTTP_PORT=$HTTP_PORT +ADMIN_PORT=$ADMIN_PORT +ENABLE_ADMIN=$ENABLE_ADMIN +RTP_RANGE=$RTP_RANGE +PUBLIC_HOST=$PUBLIC_HOST +EOF + + log INFO "Janus configured. Marker created: $JANUS_PREFIX/.osmtool_janus_ready" +} + +prepare_ubuntu() { + need_cmd apt-get + log INFO "Preparing Ubuntu server" + sudo apt-get update + sudo apt-get -y upgrade + sudo apt-get -y install curl git screen unzip jq wget + log INFO "Ubuntu base packages installed" +} + +install_opensim() { + need_cmd git + need_cmd dotnet + need_cmd bash + + log INFO "Starting OpenSim install/build flow" + log INFO "OpenSim repository: $OPENSIM_REPO" + log INFO "OpenSim branch: $OPENSIM_BRANCH" + log INFO "OpenSim directory: $OPENSIM_DIR" + + sync_git_repo "$OPENSIM_REPO" "$OPENSIM_DIR" "$OPENSIM_BRANCH" "$REPO_MODE" + ensure_user_owns_path "$OPENSIM_DIR" + + if [[ -n "$LEGACY_PATCH_DIR" ]]; then + apply_legacy_patches "$LEGACY_PATCH_DIR" + fi + + sync_support_repo "opensim-tsassets" "$TSASSETS_REPO" "$TSASSETS_DIR" + sync_support_repo "opensimcurrencyserver" "$CURRENCY_REPO" "$CURRENCY_DIR" + sync_support_repo "os-data-backup" "$DATA_BACKUP_REPO" "$DATA_BACKUP_DIR" + + copy_tree_contents "$TSASSETS_DIR/bin" "$OPENSIM_DIR/bin" "tsassets bin content" + copy_tree_contents "$TSASSETS_DIR/OpenSim" "$OPENSIM_DIR/OpenSim" "tsassets OpenSim content" + copy_tree_contents "$CURRENCY_DIR/addon-modules" "$OPENSIM_DIR/addon-modules" "currency addon modules" + copy_tree_contents "$CURRENCY_DIR/bin" "$OPENSIM_DIR/bin" "currency bin content" + copy_tree_contents "$DATA_BACKUP_DIR" "$OPENSIM_DIR/addon-modules/os-data-backup" "os-data-backup content" + + log INFO "Running OpenSim prebuild" + ( + cd "$OPENSIM_DIR" + bash runprebuild.sh + dotnet build --configuration Release OpenSim.sln + ) + + [[ -f "$OPENSIM_DIR/bin/OpenSim.dll" ]] || die "OpenSim build output missing: $OPENSIM_DIR/bin/OpenSim.dll" + [[ -f "$OPENSIM_DIR/bin/Robust.dll" ]] || die "OpenSim build output missing: $OPENSIM_DIR/bin/Robust.dll" + + if [[ "$DEPLOY_BINARIES" == "true" ]]; then + deploy_opensim_binaries + configure_opensim_runtime + else + log INFO "Binary deployment skipped by configuration" + fi + + log INFO "OpenSim install/build flow completed" +} + +install_janus() { + compile_janus + configure_janus +} + +WORKDIR="$DEFAULT_WORKDIR" +ACTION="" +PROFILE="grid-sim" +REPO_MODE="${REPO_MODE:-update}" +OPENSIM_REPO="${OPENSIM_REPO:-https://github.com/opensim/opensim.git}" +OPENSIM_BRANCH="${OPENSIM_BRANCH:-master}" +OPENSIM_DIR="${OPENSIM_DIR:-$WORKDIR/opensim}" +TSASSETS_REPO="${TSASSETS_REPO:-https://github.com/ManfredAabye/opensim-tsassets.git}" +TSASSETS_DIR="${TSASSETS_DIR:-$WORKDIR/opensim-tsassets}" +CURRENCY_REPO="${CURRENCY_REPO:-https://github.com/ManfredAabye/opensimcurrencyserver-dotnet.git}" +CURRENCY_DIR="${CURRENCY_DIR:-$WORKDIR/opensimcurrencyserver}" +DATA_BACKUP_REPO="${DATA_BACKUP_REPO:-https://github.com/ManfredAabye/os-data-backup.git}" +DATA_BACKUP_DIR="${DATA_BACKUP_DIR:-$WORKDIR/os-data-backup}" +DEPLOY_BINARIES="${DEPLOY_BINARIES:-true}" +LEGACY_PATCH_DIR="${LEGACY_PATCH_DIR:-}" +DB_ROOT_USER="${DB_ROOT_USER:-root}" +DB_ROOT_PASS="${DB_ROOT_PASS:-}" +DB_USER="${DB_USER:-opensimuser}" +DB_PASS="${DB_PASS:-}" +JANUS_PREFIX="${JANUS_PREFIX:-/opt/janus}" +JANUS_SRC="${JANUS_SRC:-$WORKDIR/janus-gateway}" +PUBLIC_HOST="${PUBLIC_HOST:-127.0.0.1}" +HTTP_PORT="${HTTP_PORT:-8088}" +ADMIN_PORT="${ADMIN_PORT:-7088}" +RTP_RANGE="${RTP_RANGE:-10000-10200}" +ENABLE_ADMIN="${ENABLE_ADMIN:-false}" +API_SECRET="${API_SECRET:-}" +ADMIN_SECRET="${ADMIN_SECRET:-}" + +while [[ $# -gt 0 ]]; do + case "$1" in + --workdir) WORKDIR="$2"; shift 2 ;; + --profile) PROFILE="$2"; shift 2 ;; + --action) ACTION="$2"; shift 2 ;; + --opensim-dir) OPENSIM_DIR="$2"; shift 2 ;; + --opensim-repo) OPENSIM_REPO="$2"; shift 2 ;; + --opensim-branch) OPENSIM_BRANCH="$2"; shift 2 ;; + --repo-mode) REPO_MODE="$2"; shift 2 ;; + --tsassets-dir) TSASSETS_DIR="$2"; shift 2 ;; + --currency-dir) CURRENCY_DIR="$2"; shift 2 ;; + --data-backup-dir) DATA_BACKUP_DIR="$2"; shift 2 ;; + --deploy-binaries) DEPLOY_BINARIES="$2"; shift 2 ;; + --legacy-patch-dir) LEGACY_PATCH_DIR="$2"; shift 2 ;; + --db-root-user) DB_ROOT_USER="$2"; shift 2 ;; + --db-root-pass) DB_ROOT_PASS="$2"; shift 2 ;; + --db-user) DB_USER="$2"; shift 2 ;; + --db-pass) DB_PASS="$2"; shift 2 ;; + --janus-prefix) JANUS_PREFIX="$2"; shift 2 ;; + --janus-src) JANUS_SRC="$2"; shift 2 ;; + --public-host) PUBLIC_HOST="$2"; shift 2 ;; + --http-port) HTTP_PORT="$2"; shift 2 ;; + --admin-port) ADMIN_PORT="$2"; shift 2 ;; + --rtp-range) RTP_RANGE="$2"; shift 2 ;; + --enable-admin) ENABLE_ADMIN="$2"; shift 2 ;; + --api-secret) API_SECRET="$2"; shift 2 ;; + --admin-secret) ADMIN_SECRET="$2"; shift 2 ;; + --help|-h) usage; exit 0 ;; + *) die "Unknown option: $1" ;; + esac +done + +[[ -n "$ACTION" ]] || die "Missing --action" +validate_profile "$PROFILE" +ensure_profile_action_allowed install "$PROFILE" "$ACTION" + +case "$REPO_MODE" in + update|fresh) ;; + *) die "Unsupported repo mode: $REPO_MODE" ;; +esac + +case "$DEPLOY_BINARIES" in + true|false) ;; + *) die "--deploy-binaries must be true or false" ;; +esac + +case "$ACTION" in + install-opensim|configure-opensim|configure-database|compile-janus|configure-janus|install-janus) + ensure_server_prepared + ;; +esac + +log INFO "Using workdir: $WORKDIR" +log INFO "Using profile: $PROFILE" +log INFO "Repo mode: $REPO_MODE" +log INFO "OpenSim dir: $OPENSIM_DIR" +log INFO "Janus prefix: $JANUS_PREFIX" + +case "$ACTION" in + bootstrap-server) bootstrap_server ;; + server-check) server_check ;; + prepare-ubuntu) prepare_ubuntu ;; + install-opensim-deps) install_opensim_deps ;; + install-dotnet8) install_dotnet8 ;; + install-opensim) install_opensim ;; + configure-opensim) configure_opensim_runtime ;; + configure-database) configure_database ;; + compile-janus) compile_janus ;; + configure-janus) configure_janus ;; + install-janus) install_janus ;; + *) die "Unsupported action: $ACTION" ;; +esac diff --git a/modular/osmtool_report.sh b/modular/osmtool_report.sh new file mode 100644 index 0000000..01265a0 --- /dev/null +++ b/modular/osmtool_report.sh @@ -0,0 +1,293 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +# shellcheck source=./osmtool_core.sh +source "$SCRIPT_DIR/osmtool_core.sh" + +usage() { + cat <<'EOF' +Usage: + osmtool_report.sh [--workdir ] --action + [--output ] [--expected-screens ] [--ports ] + [--db-user ] [--db-pass ] [--db-name ] [--log-file ] + +Examples: + osmtool_report.sh --action generate --workdir /opt --db-user opensim --db-pass secret --db-name robust + osmtool_report.sh --action generate --workdir /opt --output /opt/reports/manual_report.txt +EOF + print_usage_common +} + +timestamp_compact() { + date '+%Y%m%d_%H%M%S' +} + +timestamp_iso() { + date '+%Y-%m-%d %H:%M:%S' +} + +split_csv() { + local csv="$1" + local token + local IFS=',' + read -r -a _TOKENS <<< "$csv" + for token in "${_TOKENS[@]}"; do + token="${token## }" + token="${token%% }" + [[ -n "$token" ]] && printf '%s\n' "$token" + done +} + +reports_dir() { + printf '%s' "$WORKDIR/reports" +} + +ensure_reports_dir() { + local dir + dir="$(reports_dir)" + + if mkdir -p "$dir" 2>/dev/null; then + return 0 + fi + + sudo mkdir -p "$dir" + sudo chown "$(id -u):$(id -g)" "$dir" +} + +latest_file() { + local dir="$1" + local pattern="$2" + + [[ -d "$dir" ]] || return 0 + find "$dir" -maxdepth 1 -type f -name "$pattern" -printf '%T@ %p\n' 2>/dev/null | \ + sort -rn | head -1 | cut -d' ' -f2- +} + +format_file_age_hours() { + local path="$1" + local now ts age + + [[ -f "$path" ]] || { + printf 'n/a' + return 0 + } + + now="$(date +%s)" + ts="$(stat -c %Y "$path" 2>/dev/null || echo 0)" + if [[ "$ts" -le 0 ]]; then + printf 'n/a' + return 0 + fi + + age=$(( (now - ts) / 3600 )) + printf '%sh' "$age" +} + +screen_status_lines() { + local screen_name + + if [[ -z "$EXPECTED_SCREENS" ]]; then + printf 'screen: skipped\n' + return 0 + fi + + while IFS= read -r screen_name; do + [[ -n "$screen_name" ]] || continue + if screen -list 2>/dev/null | grep -qE "\\.${screen_name}[[:space:]]"; then + printf 'screen:%s=up\n' "$screen_name" + else + printf 'screen:%s=down\n' "$screen_name" + fi + done < <(split_csv "$EXPECTED_SCREENS") +} + +port_status_lines() { + local port + local use_ss=0 + + if [[ -z "$PORTS" ]]; then + printf 'port: skipped\n' + return 0 + fi + + if command -v ss >/dev/null 2>&1; then + use_ss=1 + elif ! command -v netstat >/dev/null 2>&1; then + printf 'port: unavailable (no ss/netstat)\n' + return 0 + fi + + while IFS= read -r port; do + [[ -n "$port" ]] || continue + if [[ "$use_ss" -eq 1 ]]; then + if ss -ltnH | awk '{print $4}' | grep -qE "[:.]${port}$"; then + printf 'port:%s=listen\n' "$port" + else + printf 'port:%s=closed\n' "$port" + fi + else + if netstat -ltn 2>/dev/null | awk '{print $4}' | grep -qE "[:.]${port}$"; then + printf 'port:%s=listen\n' "$port" + else + printf 'port:%s=closed\n' "$port" + fi + fi + done < <(split_csv "$PORTS") +} + +database_status() { + if [[ -z "$DB_USER" || -z "$DB_NAME" ]]; then + printf 'db: skipped\n' + return 0 + fi + + if ! command -v mariadb >/dev/null 2>&1; then + printf 'db:%s=client-missing\n' "$DB_NAME" + return 0 + fi + + if [[ -n "$DB_PASS" ]]; then + if mariadb -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" -e "SELECT 1" >/dev/null 2>&1; then + printf 'db:%s=ok\n' "$DB_NAME" + else + printf 'db:%s=fail\n' "$DB_NAME" + fi + else + if mariadb -u"$DB_USER" "$DB_NAME" -e "SELECT 1" >/dev/null 2>&1; then + printf 'db:%s=ok\n' "$DB_NAME" + else + printf 'db:%s=fail\n' "$DB_NAME" + fi + fi +} + +log_warning_count() { + local file="$1" + [[ -f "$file" ]] || { + printf 'n/a' + return 0 + } + + grep -Eic 'error|warn|exception|fatal' "$file" 2>/dev/null || printf '0' +} + +default_screens_for_profile() { + case "$1" in + grid-sim) echo "robustserver,sim1" ;; + robust) echo "robustserver" ;; + standalone) echo "standalone" ;; + esac +} + +default_ports_for_profile() { + case "$1" in + grid-sim) echo "8002,9000,9001,8088" ;; + robust) echo "8002,9000,9001" ;; + standalone) echo "9000" ;; + esac +} + +generate_report() { + local out report_title log_target + local newest_sql newest_oar newest_rollback + + ensure_reports_dir + + if [[ -n "$OUTPUT_FILE" ]]; then + out="$OUTPUT_FILE" + else + out="$(reports_dir)/osm_report_$(timestamp_compact).txt" + fi + + newest_sql="$(latest_file "$WORKDIR/backup" 'robust_*.sql*')" + newest_oar="$(latest_file "$WORKDIR/backup" '*.oar')" + newest_rollback="$(latest_file "$WORKDIR/rollback" '*.manifest')" + log_target="$LOG_FILE_PATH" + if [[ -z "$log_target" ]]; then + log_target="$ROOT_DIR/osmtool_backup.log" + fi + + report_title="OSMTool Daily Status Report" + + { + printf '%s\n' "$report_title" + printf 'generated_at: %s\n' "$(timestamp_iso)" + printf 'profile: %s\n' "$PROFILE" + printf 'workdir: %s\n' "$WORKDIR" + printf '\n' + printf '[runtime]\n' + screen_status_lines + port_status_lines + database_status + printf '\n' + printf '[backup]\n' + printf 'sql_latest: %s\n' "${newest_sql:-none}" + printf 'sql_age: %s\n' "$(format_file_age_hours "${newest_sql:-}")" + printf 'oar_latest: %s\n' "${newest_oar:-none}" + printf 'oar_age: %s\n' "$(format_file_age_hours "${newest_oar:-}")" + printf '\n' + printf '[rollback]\n' + printf 'latest_manifest: %s\n' "${newest_rollback:-none}" + printf 'manifest_age: %s\n' "$(format_file_age_hours "${newest_rollback:-}")" + printf '\n' + printf '[logs]\n' + printf 'file: %s\n' "$log_target" + printf 'warning_matches: %s\n' "$(log_warning_count "$log_target")" + } > "$out" + + log INFO "Report generated: $out" + cat "$out" +} + +WORKDIR="$DEFAULT_WORKDIR" +ACTION="" +PROFILE="grid-sim" +OUTPUT_FILE="" +EXPECTED_SCREENS="" +PORTS="" +DB_USER="" +DB_PASS="" +DB_NAME="" +LOG_FILE_PATH="" + +while [[ $# -gt 0 ]]; do + case "$1" in + --workdir) WORKDIR="$2"; shift 2 ;; + --profile) PROFILE="$2"; shift 2 ;; + --action) ACTION="$2"; shift 2 ;; + --output) OUTPUT_FILE="$2"; shift 2 ;; + --expected-screens) EXPECTED_SCREENS="$2"; shift 2 ;; + --ports) PORTS="$2"; shift 2 ;; + --db-user) DB_USER="$2"; shift 2 ;; + --db-pass) DB_PASS="$2"; shift 2 ;; + --db-name) DB_NAME="$2"; shift 2 ;; + --log-file) LOG_FILE_PATH="$2"; shift 2 ;; + --help|-h) usage; exit 0 ;; + *) die "Unknown option: $1" ;; + esac +done + +[[ -n "$ACTION" ]] || die "Missing --action" +validate_profile "$PROFILE" +ensure_profile_action_allowed report "$PROFILE" "$ACTION" + +if [[ -z "$EXPECTED_SCREENS" ]]; then + EXPECTED_SCREENS="$(default_screens_for_profile "$PROFILE")" +elif [[ "$EXPECTED_SCREENS" == "none" ]]; then + EXPECTED_SCREENS="" +fi + +if [[ -z "$PORTS" ]]; then + PORTS="$(default_ports_for_profile "$PROFILE")" +elif [[ "$PORTS" == "none" ]]; then + PORTS="" +fi + +log INFO "Using profile: $PROFILE" + +case "$ACTION" in + generate) generate_report ;; + *) die "Unsupported action: $ACTION" ;; +esac \ No newline at end of file diff --git a/modular/osmtool_restore.sh b/modular/osmtool_restore.sh new file mode 100644 index 0000000..a1ddbc4 --- /dev/null +++ b/modular/osmtool_restore.sh @@ -0,0 +1,260 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck source=./osmtool_core.sh +source "$SCRIPT_DIR/osmtool_core.sh" + +usage() { + cat <<'EOF' +Usage: + osmtool_restore.sh [--workdir ] --action + [--db-user ] [--db-pass ] [--db-name ] + [--region ] [--session ] [--file ] + [--dry-run ] + +Examples: + osmtool_restore.sh --action list-backups --workdir /opt + osmtool_restore.sh --action db-restore --db-user opensim --db-pass secret --db-name robust --file /opt/backup/robust_robust_20260401_120000.sql +EOF + print_usage_common +} + +backup_dir() { + printf '%s' "$WORKDIR/backup" +} + +ensure_backup_dir() { + local dir + dir="$(backup_dir)" + + if [[ ! -d "$dir" ]]; then + die "Backup directory does not exist: $dir" + fi + + return 0 +} + +list_backups() { + local dir + dir="$(backup_dir)" + + if [[ ! -d "$dir" ]]; then + log INFO "No backup directory found: $dir" + return 0 + fi + + log INFO "SQL Backups:" + find "$dir" -maxdepth 1 -name "robust_*.sql*" -type f -printf '%T@ %p\n' 2>/dev/null | \ + sort -rn | cut -d' ' -f2- | head -5 || true + + log INFO "OAR Backups:" + find "$dir" -maxdepth 1 -name "*.oar" -type f -printf '%T@ %p\n' 2>/dev/null | \ + sort -rn | cut -d' ' -f2- | head -5 || true +} + +resolve_default_session() { + if [[ -n "$SESSION_NAME" ]]; then + printf '%s' "$SESSION_NAME" + return 0 + fi + + if [[ -n "$REGION" ]]; then + printf '%s' "$REGION" + return 0 + fi + + printf 'sim1' +} + +screen_session_exists() { + local name="$1" + screen -list 2>/dev/null | grep -qE "\\.${name}[[:space:]]" +} + +send_to_screen() { + local session_name="$1" + local command_text="$2" + + screen -S "$session_name" -p 0 -X stuff "${command_text}^M" +} + +validate_sql_file() { + local file="$1" + + if [[ ! -f "$file" ]]; then + die "SQL backup file not found: $file" + fi + + # Check for gzip compression + if [[ "$file" =~ \.gz$ ]]; then + need_cmd zcat + return 0 + fi + + return 0 +} + +validate_oar_file() { + local file="$1" + + if [[ ! -f "$file" ]]; then + die "OAR backup file not found: $file" + fi + + if [[ ! "$file" =~ \.oar$ ]]; then + die "File does not appear to be an OAR backup: $file" + fi + + return 0 +} + +db_restore_mysql() { + local file sql_cmd + + [[ -n "$DB_USER" && -n "$DB_PASS" && -n "$DB_NAME" ]] || die "db-restore requires --db-user --db-pass --db-name" + [[ -n "$FILE" ]] || die "db-restore requires --file" + + file="$FILE" + validate_sql_file "$file" + + need_cmd mysql + + if [[ "$file" =~ \.gz$ ]]; then + sql_cmd="zcat '$file' | mysql -u'$DB_USER' -p'$DB_PASS' '$DB_NAME'" + else + sql_cmd="mysql -u'$DB_USER' -p'$DB_PASS' '$DB_NAME' < '$file'" + fi + + log INFO "Database restore command:" + log INFO "$sql_cmd" + + if [[ "$DRY_RUN" == "true" ]]; then + log INFO "[DRY-RUN] Not executing database restore" + return 0 + fi + + log INFO "Restoring database from: $file" + if [[ "$file" =~ \.gz$ ]]; then + zcat "$file" | mysql -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" || die "Database restore failed" + else + mysql -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" < "$file" || die "Database restore failed" + fi + + log INFO "Database restore completed successfully from: $file" +} + +db_restore() { + db_restore_mysql +} + +oar_restore() { + local session_name file cmd + + [[ -n "$REGION" ]] || die "oar-restore requires --region" + [[ -n "$FILE" ]] || die "oar-restore requires --file" + + file="$FILE" + validate_oar_file "$file" + + session_name="$(resolve_default_session)" + + if ! screen_session_exists "$session_name"; then + die "Screen session not running: $session_name" + fi + + cmd="load oar $file" + log INFO "OAR restore command:" + log INFO "$cmd" + + if [[ "$DRY_RUN" == "true" ]]; then + log INFO "[DRY-RUN] Not executing OAR restore" + log INFO "To complete restore, region '$session_name' must be running and responsive" + return 0 + fi + + log INFO "Sending OAR restore command to screen session '$session_name'" + send_to_screen "$session_name" "$cmd" + log INFO "OAR restore command sent. Region import may take some time. Check session output for completion." +} + +full_restore() { + local has_sql has_oar + + has_sql=false + has_oar=false + + # Try to detect which restores to perform based on parameters + if [[ -n "$FILE" ]] && [[ "$FILE" =~ \.(sql|gz)$ ]]; then + has_sql=true + fi + + if [[ -n "$FILE" ]] && [[ "$FILE" =~ \.oar$ ]]; then + has_oar=true + fi + + if [[ "$has_sql" == "true" ]]; then + log INFO "Detected SQL backup file, proceeding with database restore..." + db_restore + fi + + if [[ "$has_oar" == "true" ]]; then + log INFO "Detected OAR backup file, proceeding with OAR restore..." + if [[ -n "$REGION" ]]; then + oar_restore + else + log WARN "OAR file provided but no --region specified. Skipping OAR restore." + fi + fi + + if [[ "$has_sql" == "false" && "$has_oar" == "false" ]]; then + die "full-restore requires --file with .sql/.sql.gz or .oar extension" + fi +} + +WORKDIR="$DEFAULT_WORKDIR" +ACTION="" +DB_USER="" +DB_PASS="" +DB_NAME="" +REGION="" +SESSION_NAME="" +FILE="" +DRY_RUN="false" +PROFILE="grid-sim" + +while [[ $# -gt 0 ]]; do + case "$1" in + --workdir) WORKDIR="$2"; shift 2 ;; + --profile) PROFILE="$2"; shift 2 ;; + --action) ACTION="$2"; shift 2 ;; + --db-user) DB_USER="$2"; shift 2 ;; + --db-pass) DB_PASS="$2"; shift 2 ;; + --db-name) DB_NAME="$2"; shift 2 ;; + --region) REGION="$2"; shift 2 ;; + --session) SESSION_NAME="$2"; shift 2 ;; + --file) FILE="$2"; shift 2 ;; + --dry-run) DRY_RUN="$2"; shift 2 ;; + --help|-h) usage; exit 0 ;; + *) die "Unknown option: $1" ;; + esac +done + +[[ -n "$ACTION" ]] || die "Missing --action" +validate_profile "$PROFILE" +ensure_profile_action_allowed restore "$PROFILE" "$ACTION" + +case "$DRY_RUN" in + true|false) ;; + *) die "--dry-run must be true or false" ;; +esac + +log INFO "Using profile: $PROFILE" + +case "$ACTION" in + db-restore) db_restore ;; + oar-restore) oar_restore ;; + full-restore) full_restore ;; + list-backups) ensure_backup_dir; list_backups ;; + *) die "Unsupported action: $ACTION" ;; +esac diff --git a/modular/osmtool_smoke.sh b/modular/osmtool_smoke.sh new file mode 100644 index 0000000..321f7fb --- /dev/null +++ b/modular/osmtool_smoke.sh @@ -0,0 +1,191 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +# shellcheck source=./osmtool_core.sh +source "$SCRIPT_DIR/osmtool_core.sh" + +usage() { + cat <<'EOF' +Usage: + osmtool_smoke.sh [--workdir ] --action + [--target ] [--name ] + [--expected-screens ] [--wait-after-start ] [--wait-after-stop ] + [--janus-prefix ] + +Examples: + osmtool_smoke.sh --action run --profile grid-sim --workdir /opt + osmtool_smoke.sh --action run --profile robust --workdir /opt --expected-screens robustserver +EOF + print_usage_common +} + +split_csv() { + local csv="$1" + local token + local IFS=',' + read -r -a _TOKENS <<< "$csv" + for token in "${_TOKENS[@]}"; do + token="${token## }" + token="${token%% }" + [[ -n "$token" ]] && printf '%s\n' "$token" + done +} + +default_target_for_profile() { + case "$1" in + grid-sim) echo "grid" ;; + robust) echo "robust" ;; + standalone) echo "standalone" ;; + esac +} + +default_expected_screens_for_target() { + case "$1" in + grid) echo "robustserver,sim1" ;; + robust) echo "robustserver" ;; + sim1) echo "sim1" ;; + standalone) echo "standalone" ;; + janus) echo "janus_gateway" ;; + region) [[ -n "$NAME" ]] && echo "$NAME" || echo "" ;; + *) echo "" ;; + esac +} + +screen_session_exists() { + local name="$1" + screen -list 2>/dev/null | grep -qE "\\.${name}[[:space:]]" +} + +verify_screens_up() { + local name failed=0 + + [[ -n "$EXPECTED_SCREENS" ]] || { + log INFO "No expected screens configured; skipping UP verification" + return 0 + } + + while IFS= read -r name; do + [[ -n "$name" ]] || continue + if screen_session_exists "$name"; then + log INFO "[SMOKE][OK] screen up: $name" + else + log ERROR "[SMOKE][FAIL] screen not running: $name" + failed=$((failed + 1)) + fi + done < <(split_csv "$EXPECTED_SCREENS") + + return "$failed" +} + +verify_screens_down() { + local name failed=0 + + [[ -n "$EXPECTED_SCREENS" ]] || { + log INFO "No expected screens configured; skipping DOWN verification" + return 0 + } + + while IFS= read -r name; do + [[ -n "$name" ]] || continue + if screen_session_exists "$name"; then + log ERROR "[SMOKE][FAIL] screen still running: $name" + failed=$((failed + 1)) + else + log INFO "[SMOKE][OK] screen down: $name" + fi + done < <(split_csv "$EXPECTED_SCREENS") + + return "$failed" +} + +run_startstop() { + local action="$1" + local -a cmd + + cmd=(bash "$ROOT_DIR/osmtool_main.sh" --module startstop --profile "$PROFILE" --workdir "$WORKDIR" --target "$TARGET" --action "$action") + if [[ "$TARGET" == "region" && -n "$NAME" ]]; then + cmd+=(--name "$NAME") + fi + if [[ -n "$JANUS_PREFIX" ]]; then + cmd+=(--janus-prefix "$JANUS_PREFIX") + fi + + "${cmd[@]}" +} + +run_smoke() { + local failures=0 + + log INFO "[SMOKE] profile=$PROFILE target=$TARGET workdir=$WORKDIR" + log INFO "[SMOKE] expected_screens=$EXPECTED_SCREENS" + + run_startstop start || failures=$((failures + 1)) + sleep "$WAIT_AFTER_START" + verify_screens_up || failures=$((failures + 1)) + + run_startstop restart || failures=$((failures + 1)) + sleep "$WAIT_AFTER_START" + verify_screens_up || failures=$((failures + 1)) + + run_startstop stop || failures=$((failures + 1)) + sleep "$WAIT_AFTER_STOP" + verify_screens_down || failures=$((failures + 1)) + + if [[ "$failures" -eq 0 ]]; then + log INFO "[SMOKE][PASS] Start/Restart/Stop smoke test successful" + return 0 + fi + + log ERROR "[SMOKE][FAIL] Smoke test failed with $failures issue group(s)" + return 1 +} + +WORKDIR="$DEFAULT_WORKDIR" +ACTION="" +PROFILE="grid-sim" +TARGET="" +NAME="" +EXPECTED_SCREENS="" +WAIT_AFTER_START="3" +WAIT_AFTER_STOP="8" +JANUS_PREFIX="${JANUS_PREFIX:-/opt/janus}" + +while [[ $# -gt 0 ]]; do + case "$1" in + --workdir) WORKDIR="$2"; shift 2 ;; + --profile) PROFILE="$2"; shift 2 ;; + --action) ACTION="$2"; shift 2 ;; + --target) TARGET="$2"; shift 2 ;; + --name) NAME="$2"; shift 2 ;; + --expected-screens) EXPECTED_SCREENS="$2"; shift 2 ;; + --wait-after-start) WAIT_AFTER_START="$2"; shift 2 ;; + --wait-after-stop) WAIT_AFTER_STOP="$2"; shift 2 ;; + --janus-prefix) JANUS_PREFIX="$2"; shift 2 ;; + --help|-h) usage; exit 0 ;; + *) die "Unknown option: $1" ;; + esac +done + +[[ -n "$ACTION" ]] || die "Missing --action" +validate_profile "$PROFILE" +ensure_profile_action_allowed smoke "$PROFILE" "$ACTION" + +if [[ -z "$TARGET" ]]; then + TARGET="$(default_target_for_profile "$PROFILE")" +fi +ensure_profile_target_allowed "$PROFILE" "$TARGET" + +if [[ "$TARGET" == "region" && -z "$NAME" ]]; then + die "--name is required for --target region" +fi + +if [[ -z "$EXPECTED_SCREENS" ]]; then + EXPECTED_SCREENS="$(default_expected_screens_for_target "$TARGET")" +fi + +case "$ACTION" in + run) run_smoke ;; + *) die "Unsupported action: $ACTION" ;; +esac \ No newline at end of file diff --git a/modular/osmtool_startstop.sh b/modular/osmtool_startstop.sh new file mode 100644 index 0000000..bcc4e49 --- /dev/null +++ b/modular/osmtool_startstop.sh @@ -0,0 +1,316 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck source=./osmtool_core.sh +source "$SCRIPT_DIR/osmtool_core.sh" + +usage() { + cat <<'EOF' +Usage: + osmtool_startstop.sh [--workdir ] [--profile ] --target --action [--name ] + [--janus-prefix ] + +Examples: + osmtool_startstop.sh --profile grid-sim --target grid --action start --workdir /opt + osmtool_startstop.sh --profile robust --target robust --action restart --workdir /opt +EOF + print_usage_common +} + +run_screen_cmd() { + local screen_name="$1" + local command_text="$2" + screen -S "$screen_name" -p 0 -X stuff "${command_text}^M" +} + +screen_session_exists() { + local screen_name="$1" + screen -list 2>/dev/null | grep -qE "\\.${screen_name}[[:space:]]" +} + +stop_screen_session() { + local screen_name="$1" + local grace_seconds="${2:-6}" + + if ! screen_session_exists "$screen_name"; then + log INFO "$screen_name is not running" + return 0 + fi + + run_screen_cmd "$screen_name" "shutdown" + log INFO "Sent shutdown to $screen_name" + sleep "$grace_seconds" + + if screen_session_exists "$screen_name"; then + log INFO "Session '$screen_name' still running after shutdown; forcing screen quit" + screen -S "$screen_name" -X quit || true + sleep 1 + fi + + if screen_session_exists "$screen_name"; then + log ERROR "Failed to stop screen session: $screen_name" + return 1 + fi + + log INFO "Screen session '$screen_name' stopped" + return 0 +} + +ensure_janus_ready() { + local marker="$JANUS_PREFIX/.osmtool_janus_ready" + local core_cfg="$JANUS_PREFIX/etc/janus/janus.jcfg" + local core_cfg_legacy="$JANUS_PREFIX/etc/janus/janus.cfg" + local http_cfg="$JANUS_PREFIX/etc/janus/janus.transport.http.jcfg" + local http_cfg_legacy="$JANUS_PREFIX/etc/janus/janus.transport.http.cfg" + + [[ -f "$JANUS_PREFIX/bin/janus" ]] || die "Janus binary missing: $JANUS_PREFIX/bin/janus. Run install module action compile-janus/install-janus first." + + if [[ ! -f "$core_cfg" && ! -f "$core_cfg_legacy" ]]; then + die "Janus core config missing in $JANUS_PREFIX/etc/janus. Run configure-janus/install-janus first." + fi + + if [[ ! -f "$http_cfg" && ! -f "$http_cfg_legacy" ]]; then + die "Janus HTTP config missing in $JANUS_PREFIX/etc/janus. Run configure-janus/install-janus first." + fi + + [[ -f "$marker" ]] || log INFO "Janus ready marker not found ($marker). Continuing because binary/config files exist." +} + +janus_service_action() { + local action="$1" + local janus_bin janus_cfg + + janus_bin="$JANUS_PREFIX/bin/janus" + janus_cfg="$JANUS_PREFIX/etc/janus/janus.jcfg" + [[ -f "$janus_cfg" ]] || janus_cfg="$JANUS_PREFIX/etc/janus/janus.cfg" + + if command -v systemctl >/dev/null 2>&1; then + if systemctl list-unit-files 2>/dev/null | grep -q '^janus\.service'; then + sudo systemctl "$action" janus + return 0 + fi + fi + + if command -v service >/dev/null 2>&1; then + if service --status-all 2>/dev/null | grep -q janus; then + sudo service janus "$action" + return 0 + fi + fi + + if [[ -x "$WORKDIR/janus.sh" ]]; then + "$WORKDIR/janus.sh" "${action}_janus" || "$WORKDIR/janus.sh" "$action" + return 0 + fi + + if [[ -x "$WORKDIR/janus/janus.sh" ]]; then + "$WORKDIR/janus/janus.sh" "${action}_janus" || "$WORKDIR/janus/janus.sh" "$action" + return 0 + fi + + # Fallback: control Janus directly from installed binary. + if [[ -x "$janus_bin" ]]; then + case "$action" in + start) + if pgrep -f "$janus_bin" >/dev/null 2>&1; then + log INFO "Janus already running" + return 0 + fi + if command -v screen >/dev/null 2>&1; then + screen -fa -S janus_gateway -d -U -m "$janus_bin" -C "$janus_cfg" + else + nohup "$janus_bin" -C "$janus_cfg" >/tmp/janus.log 2>&1 & + fi + return 0 + ;; + stop) + pkill -f "$janus_bin" >/dev/null 2>&1 || true + if command -v screen >/dev/null 2>&1; then + if screen -list | grep -q "janus_gateway"; then + screen -S janus_gateway -X quit || true + fi + fi + return 0 + ;; + restart) + pkill -f "$janus_bin" >/dev/null 2>&1 || true + sleep 1 + if command -v screen >/dev/null 2>&1; then + screen -fa -S janus_gateway -d -U -m "$janus_bin" -C "$janus_cfg" + else + nohup "$janus_bin" -C "$janus_cfg" >/tmp/janus.log 2>&1 & + fi + return 0 + ;; + esac + fi + + die "Janus control not configured. Expected system service or janus.sh helper." +} + +require_ini() { + local bin_dir="$1" + local ini_file="$2" + local label="$3" + + if [[ ! -f "$bin_dir/$ini_file" ]]; then + die "$label cannot start: missing $bin_dir/$ini_file — run the config action first" + fi +} + +screen_start() { + local screen_name="$1" + local bin_dir="$2" + local dll="$3" + local pat="\\.${screen_name}[[:space:]]" + + if screen -list 2>/dev/null | grep -qE "$pat"; then + log INFO "Screen session '$screen_name' already running — skipping start" + return 0 + fi + + log INFO "Starting $screen_name from $bin_dir" + (cd "$bin_dir" && screen -fa -S "$screen_name" -d -U -m dotnet "$dll") + sleep 0.5 + + if screen -list 2>/dev/null | grep -qE "$pat"; then + log INFO "Screen session '$screen_name' started successfully" + else + log ERROR "Screen session '$screen_name' did not stay alive — check logs or missing config" + fi +} + +start_grid() { + log INFO "Starting grid from $WORKDIR" + + if [[ -d "$WORKDIR/robust/bin" ]]; then + require_ini "$WORKDIR/robust/bin" "Robust.ini" "RobustServer" + screen_start "robustserver" "$WORKDIR/robust/bin" "Robust.dll" + fi + + if [[ -f "$WORKDIR/robust/bin/MoneyServer.dll" && -f "$WORKDIR/robust/bin/MoneyServer.ini" ]]; then + screen_start "moneyserver" "$WORKDIR/robust/bin" "MoneyServer.dll" + fi + + if [[ -d "$WORKDIR/sim1/bin" ]]; then + require_ini "$WORKDIR/sim1/bin" "OpenSim.ini" "sim1" + screen_start "sim1" "$WORKDIR/sim1/bin" "OpenSim.dll" + fi +} + +stop_grid() { + log INFO "Stopping grid" + stop_screen_session "sim1" || true + stop_screen_session "moneyserver" || true + stop_screen_session "robustserver" || true +} + +restart_grid() { + stop_grid + sleep 5 + start_grid +} + +start_single() { + local target="$1" + case "$target" in + robust) + require_ini "$WORKDIR/robust/bin" "Robust.ini" "RobustServer" + screen_start "robustserver" "$WORKDIR/robust/bin" "Robust.dll" + ;; + sim1) + require_ini "$WORKDIR/sim1/bin" "OpenSim.ini" "sim1" + screen_start "sim1" "$WORKDIR/sim1/bin" "OpenSim.dll" + ;; + standalone) + require_ini "$WORKDIR/standalone/bin" "OpenSim.ini" "standalone" + screen_start "standalone" "$WORKDIR/standalone/bin" "OpenSim.dll" + ;; + janus) + ensure_janus_ready + janus_service_action start + ;; + region) + [[ -n "$NAME" ]] || die "--name is required for --target region" + require_ini "$WORKDIR/${NAME}/bin" "OpenSim.ini" "$NAME" + screen_start "$NAME" "$WORKDIR/${NAME}/bin" "OpenSim.dll" + ;; + *) + die "Unsupported start target: $target" + ;; + esac +} + +stop_single() { + local target="$1" + case "$target" in + robust) + stop_screen_session "robustserver" + ;; + sim1) + stop_screen_session "sim1" + ;; + standalone) + stop_screen_session "standalone" + ;; + janus) janus_service_action stop ;; + region) + [[ -n "$NAME" ]] || die "--name is required for --target region" + stop_screen_session "$NAME" + ;; + *) die "Unsupported stop target: $target" ;; + esac +} + +WORKDIR="$DEFAULT_WORKDIR" +TARGET="" +ACTION="" +NAME="" +PROFILE="grid-sim" +JANUS_PREFIX="${JANUS_PREFIX:-/opt/janus}" + +while [[ $# -gt 0 ]]; do + case "$1" in + --workdir) WORKDIR="$2"; shift 2 ;; + --profile) PROFILE="$2"; shift 2 ;; + --janus-prefix) JANUS_PREFIX="$2"; shift 2 ;; + --target) TARGET="$2"; shift 2 ;; + --action) ACTION="$2"; shift 2 ;; + --name) NAME="$2"; shift 2 ;; + --help|-h) usage; exit 0 ;; + *) die "Unknown option: $1" ;; + esac +done + +[[ -n "$TARGET" ]] || die "Missing --target" +[[ -n "$ACTION" ]] || die "Missing --action" +validate_profile "$PROFILE" +ensure_profile_action_allowed startstop "$PROFILE" "$ACTION" +ensure_profile_target_allowed "$PROFILE" "$TARGET" + +if [[ "$TARGET" == "janus" && "$ACTION" != "stop" ]]; then + ensure_janus_ready +fi + +log INFO "Using profile: $PROFILE" + +if [[ "$TARGET" == "grid" ]]; then + case "$ACTION" in + start) start_grid ;; + stop) stop_grid ;; + restart) restart_grid ;; + *) die "Unsupported action for grid: $ACTION" ;; + esac +else + case "$ACTION" in + start) start_single "$TARGET" ;; + stop) stop_single "$TARGET" ;; + restart) + stop_single "$TARGET" + sleep 3 + start_single "$TARGET" + ;; + *) die "Unsupported action: $ACTION" ;; + esac +fi diff --git a/modular/osmtool_update.sh b/modular/osmtool_update.sh new file mode 100644 index 0000000..6ebee54 --- /dev/null +++ b/modular/osmtool_update.sh @@ -0,0 +1,262 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck source=./osmtool_core.sh +source "$SCRIPT_DIR/osmtool_core.sh" + +usage() { + cat <<'EOF' +Usage: + osmtool_update.sh [--workdir ] --action + [--opensim-dir ] [--opensim-repo ] [--opensim-branch ] [--repo-mode ] + [--tsassets-dir ] [--currency-dir ] [--data-backup-dir ] + [--deploy-binaries ] [--legacy-patch-dir ] + [--janus-prefix ] [--janus-src ] [--public-host ] [--http-port ] + [--admin-port ] [--rtp-range ] [--enable-admin ] + [--api-secret ] [--admin-secret ] [--label ] [--dry-run ] + +Examples: + osmtool_update.sh --action create-rollback --workdir /opt --label pre_update + osmtool_update.sh --action update-opensim --workdir /opt --dry-run true +EOF + print_usage_common +} + +timestamp() { + date '+%Y%m%d_%H%M%S' +} + +rollback_dir() { + printf '%s' "$WORKDIR/rollback" +} + +ensure_rollback_dir() { + local dir + dir="$(rollback_dir)" + + if mkdir -p "$dir" 2>/dev/null; then + return 0 + fi + + sudo mkdir -p "$dir" + sudo chown "$(id -u):$(id -g)" "$dir" +} + +path_relative_to_root() { + local path="$1" + printf '%s' "${path#/}" +} + +append_snapshot_path() { + local path="$1" + + [[ -e "$path" ]] || return 0 + SNAPSHOT_PATHS+=("$(path_relative_to_root "$path")") +} + +build_opensim_snapshot_paths() { + local sim_dir + + SNAPSHOT_PATHS=() + append_snapshot_path "$OPENSIM_DIR" + append_snapshot_path "$WORKDIR/robust/bin" + append_snapshot_path "$WORKDIR/standalone/bin" + + shopt -s nullglob + for sim_dir in "$WORKDIR"/sim*; do + append_snapshot_path "$sim_dir/bin" + done + shopt -u nullglob +} + +build_janus_snapshot_paths() { + SNAPSHOT_PATHS=() + append_snapshot_path "$JANUS_PREFIX" + append_snapshot_path "$JANUS_SRC" +} + +create_snapshot_archive() { + local scope="$1" + shift + local archive_name archive_path manifest_path + local -a paths=("$@") + + ensure_rollback_dir + + if [[ ${#paths[@]} -eq 0 ]]; then + log WARN "No existing paths found for rollback scope: $scope" + return 0 + fi + + archive_name="${scope}_$(timestamp)" + if [[ -n "$LABEL" ]]; then + archive_name="${archive_name}_${LABEL}" + fi + + archive_path="$(rollback_dir)/${archive_name}.tar.gz" + manifest_path="$(rollback_dir)/${archive_name}.manifest" + + log INFO "Creating rollback archive: $archive_path" + printf '%s\n' "${paths[@]}" > "$manifest_path" + + if [[ "$DRY_RUN" == "true" ]]; then + log INFO "[DRY-RUN] Would archive paths listed in: $manifest_path" + return 0 + fi + + tar -czf "$archive_path" -C / "${paths[@]}" + log INFO "Rollback archive created: $archive_path" + log INFO "Rollback manifest created: $manifest_path" +} + +run_install_action() { + local install_action="$1" + local -a cmd + + cmd=("$SCRIPT_DIR/osmtool_install.sh" --profile "$PROFILE" --workdir "$WORKDIR" --action "$install_action") + + [[ -n "$OPENSIM_DIR" ]] && cmd+=(--opensim-dir "$OPENSIM_DIR") + [[ -n "$OPENSIM_REPO" ]] && cmd+=(--opensim-repo "$OPENSIM_REPO") + [[ -n "$OPENSIM_BRANCH" ]] && cmd+=(--opensim-branch "$OPENSIM_BRANCH") + [[ -n "$REPO_MODE" ]] && cmd+=(--repo-mode "$REPO_MODE") + [[ -n "$TSASSETS_DIR" ]] && cmd+=(--tsassets-dir "$TSASSETS_DIR") + [[ -n "$CURRENCY_DIR" ]] && cmd+=(--currency-dir "$CURRENCY_DIR") + [[ -n "$DATA_BACKUP_DIR" ]] && cmd+=(--data-backup-dir "$DATA_BACKUP_DIR") + [[ -n "$DEPLOY_BINARIES" ]] && cmd+=(--deploy-binaries "$DEPLOY_BINARIES") + [[ -n "$LEGACY_PATCH_DIR" ]] && cmd+=(--legacy-patch-dir "$LEGACY_PATCH_DIR") + [[ -n "$JANUS_PREFIX" ]] && cmd+=(--janus-prefix "$JANUS_PREFIX") + [[ -n "$JANUS_SRC" ]] && cmd+=(--janus-src "$JANUS_SRC") + [[ -n "$PUBLIC_HOST" ]] && cmd+=(--public-host "$PUBLIC_HOST") + [[ -n "$HTTP_PORT" ]] && cmd+=(--http-port "$HTTP_PORT") + [[ -n "$ADMIN_PORT" ]] && cmd+=(--admin-port "$ADMIN_PORT") + [[ -n "$RTP_RANGE" ]] && cmd+=(--rtp-range "$RTP_RANGE") + [[ -n "$ENABLE_ADMIN" ]] && cmd+=(--enable-admin "$ENABLE_ADMIN") + [[ -n "$API_SECRET" ]] && cmd+=(--api-secret "$API_SECRET") + [[ -n "$ADMIN_SECRET" ]] && cmd+=(--admin-secret "$ADMIN_SECRET") + + log INFO "Delegating update to install action: $install_action" + if [[ "$DRY_RUN" == "true" ]]; then + printf -v rendered '%q ' "${cmd[@]}" + log INFO "[DRY-RUN] $rendered" + return 0 + fi + + "${cmd[@]}" +} + +create_rollback() { + build_opensim_snapshot_paths + create_snapshot_archive opensim "${SNAPSHOT_PATHS[@]}" + build_janus_snapshot_paths + create_snapshot_archive janus "${SNAPSHOT_PATHS[@]}" +} + +list_rollbacks() { + local dir + dir="$(rollback_dir)" + + if [[ ! -d "$dir" ]]; then + log INFO "No rollback directory found: $dir" + return 0 + fi + + find "$dir" -maxdepth 1 -type f \( -name '*.tar.gz' -o -name '*.manifest' \) -printf '%T@ %p\n' 2>/dev/null | \ + sort -rn | cut -d' ' -f2- +} + +update_opensim() { + build_opensim_snapshot_paths + create_snapshot_archive opensim "${SNAPSHOT_PATHS[@]}" + run_install_action install-opensim +} + +update_janus() { + build_janus_snapshot_paths + create_snapshot_archive janus "${SNAPSHOT_PATHS[@]}" + run_install_action install-janus +} + +full_update() { + update_opensim + update_janus +} + +WORKDIR="$DEFAULT_WORKDIR" +ACTION="" +PROFILE="grid-sim" +OPENSIM_DIR="" +OPENSIM_REPO="" +OPENSIM_BRANCH="" +REPO_MODE="update" +TSASSETS_DIR="" +CURRENCY_DIR="" +DATA_BACKUP_DIR="" +DEPLOY_BINARIES="true" +LEGACY_PATCH_DIR="" +JANUS_PREFIX="" +JANUS_SRC="" +PUBLIC_HOST="" +HTTP_PORT="" +ADMIN_PORT="" +RTP_RANGE="" +ENABLE_ADMIN="" +API_SECRET="" +ADMIN_SECRET="" +LABEL="" +DRY_RUN="false" +SNAPSHOT_PATHS=() + +while [[ $# -gt 0 ]]; do + case "$1" in + --workdir) WORKDIR="$2"; shift 2 ;; + --profile) PROFILE="$2"; shift 2 ;; + --action) ACTION="$2"; shift 2 ;; + --opensim-dir) OPENSIM_DIR="$2"; shift 2 ;; + --opensim-repo) OPENSIM_REPO="$2"; shift 2 ;; + --opensim-branch) OPENSIM_BRANCH="$2"; shift 2 ;; + --repo-mode) REPO_MODE="$2"; shift 2 ;; + --tsassets-dir) TSASSETS_DIR="$2"; shift 2 ;; + --currency-dir) CURRENCY_DIR="$2"; shift 2 ;; + --data-backup-dir) DATA_BACKUP_DIR="$2"; shift 2 ;; + --deploy-binaries) DEPLOY_BINARIES="$2"; shift 2 ;; + --legacy-patch-dir) LEGACY_PATCH_DIR="$2"; shift 2 ;; + --janus-prefix) JANUS_PREFIX="$2"; shift 2 ;; + --janus-src) JANUS_SRC="$2"; shift 2 ;; + --public-host) PUBLIC_HOST="$2"; shift 2 ;; + --http-port) HTTP_PORT="$2"; shift 2 ;; + --admin-port) ADMIN_PORT="$2"; shift 2 ;; + --rtp-range) RTP_RANGE="$2"; shift 2 ;; + --enable-admin) ENABLE_ADMIN="$2"; shift 2 ;; + --api-secret) API_SECRET="$2"; shift 2 ;; + --admin-secret) ADMIN_SECRET="$2"; shift 2 ;; + --label) LABEL="$2"; shift 2 ;; + --dry-run) DRY_RUN="$2"; shift 2 ;; + --help|-h) usage; exit 0 ;; + *) die "Unknown option: $1" ;; + esac +done + +[[ -n "$ACTION" ]] || die "Missing --action" +validate_profile "$PROFILE" +ensure_profile_action_allowed update "$PROFILE" "$ACTION" + +case "$DRY_RUN" in + true|false) ;; + *) die "--dry-run must be true or false" ;; +esac + +OPENSIM_DIR="${OPENSIM_DIR:-$WORKDIR/opensim}" +JANUS_PREFIX="${JANUS_PREFIX:-/opt/janus}" +JANUS_SRC="${JANUS_SRC:-$WORKDIR/janus-gateway}" + +log INFO "Using profile: $PROFILE" + +case "$ACTION" in + create-rollback) create_rollback ;; + list-rollbacks) list_rollbacks ;; + update-opensim) update_opensim ;; + update-janus) update_janus ;; + full-update) full_update ;; + *) die "Unsupported action: $ACTION" ;; +esac \ No newline at end of file diff --git a/osmtool.sh b/osmtool.sh new file mode 100644 index 0000000..2e6dbc6 --- /dev/null +++ b/osmtool.sh @@ -0,0 +1,6700 @@ +#!/bin/bash + +#?────────────────────────────────────────────────────────────────────────────────────────── +#* Debug in log Datei +#! ACHTUNG das loggen speichert alles auch vertrauliche Daten. +#?────────────────────────────────────────────────────────────────────────────────────────── + +#* ====================================================================== +#* DEBUG-LOG-SYSTEM (Überarbeitete Version) +#* +#* Merkmale: +#* - Vollständig abwärtskompatibel zu bestehenden Aufrufen +#* - Thread-sicherer Zugriff auf Log-Datei (mit flock) +#* - Automatische Log-Rotation bei Größenüberschreitung +#* - Unterstützt farbige Konsolenausgabe und bereinigte Log-Dateien +#* - Robustere Fehlerbehandlung +#* ====================================================================== + +#* 1. LOGGING-KONFIGURATION +#* ---------------------------------------------------------------------- + +# Logging global aktivieren/deaktivieren (true/false) +LOG_ENABLED=true # Default: true (Logging aktiv) +#LOG_ENABLED=false # Zum Deaktivieren + +# Soll die Log-Datei beim Start gelöscht werden? +OLD_LOG_DEL=true # Default: true (alte Logs löschen) +#OLD_LOG_DEL=false # Zum Deaktivieren + +# Log-Datei Name und Speicherort +DEBUG_LOG="osmtool_debug.log" # Default: im aktuellen Verzeichnis + +# Maximale Log-Größe in MB bevor Rotation erfolgt (0 = deaktiviert) +MAX_LOG_SIZE_MB=10 # 10MB empfohlen + +#* 2. LOG-SYSTEM INITIALISIERUNG +#* ---------------------------------------------------------------------- + +# Logdatei vorbereiten (wird nur aufgerufen wenn LOG_ENABLED=true) +function init_log_system() { + # Alte Log-Datei löschen falls gewünscht + if [[ "$OLD_LOG_DEL" == "true" ]]; then + : > "$DEBUG_LOG" 2>/dev/null || { + echo "FEHLER: Kann Logdatei nicht zurücksetzen: $DEBUG_LOG" >&2 + return 1 + } + else + touch "$DEBUG_LOG" 2>/dev/null || { + echo "FEHLER: Kann Logdatei nicht erstellen: $DEBUG_LOG" >&2 + return 1 + } + fi + + # Schreibrechte prüfen + if [ ! -w "$DEBUG_LOG" ]; then + echo "FEHLER: Keine Schreibrechte für Logdatei: $DEBUG_LOG" >&2 + return 1 + fi + + return 0 +} + +# System bei Start initialisieren +if [[ "$LOG_ENABLED" == "true" ]]; then + if ! init_log_system; then + echo "WARNUNG: Logging wird deaktiviert (Initialisierung fehlgeschlagen)" >&2 + LOG_ENABLED=false + fi +fi + +#* 3. HILFSFUNKTIONEN +#* ---------------------------------------------------------------------- + +# Entfernt ANSI-Farbcodes aus Text (für Log-Datei) +function clean_ansi() { + echo -e "$1" | sed -E 's/\x1B\[([0-9]{1,3}(;[0-9]{1,3})*)?[mGK]//g' +} + +# Führt Log-Rotation durch wenn MAX_LOG_SIZE_MB überschritten wird +function rotate_log_if_needed() { + [[ "$MAX_LOG_SIZE_MB" -eq 0 ]] && return # Rotation deaktiviert + + local log_size_mb + log_size_mb=$(du -m "$DEBUG_LOG" 2>/dev/null | cut -f1) || return + + if [[ "$log_size_mb" -ge "$MAX_LOG_SIZE_MB" ]]; then + mv "$DEBUG_LOG" "${DEBUG_LOG}.old" 2>/dev/null + : > "$DEBUG_LOG" 2>/dev/null + fi +} + +#* 4. HAUPT-LOG-FUNKTION (abwärtskompatibel!) +#* ---------------------------------------------------------------------- + +function log() { + local message="$1" + local level="${2:-INFO}" # Default-Level: INFO (abwärtskompatibel) + timestamp=$(date "+%Y-%m-%d %H:%M:%S") + + # 1. Immer zur Konsole ausgeben (mit Farben, genau wie bisher) + echo -e "$message" + + # 2. Nur in Log-Datei schreiben wenn Logging aktiviert + if [[ "$LOG_ENABLED" == "true" ]]; then + # Log-Rotation prüfen + rotate_log_if_needed + + # ANSI-Codes entfernen für Log-Datei + clean_message=$(clean_ansi "$message") + + # Thread-sicheres Schreiben (falls flock verfügbar) + if command -v flock >/dev/null; then + ( + flock -x 200 + echo "[${timestamp}] [${level}] ${clean_message}" >> "$DEBUG_LOG" + ) 200>>"$DEBUG_LOG" + else + echo "[${timestamp}] [${level}] ${clean_message}" >> "$DEBUG_LOG" + fi + fi +} + +#* ====================================================================== +#* BEISPIELAUFRUFE: +#* +#* log "${COLOR_BAD}FEHLER: Robust.HG.ini nicht gefunden in $robust_dir${COLOR_RESET}" >&2 +#* log "Eine normale Info-Meldung" +#* ====================================================================== + +#?────────────────────────────────────────────────────────────────────────────────────────── +#* Informationen Kopfzeile +#?────────────────────────────────────────────────────────────────────────────────────────── + +# Quelle: +# https://github.com/ManfredAabye/opensimMULTITOOLS-II/blob/main/osmtool.sh + +tput reset # Bildschirmausgabe loeschen inklusive dem Scrollbereich. +SCRIPTNAME="opensimMULTITOOL II" + +#testmodus=1 # Testmodus: 1=aktiviert, 0=deaktiviert + +# Versionsnummer besteht aus: Jahr.Monat.Funktionsanzahl.Eigentliche_Versionsnummer +VERSION="V25.5.130.437" +log "\e[36m$SCRIPTNAME\e[0m $VERSION" +echo "Dies ist ein Tool welches der Verwaltung von OpenSim Servern dient." +echo "Bitte beachten Sie, dass die Anwendung auf eigene Gefahr und Verantwortung erfolgt." +log "\e[33mZum Abbrechen bitte STRG+C oder CTRL+C drücken.\e[0m" +echo " " + +#?────────────────────────────────────────────────────────────────────────────────────────── +#* Variablen setzen +#?────────────────────────────────────────────────────────────────────────────────────────── + +#* FARBDEFINITIONEN +COLOR_OK='\e[32m' # Grün (Haken) +COLOR_BAD='\e[31m' # Rot (Kreuz) +COLOR_HEADING='\e[97m' # Weiß (Überschriften) +COLOR_START='\e[92m' # Hellgrün (Startaktionen) +COLOR_STOP='\e[91m' # Hellrot (Stopaktionen) +COLOR_SERVER='\e[36m' # Cyan (Server/Verzeichnisse/IPs) +COLOR_DIR='\e[90m' # Grau (Pfade) +COLOR_LABEL='\e[97m' # Weiß (Beschriftungen) +COLOR_WARNING='\e[33m' # Gelb (Warnungen) +COLOR_VALUE='\e[36m' # Cyan für Werte +COLOR_ACTION='\e[92m' # Hellgrün für Aktionen +COLOR_RESET='\e[0m' # Farbreset +COLOR_RESTART='\e[93m' # Gelb (RESTART) +COLOR_INFO='\e[31m' # Rot (INFO) +COLOR_SERVER='\e[34m' # Blau (SERVER) + +# Standardfarben +# COLOR_RED='\e[31m' # Rot +# COLOR_GREEN='\e[32m' # Grün +# COLOR_YELLOW='\e[33m' # Gelb +# COLOR_BLUE='\e[34m' # Blau (korrigiert für SERVER) +# COLOR_MAGENTA='\e[35m' # Magenta (dein aktueller "SERVER"-Wert) +# COLOR_CYAN='\e[36m' # Cyan +# COLOR_WHITE='\e[37m' # Weiß + +#* SYMBOLDEFINITIONEN +SYM_OK="${COLOR_OK}✓${COLOR_RESET}" +SYM_BAD="${COLOR_BAD}✗${COLOR_RESET}" +SYM_INFO="${COLOR_INFO}❓${COLOR_RESET}" +SYM_WAIT="${COLOR_VALUE}⏳${COLOR_RESET}" +SYM_LOG="${COLOR_VALUE}📋${COLOR_RESET}" +COLOR_SECTION='\e[0;35m' # Magenta für Sektionsnamen +COLOR_FILE='\e[0;33m' # Gelb für Dateipfade + +#* SYSTEM & SERVER +SYM_SERVER="${COLOR_SERVER}💻${COLOR_RESET}" # Server/Computer +#SYM_STORAGE="${COLOR_VALUE}💾${COLOR_RESET}" # Speicher (Diskette) +#SYM_NETWORK="${COLOR_VALUE}📡${COLOR_RESET}" # Netzwerk/Antenne +#SYM_SECURITY="${COLOR_VALUE}🔒${COLOR_RESET}" # Sicherheit + +#* DATEIEN & ORDNER +SYM_FOLDER="${COLOR_VALUE}📂${COLOR_RESET}" # Geöffneter Ordner +SYM_FILE="${COLOR_VALUE}📄${COLOR_RESET}" # Dokument +#SYM_ARCHIVE="${COLOR_VALUE}📦${COLOR_RESET}" # Archiv/Paket +#SYM_SEARCH="${COLOR_VALUE}🔍${COLOR_RESET}" # Suche + +#* TOOLS & AKTIONEN +SYM_TOOLS="${COLOR_VALUE}🧮${COLOR_RESET}" # Werkzeuge +SYM_CONFIG="${COLOR_VALUE}🔧${COLOR_RESET}" # Konfiguration +#SYM_MAINTENANCE="${COLOR_VALUE}🛠️${COLOR_RESET}" # Wartung +SYM_CLEAN="${COLOR_WARNING}🧹${COLOR_RESET}" # Bereinigung + +#* DOKUMENTATION +SYM_SCRIPT="${COLOR_VALUE}📜${COLOR_RESET}" # Skript +SYM_BOOK="${COLOR_VALUE}📖${COLOR_RESET}" # Dokumentation +#SYM_NOTES="${COLOR_VALUE}📝${COLOR_RESET}" # Notizen + +#* STATUS & KONTROLLE +SYM_OK="${COLOR_OK}✔${COLOR_RESET}" # Erfolg +SYM_BAD="${COLOR_BAD}✘${COLOR_RESET}" # Fehler +SYM_WARNING="${COLOR_WARNING}🔥${COLOR_RESET}" # Warnung +#SYM_RESTART="${COLOR_RESTART}🔄${COLOR_RESET}" # Neustart + +#* ERWEITERTE SYMBOLE (Für spezielle Funktionen) +#SYM_DATABASE="${COLOR_VALUE}💽${COLOR_RESET}" # Datenbank +#SYM_BACKUP="${COLOR_VALUE}💾${COLOR_RESET}" # Backup +#SYM_EMAIL="${COLOR_VALUE}📧${COLOR_RESET}" # E-Mail +#SYM_MONITORING="${COLOR_VALUE}📈${COLOR_RESET}" # Monitoring + +#* WARTEZEITEN muessen leider sein damit der Server nicht überfordert wird. +Simulator_Start_wait=15 # Sekunden +MoneyServer_Start_wait=30 # Sekunden +RobustServer_Start_wait=30 # Sekunden +Simulator_Stop_wait=15 # Sekunden +MoneyServer_Stop_wait=30 # Sekunden +RobustServer_Stop_wait=30 # Sekunden + +KOMMANDO=$1 #! Eingabeauswertung fuer Funktionen. + +#* Leere Zeile +function blankline() { + # Konfiguration: Zeichen für Linien (beliebig erweiterbar) + #local line_chars=("-" "_" "=" "#" "*" "~") + local line_chars=("_") + local line_length=80 # Standardlänge der Linie + + sleep 0.25 + + # Erzeuge jede Linie mit Verzögerung + for char in "${line_chars[@]}"; do + printf "%${line_length}s\n" | tr " " "$char" # Linie dynamisch generieren + echo " " # Leerzeile nach jeder Linie + done +} + +#* Hauptpfad des Skripts automatisch setzen +SCRIPT_DIR="$(dirname "$(realpath "$0")")" +cd "$SCRIPT_DIR" || exit 1 +system_ip=$(hostname -I | awk '{print $1}') +log "${COLOR_LABEL}Das Arbeitsverzeichnis ist:${COLOR_RESET} ${COLOR_VALUE}$SCRIPT_DIR${COLOR_RESET}" +log "${COLOR_LABEL}Ihre IP Adresse ist:${COLOR_RESET} ${COLOR_VALUE}$system_ip${COLOR_RESET}" +blankline + +#* Rootrechte erforderlich +function rootrights() { + # Root-Privilegien erforderlich + if [[ $EUID -ne 0 ]]; then + log "${COLOR_WARNING}Warning: Dieses Skript kann als Benutzer ausgeführt werden," >&2 + log "aber einige Funktionen benötigen Root-Rechte.${COLOR_RESET}" >&2 + log "[sudo bash osmtool.sh]" >&2 + + while true; do + log "Möchten Sie trotzdem ohne Root-Privilegien fortfahren? (ja/nein): " >&2 + read -r answer + + # Akzeptiere: j/J/ja/Ja/JA/y/Y/yes/Yes/YES + n/N/nein/Nein/NEIN + case "${answer,,}" in # ${answer,,} wandelt in Kleinbuchstaben um + j|ja|y|yes) + log "Fortfahren ohne Root-Rechte..." >&2 + return 0 # Erfolg, fortfahren + ;; + n|nein|no) + log "${COLOR_STOP}Abbruch.${COLOR_RESET}" >&2 + exit 1 + ;; + *) + log "${COLOR_WARNING}Ungültige Eingabe. Bitte 'ja' oder 'nein' eingeben.${COLOR_RESET}" >&2 + ;; + esac + done + fi +} + + +#* Soll im Hypergrid Modus gearbeitet werden oder in einem Geschlossenen Grid? +function hypergrid() { + local modus="$1" + local robust_dir="$SCRIPT_DIR/robust/bin" # Korrekter Pfad zu Robust-Dateien + + echo "Modus Einstellung" + + if [[ "$modus" == "hypergrid" ]]; then + echo "Hypergrid Modus aktiviert." + + # Prüfen ob Robust.HG.ini existiert + if [[ ! -f "$robust_dir/Robust.HG.ini" ]]; then + log "${COLOR_BAD}FEHLER: Robust.HG.ini nicht gefunden in $robust_dir${COLOR_RESET}" >&2 + return 1 + fi + + cp "$robust_dir/Robust.HG.ini" "$robust_dir/Robust.ini" || { + log "${COLOR_BAD}FEHLER: Konnte Robust.HG.ini nicht kopieren${COLOR_RESET}" >&2 + return 1 + } + + else + echo "Geschlossener Grid Modus aktiviert." + + # Prüfen ob Robust.local.ini existiert + if [[ ! -f "$robust_dir/Robust.local.ini" ]]; then + log "${COLOR_BAD}FEHLER: Robust.local.ini nicht gefunden in $robust_dir${COLOR_RESET}" >&2 + return 1 + fi + + cp "$robust_dir/Robust.local.ini" "$robust_dir/Robust.ini" || { + log "${COLOR_BAD}FEHLER: Konnte Robust.local.ini nicht kopieren${COLOR_RESET}" >&2 + return 1 + } + fi + + log "${COLOR_OK}Modus erfolgreich auf $modus gesetzt${COLOR_RESET}" + return 0 +} + +#?────────────────────────────────────────────────────────────────────────────────────────── +#* Abhängigkeiten installieren +#?────────────────────────────────────────────────────────────────────────────────────────── + +# Nach git clone/pull prüfen +check_repo_integrity() { + local repo_dir=${1:-.} + + pushd "$repo_dir" > /dev/null || return 1 + + if ! git fsck --no-progress; then + log "${SYM_BAD} ${COLOR_ERROR}Repository-Prüfung fehlgeschlagen!${COLOR_RESET}" + popd > /dev/null + return 1 + fi + + if [ "$(git status --porcelain)" ]; then + log "${COLOR_WARNING}Uncommittete Änderungen vorhanden.${COLOR_RESET}" + fi + + popd > /dev/null + log "${SYM_OK} ${COLOR_ACTION}Repository-Integrität OK.${COLOR_RESET}" + return 0 +} + +function warn_if_desktop_environment() { + log "${COLOR_SECTION}=== Systemtyp-Erkennung (Server vs. Desktop) ===${COLOR_RESET}" + + # Prüfen auf Anzeichen einer Desktop-Umgebung + if [[ -n "$XDG_CURRENT_DESKTOP" || -n "$DESKTOP_SESSION" || $(pgrep -lE 'gnome|plasma|xfce|mate|lxde|cinnamon' 2>/dev/null) ]]; then + log "${SYM_WARNING} ${COLOR_WARNING}Desktop-Umgebung erkannt!${COLOR_RESET}" + log "${COLOR_WARNING}${SYM_WARNING} Du führst dieses Skript auf einem Desktop-System aus.${COLOR_RESET}" + log "${COLOR_ACTION}Dieses Setup installiert einen Server und benötigt Root-Rechte.${COLOR_RESET}" + log "${COLOR_WARNING}Bitte stelle sicher, dass du weißt, was du tust.${COLOR_RESET}" + echo -ne "${COLOR_ACTION}Trotzdem fortfahren? (j/n) [n] ${COLOR_RESET}" + read -r user_continue + user_continue=${user_continue:-n} + + if [[ "$user_continue" =~ ^[nN]$ ]]; then + log "${SYM_BAD} ${COLOR_BAD}Installation abgebrochen vom Benutzer.${COLOR_RESET}" + exit 1 + fi + fi +} + +# -------------------------- +# Neuer Servercheck mit installationen. +# -------------------------- + +# -------------------------- +# 🛠️ HAUPTFUNKTION: servercheck() +# -------------------------- +function newservercheck() { + log "${COLOR_HEADING}🔍 Server-Kompatibilitätscheck wird durchgeführt...${COLOR_RESET}" + + rootrights + warn_if_desktop_environment + + # 1. Distro erkennen + detect_distro + + # 2. .NET prüfen/installieren + install_dotnet + + # 3. MariaDB prüfen/installieren + install_mariadb + + # 4. Abhängigkeiten installieren + install_dependencies + + log "${SYM_OK} ${COLOR_HEADING}Server-Check abgeschlossen!${COLOR_RESET}" +} + +# -------------------------- +# 🖥️ NEBENFUNKTIONEN (modular & erweiterbar) +# -------------------------- + +function detect_distro() { + # OS-Details global speichern + #log "detect_distro:" + declare -g os_id os_version os_codename # Explizit deklarieren + os_id=$(grep '^ID=' /etc/os-release | cut -d= -f2 | tr -d '"') + os_version=$(grep '^VERSION_ID=' /etc/os-release | cut -d= -f2 | tr -d '"') + os_codename=$(grep '^VERSION_CODENAME=' /etc/os-release | cut -d= -f2 | tr -d '"') + + log "${COLOR_LABEL}Server läuft mit:${COLOR_RESET} ${COLOR_SERVER}$os_id $os_version ($os_codename)${COLOR_RESET}" +} + +function install_dotnet() { + # Paketnamen-Mapping (erweiterbar!) + #log "install_dotnet:" + declare -A dotnet_pkg=( + ["ubuntu"]="dotnet-sdk-8.0" + ["debian"]="dotnet-sdk-8.0" + ["linuxmint"]="dotnet-sdk-8.0" + ["pop_os"]="dotnet-sdk-8.0" + ["arch"]="dotnet-sdk" + ["manjaro"]="dotnet-sdk" + ["raspbian"]="dotnet-sdk-8.0" + ) + + # Distro-spezifische Logik + case "$os_id" in + ubuntu|linuxmint|pop_os) + if [[ "$os_version" == "18.04" ]]; then + required_dotnet="dotnet-sdk-6.0" + elif [[ "$os_version" == "24.04" ]]; then + required_dotnet=${dotnet_pkg[$os_id]} + log "${SYM_INFO} Ubuntu 24.04 verwendet das .NET Repository für 22.04 (jammy)" + elif dpkg --compare-versions "$os_version" ge "20.04"; then + required_dotnet=${dotnet_pkg[$os_id]} + else + log "${SYM_BAD} Nicht unterstützte Ubuntu-Version: $os_version!" + return 1 + fi + ;; + debian|raspbian) + [[ "$os_version" -ge "11" ]] || { log "${SYM_BAD} Debian $os_version wird nicht unterstützt!"; return 1; } + required_dotnet=${dotnet_pkg[$os_id]} + ;; + arch|manjaro) + required_dotnet=${dotnet_pkg[$os_id]} + ;; + *) + log "${SYM_BAD} Nicht unterstützte Distribution: $os_id!" + return 1 + ;; + esac + + # Installationslogik (pacman vs. apt) + if [[ "$os_id" =~ ^(arch|manjaro)$ ]]; then + pacman_install "$required_dotnet" + else + apt_install "$required_dotnet" + # Microsoft-Repo für Debian/Ubuntu + [[ "$os_id" =~ ^(ubuntu|debian|linuxmint|pop_os|raspbian)$ ]] && add_microsoft_repo + fi +} + +function install_mariadb() { + #log "install_mariadb:" + if ! command -v mariadb >/dev/null; then + apt_install "mariadb-server" || return 1 + fi + + # Version anzeigen & Service starten + log "${SYM_OK} MariaDB-Version: $(mariadb --version)" + systemctl start mariadb || { log "${SYM_BAD} MariaDB start fehlgeschlagen!"; return 1; } +} + +function install_dependencies() { + #log "install_dependencies:" + # Dependency-Mapping (erweiterbar!) + declare -A required_packages=( + ["ubuntu"]="git libc6 libgcc-s1 libgssapi-krb5-2 libicu70 liblttng-ust1 libssl3 libstdc++6 libunwind8 zlib1g libgdiplus zip screen" + ["arch"]="git glibc gcc-libs krb5 icu lttng-ust openssl libunwind zlib gdiplus zip screen" + # ... weitere Distros hier ergänzen + ) + + # Fallback-Pakete + local pkg_list=("git" "libc6" "libgcc-s1" "libssl3" "zip" "screen") + [[ -n "${required_packages[$os_id]}" ]] && IFS=' ' read -r -a pkg_list <<< "${required_packages[$os_id]}" + + # Installieren + for pkg in "${pkg_list[@]}"; do + if [[ "$os_id" =~ ^(arch|manjaro)$ ]]; then + pacman_install "$pkg" + else + apt_install "$pkg" + fi + done +} + +# -------------------------- +# 🔧 HELFERFUNKTIONEN (DRY-Prinzip) +# -------------------------- +function apt_install() { + #log "apt_install: $1" + if ! dpkg-query -W "$1" >/dev/null 2>&1; then + log "${SYM_OK} Installiere $1..." + sudo apt-get install -y "$1" || { log "${SYM_BAD} Installation von $1 fehlgeschlagen!"; return 1; } + fi +} + +function pacman_install() { + #log "pacman_install:" + if ! pacman -Qi "$1" >/dev/null 2>&1; then + log "${SYM_OK} Installiere $1..." + sudo pacman -S --noconfirm "$1" || { log "${SYM_BAD} Installation von $1 fehlgeschlagen!"; return 1; } + fi +} + +function add_microsoft_repo() { + #log "add_microsoft_repo:" + # Prüfen, ob das Repo existiert UND funktioniert (via apt-key) + if ! apt-cache policy | grep -q "packages.microsoft.com" || \ + ! grep -q "packages.microsoft.com" /etc/apt/sources.list.d/* 2>/dev/null; then + log "${SYM_INFO} Füge Microsoft-Repository hinzu..." + + # Download & Install mit Fehlerabfrage + if ! wget -qO packages-microsoft-prod.deb "https://packages.microsoft.com/config/ubuntu/22.04/packages-microsoft-prod.deb"; then + log "${SYM_BAD} Download des Microsoft-Repos fehlgeschlagen!" + return 1 + fi + + if ! sudo dpkg -i packages-microsoft-prod.deb >/dev/null; then + log "${SYM_BAD} Installation des Microsoft-Repos fehlgeschlagen!" + rm -f packages-microsoft-prod.deb + return 1 + fi + + rm -f packages-microsoft-prod.deb + sudo apt-get update >/dev/null || { log "${SYM_BAD} apt-get update fehlgeschlagen!"; return 1; } + else + log "${SYM_INFO} Microsoft-Repository ist bereits konfiguriert." + fi +} + +# -------------------------- +# Neuer Servercheck mit installationen ENDE +# -------------------------- + +function servercheck() { + # Direkt kompatible Distributionen: + # Debian 11+ (Bullseye, Bookworm) – Offiziell unterstützt für .NET 8 + # Ubuntu 20.04, 22.04, 24.04 – Microsoft bietet direkt kompatible Pakete + # Linux Mint (basierend auf Ubuntu 20.04 oder 22.04) + # Pop!_OS (System76, basiert auf Ubuntu) + # MX Linux (Debian-basiert, integriert Ubuntu-Funktionen) + # Arch Linux – Offiziell unterstützte Pakete über pacman + # Manjaro – Bietet .NET-Pakete direkt über die Arch-Repositorys + + # Mögliche kompatible Distributionen (mit Anpassungen): + # Kali Linux (basierend auf Debian 12, Anpassungen nötig für .NET-Pakete) + # Zorin OS (Ubuntu-basiert, Unterstützung abhängig von Version) + # elementary OS (Ubuntu-basiert, kann .NET aus Ubuntu-Quellen beziehen) + # Raspberry Pi OS (Debian-basiert, erfordert manuelle Installation für .NET) + + log "${COLOR_HEADING}🔍 Server-Kompatibilitätscheck wird durchgeführt...${COLOR_RESET}" + + rootrights + warn_if_desktop_environment + + # Ermitteln der Distribution und Version + os_id=$(grep '^ID=' /etc/os-release | cut -d= -f2 | tr -d '"') + os_version=$(grep '^VERSION_ID=' /etc/os-release | cut -d= -f2 | tr -d '"') + os_codename=$(grep '^VERSION_CODENAME=' /etc/os-release | cut -d= -f2 | tr -d '"') + + log "${COLOR_LABEL}Server läuft mit:${COLOR_RESET} ${COLOR_SERVER}$os_id $os_version ($os_codename)${COLOR_RESET}" + + # Paketnamen für verschiedene Distributionen definieren + declare -A dotnet_pkg=( + ["ubuntu"]="dotnet-sdk-8.0" + ["debian"]="dotnet-sdk-8.0" + ["linuxmint"]="dotnet-sdk-8.0" + ["pop_os"]="dotnet-sdk-8.0" + ["arch"]="dotnet-sdk" + ["manjaro"]="dotnet-sdk" + ["raspbian"]="dotnet-sdk-8.0" + ) + + # Abhängigkeiten für verschiedene Distributionen + declare -A required_packages=( + ["ubuntu"]="git libc6 libgcc-s1 libgssapi-krb5-2 libicu70 liblttng-ust1 libssl3 libstdc++6 libunwind8 zlib1g libgdiplus zip screen" + ["debian"]="git libc6 libgcc1 libgssapi-krb5-2 libicu67 liblttng-ust1 libssl3 libstdc++6 libunwind8 zlib1g libgdiplus zip screen" + ["linuxmint"]="git libc6 libgcc-s1 libgssapi-krb5-2 libicu70 liblttng-ust1 libssl3 libstdc++6 libunwind8 zlib1g libgdiplus zip screen" + ["pop_os"]="git libc6 libgcc-s1 libgssapi-krb5-2 libicu70 liblttng-ust1 libssl3 libstdc++6 libunwind8 zlib1g libgdiplus zip screen" + ["arch"]="git glibc gcc-libs krb5 icu lttng-ust openssl libunwind zlib gdiplus zip screen" + ["manjaro"]="git glibc gcc-libs krb5 icu lttng-ust openssl libunwind zlib gdiplus zip screen" + ["raspbian"]="git libc6 libgcc1 libgssapi-krb5-2 libicu67 liblttng-ust0 libssl1.1 libstdc++6 libunwind8 zlib1g libgdiplus zip screen" + ) + + # Prüfen, welche .NET-Version installiert werden muss + if [[ "$os_id" == "ubuntu" || "$os_id" == "linuxmint" || "$os_id" == "pop_os" ]]; then + if [[ "$os_version" == "18.04" ]]; then + required_dotnet="dotnet-sdk-6.0" + elif dpkg --compare-versions "$os_version" ge "20.04"; then + required_dotnet=${dotnet_pkg[$os_id]} + + # Spezialfall Ubuntu 24.04 (verwendet jammy Repository) + if [[ "$os_version" == "24.04" ]]; then + log "${SYM_INFO} Ubuntu 24.04 verwendet das .NET Repository für Ubuntu 22.04 (jammy)" + fi + else + log "${SYM_BAD} ${COLOR_WARNING}Nicht unterstützte Ubuntu-Version: $os_version!${COLOR_RESET}" + return 1 + fi + elif [[ "$os_id" == "debian" ]]; then + if [[ "$os_version" -ge "11" ]]; then + required_dotnet=${dotnet_pkg[$os_id]} + else + log "${SYM_BAD} ${COLOR_WARNING}Debian Version $os_version wird nicht unterstützt (mindestens Debian 11 erforderlich)!${COLOR_RESET}" + return 1 + fi + elif [[ "$os_id" == "arch" || "$os_id" == "manjaro" ]]; then + required_dotnet=${dotnet_pkg[$os_id]} + elif [[ "$os_id" == "raspbian" ]]; then + if [[ "$os_version" -ge "11" ]]; then + required_dotnet=${dotnet_pkg[$os_id]} + log "${SYM_INFO} Raspberry Pi OS erfordert möglicherweise manuelle Anpassungen für .NET 8.0" + else + log "${SYM_BAD} ${COLOR_WARNING}Raspberry Pi OS Version $os_version wird nicht unterstützt!${COLOR_RESET}" + return 1 + fi + else + log "${SYM_BAD} ${COLOR_WARNING}Keine unterstützte Distribution für .NET gefunden!${COLOR_RESET}" + return 1 + fi + + # .NET-Installationsstatus prüfen + log "${COLOR_HEADING}🔄 .NET Runtime-Überprüfung:${COLOR_RESET}" + + if [[ "$os_id" == "arch" || "$os_id" == "manjaro" ]]; then + if ! pacman -Qi "$required_dotnet" >/dev/null 2>&1; then + log "${SYM_OK} ${COLOR_ACTION}Installiere ${COLOR_SERVER}$required_dotnet${COLOR_RESET}..." + sudo pacman -S --noconfirm "$required_dotnet" + log "${SYM_OK} ${COLOR_SERVER}$required_dotnet${COLOR_RESET} ${COLOR_ACTION}wurde erfolgreich installiert.${COLOR_RESET}" + else + log "${SYM_OK} ${COLOR_SERVER}$required_dotnet${COLOR_RESET} ${COLOR_ACTION}ist bereits installiert.${COLOR_RESET}" + fi + else + #if ! dpkg -s "$required_dotnet" >/dev/null 2>&1; then + # dpkg-query -W hat eine bessere Fehlerabfrage + if ! dpkg-query -W "$required_dotnet" >/dev/null 2>&1; then + log "${SYM_OK} ${COLOR_ACTION}Installiere ${COLOR_SERVER}$required_dotnet${COLOR_RESET}..." + + # Microsoft Repository hinzufügen für Debian/Ubuntu-basierte Systeme + if [[ "$os_id" == "ubuntu" || "$os_id" == "debian" || "$os_id" == "linuxmint" || "$os_id" == "pop_os" || "$os_id" == "raspbian" ]]; then + log "${SYM_INFO} Füge Microsoft Repository hinzu..." + wget https://packages.microsoft.com/config/ubuntu/22.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb + sudo dpkg -i packages-microsoft-prod.deb + rm packages-microsoft-prod.deb + sudo apt-get update + fi + + sudo apt-get install -y "$required_dotnet" + log "${SYM_OK} ${COLOR_SERVER}$required_dotnet${COLOR_RESET} ${COLOR_ACTION}wurde erfolgreich installiert.${COLOR_RESET}" + else + log "${SYM_OK} ${COLOR_SERVER}$required_dotnet${COLOR_RESET} ${COLOR_ACTION}ist bereits installiert.${COLOR_RESET}" + fi + fi + + # Prüfen, ob MariaDB installiert ist + if ! command -v mariadb >/dev/null 2>&1; then + log "${SYM_BAD} ${COLOR_BAD}MariaDB ist nicht installiert! Ich installiere mariadb über apt.${COLOR_RESET}" + # 20.05.2025 Hier wird vor der installation von MariaDB abgebrochen dabei sollte das spätestens jetzt installiert werden. + sudo apt install mariadb-server + #exit 1 + fi + + # MariaDB-Version anzeigen + mariadb_version=$(mariadb --version) + log "${SYM_OK} ${COLOR_OK}Gefundene MariaDB-Version: ${COLOR_VALUE}$mariadb_version${COLOR_RESET}" + + # Prüfen, ob Dienst läuft + if systemctl is-active --quiet mariadb; then + log "${SYM_OK} ${COLOR_OK}MariaDB-Dienst ist bereits aktiv.${COLOR_RESET}" + else + log "${SYM_STOP} ${COLOR_WARNING}MariaDB ist nicht aktiv. Versuche zu starten...${COLOR_RESET}" + sudo systemctl start mariadb + if systemctl is-active --quiet mariadb; then + log "${SYM_OK} ${COLOR_OK}MariaDB wurde erfolgreich gestartet.${COLOR_RESET}" + else + log "${SYM_BAD} ${COLOR_BAD}Fehler: MariaDB konnte nicht gestartet werden!${COLOR_RESET}" + exit 1 + fi + fi + + # Prüfen, ob Datenbankverzeichnis initialisiert ist + if [ -d /var/lib/mysql/mysql ]; then + log "${SYM_INFO} ${COLOR_LABEL}MariaDB ist bereits initialisiert.${COLOR_RESET}" + else + log "${SYM_TOOLS} ${COLOR_ACTION}Initialisiere MariaDB-Datenbankverzeichnis...${COLOR_RESET}" + sudo mariadb-install-db --user=mysql --basedir=/usr --datadir=/var/lib/mysql + sudo systemctl restart mariadb + log "${SYM_OK} ${COLOR_OK}MariaDB wurde initialisiert und neu gestartet.${COLOR_RESET}" + fi + # MariaDB-Version checken ende + + # Fehlende Pakete prüfen und installieren + log "${COLOR_HEADING}${SYM_PACKAGE} Überprüfe fehlende Pakete...${COLOR_RESET}" + + # Paketliste basierend auf Distribution auswählen + if [[ -n "${required_packages[$os_id]}" ]]; then + IFS=' ' read -r -a pkg_list <<< "${required_packages[$os_id]}" + else + # Fallback für nicht aufgeführte Distributionen + pkg_list=("git" "libc6" "libgcc-s1" "libgssapi-krb5-2" "libicu70" "libssl3" "libstdc++6" "zlib1g" "zip" "screen") + fi + + for package in "${pkg_list[@]}"; do + if [[ "$os_id" == "arch" || "$os_id" == "manjaro" ]]; then + if ! pacman -Qi "$package" >/dev/null 2>&1; then + log "${SYM_OK} ${COLOR_ACTION}Installiere ${COLOR_SERVER}$package${COLOR_RESET}..." + sudo pacman -S --noconfirm "$package" + fi + else + #if ! dpkg -s "$package" >/dev/null 2>&1; then + # dpkg-query -W hat eine bessere Fehlerabfrage + if ! dpkg-query -W "$package" >/dev/null 2>&1; then + log "${SYM_OK} ${COLOR_ACTION}Installiere ${COLOR_SERVER}$package${COLOR_RESET}..." + sudo apt-get install -y "$package" + fi + fi + done + + log "${SYM_OK} ${COLOR_HEADING}Alle benötigten Pakete wurden installiert.${COLOR_RESET}" + blankline +} + +function setup_webserver() { + log "${COLOR_SECTION}=== Web Server Setup (Apache/PHP) ===${COLOR_RESET}" + + rootrights + + # 1. Paketlisten mit erweiterten PHP-Modulen + declare -A web_packages=( + ["ubuntu"]="apache2 php libapache2-mod-php php-mysql php-gd php-curl php-mbstring php-xml php-zip" + ["debian"]="apache2 php libapache2-mod-php php-mysql php-gd php-curl php-mbstring php-xml php-zip" + ["arch"]="apache php php-apache php-gd php-curl php-mysql mariadb-libs php-mbstring php-xml" + ["manjaro"]="apache php php-apache php-gd php-curl php-mysql mariadb-libs php-mbstring php-xml" + ["centos"]="httpd php php-mysqlnd php-gd php-curl php-mbstring php-xml" + ["fedora"]="httpd php php-mysqlnd php-gd php-curl php-mbstring php-xml" + ) + + # 2. Installation + case $current_distro in + ubuntu|debian|linuxmint|pop|raspbian) + log "${SYM_INFO} ${COLOR_ACTION}Installiere Apache/PHP (apt)...${COLOR_RESET}" + sudo apt-get update + sudo apt-get install -y "${web_packages[ubuntu]}" || { + log "${SYM_BAD} ${COLOR_ERROR}Installation fehlgeschlagen!${COLOR_RESET}"; + return 1; + } + sudo a2enmod rewrite + sudo systemctl restart apache2 + ;; + + arch|manjaro) + log "${SYM_INFO} ${COLOR_ACTION}Installiere Apache/PHP (pacman)...${COLOR_RESET}" + sudo pacman -Sy --noconfirm "${web_packages[arch]}" || { + log "${SYM_BAD} ${COLOR_ERROR}Installation fehlgeschlagen!${COLOR_RESET}"; + return 1; + } + + # Apache-Konfiguration für PHP + sudo sed -i 's/LoadModule mpm_event_module/#LoadModule mpm_event_module/' /etc/httpd/conf/httpd.conf + sudo sed -i 's/#LoadModule mpm_prefork_module/LoadModule mpm_prefork_module/' /etc/httpd/conf/httpd.conf + log "\n# PHP-Konfiguration\nLoadModule php_module modules/libphp.so\nAddHandler php-script .php\nInclude conf/extra/php_module.conf" | sudo tee -a /etc/httpd/conf/httpd.conf >/dev/null + + sudo systemctl restart httpd + ;; + + centos|fedora) + log "${SYM_INFO} ${COLOR_ACTION}Installiere Apache/PHP (yum/dnf)...${COLOR_RESET}" + [[ "$current_distro" == "centos" ]] && sudo yum install -y epel-release + sudo yum install -y "${web_packages[centos]}" || { + log "${SYM_BAD} ${COLOR_ERROR}Installation fehlgeschlagen!${COLOR_RESET}"; + return 1; + } + sudo systemctl restart httpd + sudo firewall-cmd --permanent --add-service=http + sudo firewall-cmd --reload + ;; + esac + + # 3. PHP-Check (OHNE Dateianlage) + log "${SYM_INFO} ${COLOR_ACTION}Prüfe PHP-Konfiguration...${COLOR_RESET}" + php_check() { + log "${COLOR_HEADING}=== PHP-Status ===${COLOR_RESET}" + log "Version: ${COLOR_VALUE}$(php -r 'echo PHP_VERSION;')${COLOR_RESET}" + + declare -A modules=( + ["mysqli"]="MySQL-Datenbank" + ["gd"]="GD-Grafikbibliothek" + ["curl"]="cURL" + ["xml"]="XML-Unterstützung" + ["mbstring"]="Multibyte-Strings" + ) + + for mod in "${!modules[@]}"; do + if php -m | grep -q "^$mod$"; then + log "${SYM_OK} ${COLOR_OK}${modules[$mod]} (${mod})${COLOR_RESET}" + else + log "${SYM_BAD} ${COLOR_ERROR}Fehlend: ${modules[$mod]} (${mod})${COLOR_RESET}" + fi + done + } + + # Führe Check aus und protokolliere Ausgabe + php_check | while IFS= read -r line; do log "$line"; done + + # 4. osWebinterface Deployment + if [[ -d "oswebinterface" ]]; then + log "${SYM_INFO} ${COLOR_ACTION}Kopiere osWebinterface...${COLOR_RESET}" + sudo cp -r oswebinterface/* /var/www/html/ + sudo chown -R www-data:www-data /var/www/html 2>/dev/null || sudo chown -R apache:apache /var/www/html + + log "${SYM_OK} ${COLOR_OK}osWebinterface erfolgreich deployt${COLOR_RESET}" + fi + + log "${COLOR_SUCCESS}Webserver-Setup abgeschlossen!${COLOR_RESET}" + blankline +} + +# function setup_email_server() { +# # SMTP-Server Konfiguration (z.B. mail.gridname.de) +# local smtp_server="mail.gridname.de" +# local sender_email="noresponse@gridname.de" +# local sender_password="DEIN_PASSWORT" # Passwort für noresponse@ + +# echo -e "\n\033[32m=== Konfiguriere E-Mail-Server für PHP mail() ===\033[0m" + +# # Postfix installieren +# sudo apt update +# sudo DEBIAN_FRONTEND=noninteractive apt install -y postfix mailutils + +# # Postfix als SMTP-Relay einrichten +# sudo tee /etc/postfix/main.cf > /dev/null <&2 + return 2 # Spezieller Fehlercode für ungültige Eingabe + fi + screen -list | grep -q "$target_session" # Rückgabewert 0 = läuft, 1 = nicht läuft +} + +function standalonestart() { + if is_opensim_running; then + echo "OpenSim läuft bereits (Screen: $SCREEN_NAME)." + else + echo "Starte OpenSim in screen '$SCREEN_NAME'..." + cd "$OPENSIM_DIR" || exit 1 + screen -fa -S "$SCREEN_NAME" -d -U -m dotnet OpenSim.dll + echo "OpenSim starten abgeschlossen." + fi + blankline +} + +function standalonestop() { + if is_opensim_running; then + echo "Sende Shutdown-Befehl an OpenSim..." + screen -S "$SCREEN_NAME" -p 0 -X stuff "$SHUTDOWN_CMD^M" + sleep 5 # Warte auf saubere Beendigung + if ! is_opensim_running; then + echo "OpenSim wurde gestoppt." + else + echo "Warnung: OpenSim reagiert nicht auf 'shutdown'. Versuche hartes Stoppen..." + screen -S "$SCREEN_NAME" -X quit + fi + else + echo "OpenSim läuft nicht." + fi + blankline +} + +# Standalone-Service-Neustart (ohne Logbereinigung) +function standalonerestart() { + standalonestop + sleep $Simulator_Stop_wait # Wartezeit für sauberen Shutdown + standalonestart +} + +function standalonestatus() { + if is_opensim_running; then + echo "✅ OpenSim läuft (Screen: $SCREEN_NAME)." + else + echo "❌ OpenSim läuft nicht." + fi +} + +function validate_sim_name() { + local sim_name=$1 + # Überprüfe ob der Name dem Muster sim[1-99] entspricht (case-sensitive) + if [[ ! "$sim_name" =~ ^sim([1-9]|[1-9][0-9])$ ]]; then + log "${SYM_BAD} ${COLOR_WARNING}Ungültiger Sim-Name! Bitte verwende sim1 bis sim99.${COLOR_RESET}" + return 1 + fi + return 0 +} + +# Einzelne Instanzen starten und stoppen + +function simstart() { + local sim_name="$1" + local sim_dir="${sim_name}/bin" + local max_wait=${Simulator_Start_wait:-10} # Default 10 Sekunden Timeout + + # 1. Validierung + if ! validate_sim_name "$sim_name"; then + log "${SYM_BAD} ${COLOR_ERROR}Ungültiger Simulationsname!${COLOR_RESET}" + return 1 + fi + + # 2. Prüfen ob die Simulation bereits läuft + if is_opensim_running "$sim_name"; then + log "${SYM_INFO} ${COLOR_WARNING}Simulation '${sim_name}' läuft bereits!${COLOR_RESET}" + blankline + return 0 + fi + + # 3. Verzeichnisprüfung + if [[ ! -d "$sim_dir" ]]; then + log "${SYM_BAD} ${COLOR_ERROR}Verzeichnis nicht gefunden: ${sim_dir}${COLOR_RESET}" + return 1 + fi + + # 4. Startversuch + log "${SYM_OK} ${COLOR_START}Starte ${COLOR_SERVER}${sim_name}${COLOR_RESET}" + if ! (cd "$sim_dir" && screen -fa -S "$sim_name" -d -U -m dotnet OpenSim.dll); then + log "${SYM_BAD} ${COLOR_ERROR}Start fehlgeschlagen!${COLOR_RESET}" + return 1 + fi + + # 5. Startüberprüfung mit Timeout + local waited=0 + while ! is_opensim_running "$sim_name" && ((waited < max_wait)); do + sleep 1 + ((waited++)) + echo -n "." + done + echo "" + + # 6. Ergebnis auswerten + if is_opensim_running "$sim_name"; then + log "${SYM_OK} ${COLOR_START}Simulation erfolgreich gestartet (${waited}s).${COLOR_RESET}" + blankline + return 0 + else + log "${SYM_BAD} ${COLOR_ERROR}Timeout: Simulation startet nicht (${max_wait}s)${COLOR_RESET}" + return 1 + fi +} + +function simstop() { + local simstop="$1" + local max_wait=${Simulator_Stop_wait:-15} # Default 15 Sekunden Timeout + + # 1. Validierung + if ! validate_sim_name "$simstop"; then + log "${SYM_BAD} ${COLOR_ERROR}Ungültiger Simulationsname!${COLOR_RESET}" + return 1 + fi + + # 2. Prüfen ob die Simulation läuft + if ! is_opensim_running "$simstop"; then + log "${SYM_INFO} ${COLOR_WARNING}Simulation '$simstop' ist nicht aktiv.${COLOR_RESET}" + blankline + return 0 + fi + + # 3. Stop-Befehl senden + log "${SYM_OK} ${COLOR_STOP}Stoppe ${COLOR_SERVER}${simstop}${COLOR_RESET}" + if ! screen -S "$simstop" -p 0 -X stuff "shutdown^M"; then + log "${SYM_BAD} ${COLOR_ERROR}Konnte Stop-Befehl nicht senden!${COLOR_RESET}" + return 1 + fi + + # 4. Graceful Shutdown mit Timeout + local waited=0 + while is_opensim_running "$simstop" && ((waited < max_wait)); do + sleep 1 + ((waited++)) + echo -n "." + done + echo "" + + # 5. Ergebnis auswerten + if is_opensim_running "$simstop"; then + log "${SYM_BAD} ${COLOR_ERROR}Timeout: Simulation reagiert nicht auf shutdown (${max_wait}s)${COLOR_RESET}" + log "${SYM_INFO} ${COLOR_WARNING}Versuche hartes Stoppen...${COLOR_RESET}" + screen -S "$simstop" -X quit && return 0 || return 1 + else + log "${SYM_OK} ${COLOR_STOP}Simulation sauber gestoppt (${waited}s).${COLOR_RESET}" + blankline + return 0 + fi +} + +function simrestart() { + local simrestart=$1 + if ! validate_sim_name "$simrestart"; then + exit 1 + fi + + simstop "$simrestart" + simstart "$simrestart" + echo + echo "OpenSim Restart abgeschlossen." +} + +#?────────────────────────────────────────────────────────────────────────────────────────── +#* Start Stop Grid +#?────────────────────────────────────────────────────────────────────────────────────────── + +#* OpenSim starten (robust → money → sim1 bis sim999) +function opensimstart() { + log "${SYM_WAIT} ${COLOR_START}Starte das Grid!${COLOR_RESET}" + + # RobustServer starten + if [[ -d "robust/bin" && -f "robust/bin/Robust.dll" ]]; then + log "${SYM_OK} ${COLOR_START}Starte ${COLOR_SERVER}RobustServer ${COLOR_RESET} ${COLOR_START}aus ${COLOR_DIR}robust/bin...${COLOR_RESET}" + cd robust/bin || exit 1 + # -inifile=Robust.HG.ini + + screen -fa -S robustserver -d -U -m dotnet Robust.dll + cd - >/dev/null 2>&1 || exit 1 + sleep $RobustServer_Start_wait + else + log "${SYM_BAD} ${COLOR_SERVER}RobustServer: ${COLOR_BAD}Robust.dll nicht gefunden.${COLOR_RESET} ${COLOR_START}Überspringe Start.${COLOR_RESET}" + fi + + # MoneyServer starten + if [[ -f "robust/bin/MoneyServer.dll" ]]; then + log "${SYM_OK} ${COLOR_START}Starte ${COLOR_SERVER}MoneyServer${COLOR_RESET} ${COLOR_START}aus ${COLOR_DIR}robust/bin...${COLOR_RESET}" + cd robust/bin || exit 1 + screen -fa -S moneyserver -d -U -m dotnet MoneyServer.dll + cd - >/dev/null 2>&1 || exit 1 + sleep $MoneyServer_Start_wait + else + log "${SYM_BAD} ${COLOR_SERVER}MoneyServer: ${COLOR_BAD}MoneyServer.dll nicht gefunden.${COLOR_RESET} ${COLOR_START}Überspringe Start.${COLOR_RESET}" + fi + + # Sim-Regionen starten + for ((i=1; i<=999; i++)); do + sim_dir="sim$i/bin" + if [[ -d "$sim_dir" && -f "$sim_dir/OpenSim.dll" ]]; then + log "${SYM_OK} ${COLOR_START}Starte ${COLOR_SERVER}sim$i${COLOR_RESET} ${COLOR_START}aus ${COLOR_DIR}$sim_dir...${COLOR_RESET}" + cd "$sim_dir" || continue + screen -fa -S "sim$i" -d -U -m dotnet OpenSim.dll + cd - >/dev/null 2>&1 || continue + sleep $Simulator_Start_wait + fi + done + echo "OpenSim starten abgeschlossen." + blankline +} +# Test OpenSim starten Parallel +function opensimstartParallel() { + log "${SYM_WAIT} ${COLOR_START}Starte das Grid!${COLOR_RESET}" + + # 1. RobustServer (muss zuerst laufen) + if [[ -d "robust/bin" && -f "robust/bin/Robust.dll" ]]; then + log "${SYM_OK} ${COLOR_START}Starte ${COLOR_SERVER}RobustServer ${COLOR_RESET} ${COLOR_START}aus ${COLOR_DIR}robust/bin...${COLOR_RESET}" + (cd robust/bin && screen -fa -S robustserver -d -U -m dotnet Robust.dll) + sleep "$RobustServer_Start_wait" + else + log "${SYM_BAD} ${COLOR_SERVER}RobustServer: ${COLOR_BAD}Robust.dll nicht gefunden.${COLOR_RESET} ${COLOR_START}Überspringe Start.${COLOR_RESET}" + fi + + # 2. MoneyServer (optional) + if [[ -f "robust/bin/MoneyServer.dll" ]]; then + log "${SYM_OK} ${COLOR_START}Starte ${COLOR_SERVER}MoneyServer${COLOR_RESET} ${COLOR_START}aus ${COLOR_DIR}robust/bin...${COLOR_RESET}" + (cd robust/bin && screen -fa -S moneyserver -d -U -m dotnet MoneyServer.dll) + sleep "$MoneyServer_Start_wait" + fi + + # 3. SIM1 ZUERST (sequentiell) + if [[ -d "sim1/bin" && -f "sim1/bin/OpenSim.dll" ]]; then + log "${SYM_OK} ${COLOR_START}Starte ${COLOR_SERVER}sim1${COLOR_RESET} ${COLOR_START}(primäre Region)...${COLOR_RESET}" + (cd sim1/bin && screen -fa -S sim1 -d -U -m dotnet OpenSim.dll) + sleep "$Simulator_Start_wait" + else + log "${SYM_BAD} ${COLOR_SERVER}sim1: ${COLOR_BAD}OpenSim.dll nicht gefunden.${COLOR_RESET}" + fi + + # 4. SIM2-SIM99 PARALLEL + log "${SYM_INFO} ${COLOR_START}Starte sekundäre Simulatoren parallel...${COLOR_RESET}" + + # Maximal 10 parallele Jobs + MAX_JOBS=10 + for ((i=2; i<=99; i++)); do + sim_dir="sim$i/bin" + if [[ -d "$sim_dir" && -f "$sim_dir/OpenSim.dll" ]]; then + + # Warten falls zu viele Jobs laufen + while (( $(jobs -p | wc -l) >= MAX_JOBS )); do + sleep 1 + done + + log "${SYM_OK} ${COLOR_START}Starte ${COLOR_SERVER}sim$i${COLOR_RESET} ${COLOR_START}aus ${COLOR_DIR}$sim_dir...${COLOR_RESET}" + (cd "$sim_dir" && screen -fa -S "sim$i" -d -U -m dotnet OpenSim.dll) & + fi + done + + wait # Warte auf alle Hintergrundjobs + log "${SYM_OK} ${COLOR_START}Alle Simulatoren gestartet.${COLOR_RESET}" + blankline +} + +#* OpenSim stoppen (sim999 bis sim1 → money → robust) +function opensimstop() { + log "${SYM_WAIT} ${COLOR_STOP}Stoppe das Grid!${COLOR_RESET}" + + # Sim-Regionen stoppen + for ((i=999; i>=1; i--)); do + sim_dir="sim$i" + if screen -list | grep -q "$sim_dir"; then + screen -S "sim$i" -p 0 -X stuff "shutdown^M" + log "${SYM_OK} ${COLOR_STOP}Stoppe ${COLOR_SERVER}sim$i${COLOR_RESET} ${COLOR_STOP}...${COLOR_RESET}" + sleep $Simulator_Stop_wait + fi + done + + # MoneyServer stoppen + if screen -list | grep -q "moneyserver"; then + log "${SYM_OK} ${COLOR_STOP}Stoppe ${COLOR_SERVER}MoneyServer${COLOR_RESET} ${COLOR_STOP}...${COLOR_RESET}" + screen -S moneyserver -p 0 -X stuff "shutdown^M" + sleep $MoneyServer_Stop_wait + else + log "${SYM_BAD} ${COLOR_SERVER}MoneyServer: ${COLOR_BAD}Läuft nicht.${COLOR_RESET} ${COLOR_STOP}Überspringe Stopp.${COLOR_RESET}" + fi + + # RobustServer stoppen + if screen -list | grep -q "robust"; then + log "${SYM_OK} ${COLOR_STOP}Stoppe ${COLOR_SERVER}RobustServer${COLOR_RESET} ${COLOR_STOP}...${COLOR_RESET}" + screen -S robustserver -p 0 -X stuff "shutdown^M" + sleep $RobustServer_Stop_wait + else + log "${SYM_BAD} ${COLOR_SERVER}RobustServer: ${COLOR_BAD}Läuft nicht.${COLOR_RESET} ${COLOR_STOP}Überspringe Stopp.${COLOR_RESET}" + fi + echo "OpenSim stoppen abgeschlossen." + blankline +} + +# Das parallele Stoppen in der Reihenfolge sim999 → sim2 parallel, dann sim1 → MoneyServer → RobustServer +function opensimstopParallel() { + log "${SYM_WAIT} ${COLOR_STOP}Stoppe das Grid!${COLOR_RESET}" + local MAX_PARALLEL=10 # Maximale parallele Stopp-Vorgänge + + # 1. Paralleles Stoppen von sim999 bis sim2 + log "${SYM_INFO} ${COLOR_STOP}Stoppe sekundäre Simulatoren parallel...${COLOR_RESET}" + for ((i=999; i>=2; i--)); do + sim_name="sim$i" + + # Warten falls zu viele parallele Jobs laufen + #while [ $(jobs -p | wc -l) -ge $MAX_PARALLEL ]; do + while [ "$(jobs -p | wc -l)" -ge $MAX_PARALLEL ]; do + sleep 0.5 + done + + if screen -list | grep -q "$sim_name"; then + log "${SYM_OK} ${COLOR_STOP}Stoppe ${COLOR_SERVER}$sim_name${COLOR_RESET}" + ( + screen -S "$sim_name" -p 0 -X stuff "shutdown^M" + sleep $Simulator_Stop_wait + ) & + fi + done + wait # Warte auf alle parallelen Stopp-Vorgänge + + # 2. Sim1 sequentiell stoppen + if screen -list | grep -q "sim1"; then + log "${SYM_OK} ${COLOR_STOP}Stoppe ${COLOR_SERVER}sim1${COLOR_RESET} ${COLOR_STOP}(primäre Region)...${COLOR_RESET}" + screen -S "sim1" -p 0 -X stuff "shutdown^M" + sleep $Simulator_Stop_wait + fi + + # 3. MoneyServer stoppen + if screen -list | grep -q "moneyserver"; then + log "${SYM_OK} ${COLOR_STOP}Stoppe ${COLOR_SERVER}MoneyServer${COLOR_RESET} ${COLOR_STOP}...${COLOR_RESET}" + screen -S moneyserver -p 0 -X stuff "shutdown^M" + sleep $MoneyServer_Stop_wait + else + log "${SYM_BAD} ${COLOR_SERVER}MoneyServer: ${COLOR_BAD}Läuft nicht.${COLOR_RESET}" + fi + + # 4. RobustServer stoppen (zuletzt) + if screen -list | grep -q "robustserver"; then + log "${SYM_OK} ${COLOR_STOP}Stoppe ${COLOR_SERVER}RobustServer${COLOR_RESET} ${COLOR_STOP}...${COLOR_RESET}" + screen -S robustserver -p 0 -X stuff "shutdown^M" + sleep $RobustServer_Stop_wait + else + log "${SYM_BAD} ${COLOR_SERVER}RobustServer: ${COLOR_BAD}Läuft nicht.${COLOR_RESET}" + fi + + log "${SYM_OK} ${COLOR_STOP}Alle Dienste gestoppt.${COLOR_RESET}" + blankline +} + +# check_screens ist eine Grid Funktion und funktioniert nicht im Standalone. +function check_screens() { + # echo "Überprüfung der laufenden OpenSim-Prozesse..." + + restart_all=false + + # Überprüfen, ob RobustServer läuft + if [[ -d "robust/bin" && -f "robust/bin/Robust.dll" ]]; then + screenRO=$(screen -ls | grep -w "robustserver") + if [[ -z "$screenRO" ]]; then + echo "$(date '+%Y-%m-%d %H:%M:%S') - RobustServer läuft nicht und muss neu gestartet werden." >> ProblemRestarts.log + restart_all=true + fi + fi + + # Überprüfen, ob MoneyServer läuft + if [[ -d "robust/bin" && -f "robust/bin/MoneyServer.dll" ]];then + screenMoney=$(screen -ls | grep -w "moneyserver") + if [[ -z "$screenMoney" ]]; then + echo "$(date '+%Y-%m-%d %H:%M:%S') - MoneyServer läuft nicht und muss neu gestartet werden." >> ProblemRestarts.log + restart_all=true + fi + fi + + # Überprüfen, ob sim1 läuft + if [[ -d "sim1/bin" && -f "sim1/bin/OpenSim.dll" ]]; then + screenSim1=$(screen -ls | grep -w "sim1") + if [[ -z "$screenSim1" ]]; then + echo "$(date '+%Y-%m-%d %H:%M:%S') - Sim1 läuft nicht und muss neu gestartet werden." >> ProblemRestarts.log + restart_all=true + fi + fi + + # Falls eines der kritischen Systeme ausgefallen ist, alles neu starten + if [[ "$restart_all" == true ]]; then + echo "$(date '+%Y-%m-%d %H:%M:%S') - Kritische Prozesse sind ausgefallen! OpenSim wird komplett neu gestartet." >> ProblemRestarts.log + # opensimstart + opensimrestart + return 0 + fi + + # Überprüfen, ob andere Regionen (sim2 bis sim999) einzeln neu gestartet werden müssen + for ((i=2; i<=999; i++)); do + sim_dir="sim$i/bin" + if [[ -d "$sim_dir" && -f "$sim_dir/OpenSim.dll" ]]; then + screenSim=$(screen -ls | grep -w "sim$i") + if [[ -z "$screenSim" ]]; then + echo "$(date '+%Y-%m-%d %H:%M:%S') - Sim$i läuft nicht und wird einzeln neu gestartet." >> ProblemRestarts.log + cd "$sim_dir" || continue + screen -fa -S "sim$i" -d -U -m dotnet OpenSim.dll + cd - >/dev/null 2>&1 || continue + sleep $Simulator_Start_wait + fi + fi + done + blankline +} + +# Falls ein Screen einmal nicht beenden will. +function simkill() { + local SIMKILL=$1 + local CONFIRM + + # Existenzprüfung + if ! screen -list | grep -q "$SIMKILL"; then + log "${SYM_BAD} ${COLOR_ERROR}Screen '${COLOR_SERVER}$SIMKILL${COLOR_ERROR}' nicht gefunden!${COLOR_RESET}" + return 1 + fi + + # Bestätigung + log "${SYM_WARNING} ${COLOR_WARNING}Warnung:${COLOR_RESET}" + read -rp "Soll Screen '${COLOR_SERVER}$SIMKILL${COLOR_RESET}' wirklich beendet werden? [y/N] " CONFIRM + [[ $CONFIRM =~ ^[Yy]$ ]] || { + log "${SYM_INFO} ${COLOR_ACTION}Abbruch durch Benutzer.${COLOR_RESET}" + return 1 + } + + # Erst ordnungsgemäß beenden versuchen + log "${SYM_WAIT} ${COLOR_ACTION}Versuche ordentliches Beenden...${COLOR_RESET}" + if screen -S "$SIMKILL" -X quit; then + log "${SYM_OK} ${COLOR_SUCCESS}Screen '${COLOR_SERVER}$SIMKILL${COLOR_SUCCESS}' erfolgreich beendet.${COLOR_RESET}" + return 0 + fi + + # Falls nötig, hart killen + log "${SYM_WARNING} ${COLOR_WARNING}Ordentliches Beenden fehlgeschlagen, erzwinge Kill...${COLOR_RESET}" + if screen -S "$SIMKILL" -X kill; then + log "${SYM_OK} ${COLOR_SUCCESS}Screen '${COLOR_SERVER}$SIMKILL${COLOR_SUCCESS}' wurde gekillt.${COLOR_RESET}" + return 0 + else + log "${SYM_BAD} ${COLOR_ERROR}Schwerer Fehler: Screen '${COLOR_SERVER}$SIMKILL${COLOR_ERROR}' konnte nicht beendet werden!${COLOR_RESET}" + return 1 + fi +} + +#?────────────────────────────────────────────────────────────────────────────────────────── +#* Erstellen eines OpenSimulators +#?────────────────────────────────────────────────────────────────────────────────────────── + +opensim_clone() { + # Stille Klon-Funktion mit automatischem Fallback + local max_retries=3 + local retry_delay=2 + + for ((i=1; i<=max_retries; i++)); do + # 1. Versuch: HTTPS (standard) + if git clone https://opensimulator.org/git/opensim opensim --quiet 2>/dev/null; then + return 0 + fi + + # 2. Versuch: Git-Protokoll (falls HTTPS blockiert) + if git clone git://opensimulator.org/git/opensim opensim --quiet 2>/dev/null; then + return 0 + fi + + # Bei Fehler temporäres Verzeichnis bereinigen + rm -rf opensim &>/dev/null + sleep $retry_delay + done + + return 1 +} + +function opensimgitcopy() { + log "${SYM_BOOK}${COLOR_HEADING} OpenSimulator GitHub-Verwaltung${COLOR_RESET}" + + # Zuerst versuchen, ein einfaches git pull durchzuführen, falls Repository existiert + if [[ -d "opensim/.git" ]]; then + log "${SYM_WAIT} ${COLOR_ACTION}Versuche einfaches Update via git pull...${COLOR_RESET}" + cd opensim || { + log "${SYM_BAD} ${COLOR_ERROR}Verzeichniswechsel fehlgeschlagen!${COLOR_RESET}" + return 1 + } + + old_head=$(git rev-parse HEAD) + if git pull origin master; then + if [ "$old_head" == "$(git rev-parse HEAD)" ]; then + log "${SYM_OK} ${COLOR_ACTION}Keine neuen Updates verfügbar.${COLOR_RESET}" + else + log "${SYM_OK} ${COLOR_SUCCESS}Update erfolgreich durchgeführt!${COLOR_RESET}" + fi + cd .. + + # Nach erfolgreichem Update Kompatibilität prüfen + check_dotnet_compatibility + # Sourcecode Version vor dem kompilieren einstellen mit der Funktion versionrevision() + versionrevision + log # Leerzeile für bessere Lesbarkeit + return 0 + else + log "${SYM_BAD} ${COLOR_WARNING}Einfaches Update fehlgeschlagen, fahre mit Standardverfahren fort...${COLOR_RESET}" + cd .. + fi + fi + + # Falls einfaches git pull nicht möglich ist, normale Abfrage durchführen + log "${COLOR_LABEL}Möchten Sie den OpenSimulator vom GitHub verwenden oder aktualisieren? (${COLOR_OK}[upgrade]${COLOR_LABEL}/new)${COLOR_RESET}" + read -r user_choice + user_choice=${user_choice:-upgrade} + + if [[ "$user_choice" == "new" ]]; then + if [[ -d "opensim" ]]; then + log "${COLOR_ACTION}Vorhandene OpenSimulator-Version wird gelöscht...${COLOR_RESET}" + rm -rf opensim + log "${SYM_OK} ${COLOR_ACTION}Alte OpenSimulator-Version wurde erfolgreich entfernt.${COLOR_RESET}" + fi + + log "${SYM_WAIT} ${COLOR_ACTION}OpenSimulator wird von GitHub geholt...${COLOR_RESET}" + if opensim_clone; then + log "${SYM_OK} ${COLOR_SUCCESS}Download erfolgreich!${COLOR_RESET}" + log "${SYM_WAIT} ${COLOR_ACTION}Überprüfe Repository-Integrität...${COLOR_RESET}" + + if check_repo_integrity "opensim"; then + log "${SYM_OK} ${COLOR_ACTION}Repository ist intakt.${COLOR_RESET}" + else + log "${SYM_BAD} ${COLOR_ERROR}Repository beschädigt!${COLOR_RESET}" + return 1 + fi + else + log "${SYM_BAD} ${COLOR_ERROR}Download fehlgeschlagen!${COLOR_RESET}" + log "${COLOR_WARNING}Bitte Netzwerkverbindung prüfen und später erneut versuchen.${COLOR_RESET}" + return 1 + fi + + elif [[ "$user_choice" == "upgrade" ]]; then + if [[ -d "opensim/.git" ]]; then + # Erneuter Versuch (falls vorheriger pull fehlgeschlagen) + cd opensim || return 1 + if git pull origin master; then + log "${SYM_OK} ${COLOR_SUCCESS}Update erfolgreich durchgeführt!${COLOR_RESET}" + cd .. + + # Nach erfolgreichem Update Kompatibilität prüfen + check_dotnet_compatibility + # Sourcecode Version vor dem kompilieren einstellen mit der Funktion versionrevision() + versionrevision + log # Leerzeile für bessere Lesbarkeit + return 0 + else + log "${SYM_BAD} ${COLOR_ERROR}Update fehlgeschlagen!${COLOR_RESET}" + cd .. + return 1 + fi + else + log "${COLOR_WARNING}${SYM_WARNING} ${COLOR_ACTION}Kein Repository gefunden. Starte Neuinstallation...${COLOR_RESET}" + opensimgitcopy "new" + return $? + fi + else + log "${SYM_BAD} ${COLOR_ERROR}Ungültige Eingabe!${COLOR_RESET}" + return 1 + fi + + # .NET-Kompatibilität prüfen + check_dotnet_compatibility + # Sourcecode Version vor dem kompilieren einstellen mit der Funktion versionrevision() + versionrevision + + blankline +} + +check_dotnet_compatibility() { + cd opensim || return 1 + local dotnet_version + dotnet_version=$(dotnet --version 2>/dev/null | awk -F. '{print $1}') + + case $dotnet_version in + 6) + git checkout -q dotnet6 + log "${SYM_OK} ${COLOR_ACTION}Für .NET 6 konfiguriert.${COLOR_RESET}" + ;; + 7|8) + log "${SYM_OK} ${COLOR_ACTION}Kompatibel mit .NET $dotnet_version.${COLOR_RESET}" + ;; + *) + log "${COLOR_WARNING}${SYM_WARNING} ${COLOR_ACTION}Keine .NET-Version erkannt. Verwende Standard.${COLOR_RESET}" + ;; + esac + cd .. +} + +function moneygitcopy() { + log "${COLOR_HEADING}💰 MoneyServer GitHub-Verwaltung${COLOR_RESET}" + + log "${COLOR_LABEL}Möchten Sie den MoneyServer vom GitHub verwenden oder aktualisieren? (${COLOR_OK}[upgrade]${COLOR_LABEL}/new)${COLOR_RESET}" + read -r user_choice + user_choice=${user_choice:-upgrade} + + if [[ "$user_choice" == "new" ]]; then + if [[ -d "opensimcurrencyserver" ]]; then + log "${COLOR_ACTION}Vorhandene MoneyServer-Version wird gelöscht...${COLOR_RESET}" + rm -rf opensimcurrencyserver + log "${SYM_OK} ${COLOR_ACTION}Alte MoneyServer-Version wurde erfolgreich entfernt.${COLOR_RESET}" + fi + + log "${COLOR_ACTION}MONEYSERVER: MoneyServer wird vom GIT geholt...${COLOR_RESET}" + if ! git clone https://github.com/ManfredAabye/opensimcurrencyserver-dotnet.git opensimcurrencyserver; then + log "${SYM_BAD} ${COLOR_ERROR}Fehler beim Klonen!${COLOR_RESET}" + return 1 + fi + + if ! check_repo_integrity "opensimcurrencyserver"; then + log "${SYM_BAD} ${COLOR_ERROR}MoneyServer-Repository beschädigt!${COLOR_RESET}" + return 1 + fi + + log "${SYM_OK} ${COLOR_ACTION}MoneyServer wurde erfolgreich heruntergeladen.${COLOR_RESET}" + + elif [[ "$user_choice" == "upgrade" ]]; then + if [[ -d "opensimcurrencyserver/.git" ]]; then + log "${SYM_OK} ${COLOR_ACTION}Repository gefunden. Aktualisiere mit 'git pull'...${COLOR_RESET}" + cd opensimcurrencyserver || { log "${SYM_BAD} ${COLOR_ERROR}Fehler: Kann nicht ins Verzeichnis wechseln!${COLOR_RESET}"; return 1; } + + # Branch-Erkennung mit Fehlerbehandlung + branch_name=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@^refs/remotes/origin/@@') + if [ -z "$branch_name" ]; then + branch_name="master" + log "${COLOR_WARNING}Standard-Branch 'master' wird verwendet.${COLOR_RESET}" + fi + + old_head=$(git rev-parse HEAD) + + if ! git pull origin "$branch_name"; then + log "${SYM_BAD} ${COLOR_ERROR}Fehler beim Aktualisieren!${COLOR_RESET}" + cd .. + return 1 + fi + + if [ "$old_head" == "$(git rev-parse HEAD)" ]; then + log "${COLOR_OK}${SYM_OKNN} ${COLOR_ACTION}MoneyServer ist bereits aktuell.${COLOR_RESET}" + else + log "${COLOR_OK}${SYM_OKNN} ${COLOR_ACTION}MoneyServer erfolgreich aktualisiert!${COLOR_RESET}" + fi + + if ! check_repo_integrity; then + log "${SYM_BAD} ${COLOR_ERROR}Repository beschädigt nach Pull!${COLOR_RESET}" + cd .. + return 1 + fi + + cd .. + else + log "${COLOR_WARNING}${SYM_WARNING} ${COLOR_ACTION}MoneyServer-Verzeichnis nicht gefunden. Klone Repository neu...${COLOR_RESET}" + if ! git clone https://github.com/ManfredAabye/opensimcurrencyserver-dotnet.git opensimcurrencyserver; then + log "${SYM_BAD} ${COLOR_ERROR}Fehler beim Klonen!${COLOR_RESET}" + return 1 + fi + log "${COLOR_OK}${SYM_OKNN} ${COLOR_ACTION}MoneyServer erfolgreich heruntergeladen!${COLOR_RESET}" + fi + else + log "${SYM_BAD} ${COLOR_ERROR}Abbruch: Keine Aktion durchgeführt.${COLOR_RESET}" + return 1 + fi + + # Verbesserte Dateikopierfunktion mit Prüfungen + copy_with_checks() { + local src=$1 + local dest=$2 + + if [ ! -d "$src" ]; then + log "${SYM_BAD} ${COLOR_ERROR}Quellverzeichnis $src nicht gefunden!${COLOR_RESET}" + return 1 + fi + + mkdir -p "$dest" + if ! cp -r "$src" "$dest"; then + log "${SYM_BAD} ${COLOR_ERROR}Fehler beim Kopieren von $src!${COLOR_RESET}" + return 1 + fi + + log "${SYM_OK} ${COLOR_ACTION}Erfolgreich kopiert: $src → $dest${COLOR_RESET}" + return 0 + } + + # Kopiervorgänge mit Fehlerbehandlung + copy_with_checks "opensimcurrencyserver/addon-modules" "opensim/" || return 1 + copy_with_checks "opensimcurrencyserver/bin" "opensim/" || return 1 + + blankline + return 0 +} + +function osslscriptsgit() { + log "${COLOR_HEADING}📜 OSSL Beispiel-Skripte GitHub-Verwaltung${COLOR_RESET}" + + log "${COLOR_LABEL}Möchten Sie die OpenSim OSSL Beispiel-Skripte vom GitHub verwenden oder aktualisieren? (${COLOR_OK}[upgrade]${COLOR_LABEL}/new)${COLOR_RESET}" + read -r user_choice + user_choice=${user_choice:-upgrade} + + repo_name="opensim-ossl-example-scripts" + repo_url="https://github.com/ManfredAabye/opensim-ossl-example-scripts.git" + + if [[ "$user_choice" == "new" ]]; then + if [[ -d "$repo_name" ]]; then + log "${COLOR_ACTION}Vorhandene Version wird gelöscht...${COLOR_RESET}" + rm -rf "$repo_name" + log "${SYM_OK} ${COLOR_ACTION}Alte Version wurde erfolgreich entfernt.${COLOR_RESET}" + fi + + log "${COLOR_ACTION}Beispiel-Skripte werden vom GitHub heruntergeladen...${COLOR_RESET}" + if ! git clone "$repo_url" "$repo_name"; then + log "${SYM_BAD} ${COLOR_ERROR}Fehler beim Klonen!${COLOR_RESET}" + return 1 + fi + + if ! check_repo_integrity "$repo_name"; then + log "${SYM_BAD} ${COLOR_ERROR}Repository beschädigt!${COLOR_RESET}" + return 1 + fi + + log "${SYM_OK} ${COLOR_ACTION}Repository wurde erfolgreich heruntergeladen.${COLOR_RESET}" + + elif [[ "$user_choice" == "upgrade" ]]; then + if [[ -d "$repo_name/.git" ]]; then + log "${SYM_OK} ${COLOR_ACTION}Repository gefunden. Aktualisiere mit 'git pull'...${COLOR_RESET}" + cd "$repo_name" || { log "${SYM_BAD} ${COLOR_ERROR}Fehler beim Wechsel ins Verzeichnis!${COLOR_RESET}"; return 1; } + + branch_name=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@^refs/remotes/origin/@@') + [ -z "$branch_name" ] && branch_name="main" + + old_head=$(git rev-parse HEAD) + + if ! git pull origin "$branch_name"; then + log "${SYM_BAD} ${COLOR_ERROR}Fehler beim Pull!${COLOR_RESET}" + cd .. + return 1 + fi + + if [ "$old_head" != "$(git rev-parse HEAD)" ]; then + log "${COLOR_OK}${SYM_OKNN} ${COLOR_ACTION}Neue Updates wurden installiert.${COLOR_RESET}" + else + log "${COLOR_OK}${SYM_OKNN} ${COLOR_ACTION}Repository ist bereits aktuell.${COLOR_RESET}" + fi + + if ! check_repo_integrity; then + log "${SYM_BAD} ${COLOR_ERROR}Repository beschädigt nach Pull!${COLOR_RESET}" + cd .. + return 1 + fi + + cd .. + else + log "${COLOR_WARNING}${SYM_WARNING} ${COLOR_ACTION}Verzeichnis nicht gefunden oder kein Git-Repo. Klone Repository neu...${COLOR_RESET}" + if ! git clone "$repo_url" "$repo_name"; then + log "${SYM_BAD} ${COLOR_ERROR}Fehler beim Klonen!${COLOR_RESET}" + return 1 + fi + log "${COLOR_OK}${SYM_OKNN} ${COLOR_ACTION}Repository wurde erfolgreich heruntergeladen.${COLOR_RESET}" + fi + else + log "${SYM_BAD} ${COLOR_ERROR}Abbruch: Keine Aktion durchgeführt.${COLOR_RESET}" + return 1 + fi + + # Verbesserte Kopierfunktion + safe_copy() { + local src=$1 + local dest=$2 + + if [ ! -e "$src" ]; then + log "${SYM_BAD} ${COLOR_ERROR}Quelle '$src' existiert nicht!${COLOR_RESET}" + return 1 + fi + + mkdir -p "$(dirname "$dest")" + if ! cp -r "$src" "$dest"; then + log "${SYM_BAD} ${COLOR_ERROR}Fehler beim Kopieren von '$src'!${COLOR_RESET}" + return 1 + fi + + log "${SYM_OK} ${COLOR_ACTION}Erfolgreich kopiert: $src → $dest${COLOR_RESET}" + return 0 + } + + # Zielverzeichnisse erstellen mit Fehlerbehandlung + mkdir -p opensim/bin/assets/ || { log "${SYM_BAD} ${COLOR_ERROR}Konnte Verzeichnis nicht erstellen!${COLOR_RESET}"; return 1; } + mkdir -p opensim/bin/inventory/ || { log "${SYM_BAD} ${COLOR_ERROR}Konnte Verzeichnis nicht erstellen!${COLOR_RESET}"; return 1; } + + # Kopiervorgänge mit Fehlerbehandlung + safe_copy "$repo_name/ScriptsAssetSet" "opensim/bin/assets/" || return 1 + safe_copy "$repo_name/inventory/ScriptsLibrary" "opensim/bin/inventory/" || return 1 + + blankline + return 0 +} + +####################################### + +#* Das ist erst halb fertig. +function ruthrothgit() { + # Schritt 1 das bereitstellen der Pakete zur weiteren bearbeitung. + log "${COLOR_HEADING}👥 Ruth & Roth Avatar-Assets Vorbereitung${COLOR_RESET}" + + base_dir="ruthroth" + mkdir -p "$base_dir" + + declare -A repos=( + ["Ruth2"]="https://github.com/ManfredAabye/Ruth2.git" + ["Roth2"]="https://github.com/ManfredAabye/Roth2.git" + ) + + for avatar in "${!repos[@]}"; do + repo_url="${repos[$avatar]}" + target_dir="$avatar" + + if [[ ! -d "$target_dir" ]]; then + log " ${COLOR_ACTION}➜ Klone ${COLOR_SERVER}$avatar${COLOR_ACTION} von GitHub...${COLOR_RESET}" + git clone "$repo_url" "$target_dir" && log " ${COLOR_OK}${SYM_OKNN} ${COLOR_SERVER}$avatar${COLOR_RESET} ${COLOR_ACTION}wurde heruntergeladen.${COLOR_RESET}" + else + log " ${COLOR_OK}${SYM_OKNN} ${COLOR_SERVER}$avatar${COLOR_RESET} ${COLOR_ACTION}ist bereits vorhanden, überspringe Download.${COLOR_RESET}" + fi + + # Kopiere nur die relevanten IAR-Dateien direkt nach ruthroth + log " ${COLOR_ACTION}➜ Kopiere benötigte IAR-Dateien nach ${COLOR_DIR}$base_dir${COLOR_ACTION}...${COLOR_RESET}" + cp "$target_dir/Artifacts/IAR/"*.iar "$base_dir/" 2>/dev/null && log " ${SYM_OK} IAR-Dateien von ${COLOR_SERVER}$avatar${COLOR_RESET} kopiert.${COLOR_RESET}" + done + + # Kopiere das updatelibrary.py-Skript ins Hauptverzeichnis ruthroth + log " ${COLOR_ACTION}➜ Kopiere updatelibrary.py nach ${COLOR_DIR}$base_dir${COLOR_ACTION}...${COLOR_RESET}" + cp "updatelibrary.py" "$base_dir/" && log " ${SYM_OK} updatelibrary.py wurde kopiert.${COLOR_RESET}" + + echo "Verzeichniswechsel in $base_dir" + + # Wechsel ins ruthroth-Verzeichnis + cd "$base_dir" || { log "${SYM_BAD} Fehler beim Wechsel ins Verzeichnis ${COLOR_DIR}$base_dir${COLOR_RESET}"; return 1; } + + # Entpacke die IAR-Dateien direkt in ruthroth + log " ${COLOR_ACTION}➜ Entpacke IAR-Pakete in ${COLOR_DIR}$base_dir${COLOR_ACTION}...${COLOR_RESET}" + for iar_file in *.iar; do + if [[ -f "$iar_file" ]]; then + target_dir="${iar_file%.iar}" # Erstellt Verzeichnisname basierend auf Dateiname ohne .iar + mkdir -p "$target_dir" + tar -xzf "$iar_file" -C "$target_dir/" && log " ✓ ${iar_file} entpackt nach ${target_dir}" + else + log " ${SYM_WARNING} IAR-Datei ${iar_file} nicht gefunden. Überspringe..." + fi + done + + log "${COLOR_OK}${SYM_OKNN} ${COLOR_ACTION}Grundlage für die OpenSimulator-Pakete wurde erfolgreich erstellt!${COLOR_RESET}" + + # Schritt 2 die verwendung von updatelibrary.py. + cd ruthroth + python3 updatelibrary.py -n "Roth2-v1" -s "Roth2-v1" -a Roth2-v1 -i Roth2-v1 + python3 updatelibrary.py -n "Roth2-v2" -s "Roth2-v2" -a Roth2-v2 -i Roth2-v2 + python3 updatelibrary.py -n "Ruth2-v3" -s "Ruth2-v3" -a Ruth2-v3 -i Ruth2-v3 + python3 updatelibrary.py -n "Ruth2-v4" -s "Ruth2-v4" -a Ruth2-v4 -i Ruth2-v4 + cd .. + # Schritt 3 das kopieren der Daten. Das einfügen der Daten in den Dateien ähnlich wie bei PBR. +} + +# Es werden die Konfiguartionen jetzt auch geändert. +function pbrtexturesgit() { + log "${COLOR_HEADING}🎨 PBR Texturen Installation${COLOR_RESET}" + + textures_zip_url="https://github.com/ManfredAabye/OpenSim_PBR_Textures/releases/download/PBR/OpenSim_PBR_Textures.zip" + zip_file="OpenSim_PBR_Textures.zip" + unpacked_dir="OpenSim_PBR_Textures" + target_dir="opensim" + + # ZIP herunterladen, wenn nicht vorhanden + if [[ ! -f "$zip_file" ]]; then + log "${COLOR_ACTION}Lade OpenSim PBR Texturen herunter...${COLOR_RESET}" + if ! wget -q --show-progress -O "$zip_file" "$textures_zip_url"; then + log "${SYM_BAD} ${COLOR_ERROR}Fehler beim Herunterladen der Texturen!${COLOR_RESET}" + return 1 + fi + log "${SYM_OK} ${COLOR_ACTION}Download abgeschlossen: ${COLOR_DIR}$zip_file${COLOR_RESET}" + else + log "${SYM_OK} ${COLOR_ACTION}ZIP-Datei bereits vorhanden: ${COLOR_DIR}$zip_file${COLOR_RESET}" + fi + + # Entpacken, wenn Verzeichnis noch nicht existiert + if [[ ! -d "$unpacked_dir" ]]; then + log "${COLOR_ACTION}Entpacke Texturen nach ${COLOR_DIR}$unpacked_dir${COLOR_ACTION}...${COLOR_RESET}" + unzip -q "$zip_file" -d . + log "${SYM_OK} ${COLOR_ACTION}Entpackt.${COLOR_RESET}" + else + log "${SYM_OK} ${COLOR_ACTION}Verzeichnis ${COLOR_DIR}$unpacked_dir${COLOR_ACTION} existiert bereits – überspringe Entpacken.${COLOR_RESET}" + fi + + # Kopieren nach opensim/bin + if [[ -d "$unpacked_dir/bin" ]]; then + log "${COLOR_ACTION}Kopiere Texturen nach ${COLOR_DIR}$target_dir${COLOR_ACTION}...${COLOR_RESET}" + cp -r "$unpacked_dir/bin" "$target_dir" + log "${SYM_OK} ${COLOR_ACTION}Texturen erfolgreich installiert in ${COLOR_DIR}$target_dir${COLOR_RESET}" + else + log "${SYM_BAD} ${COLOR_ERROR}Verzeichnis ${COLOR_DIR}$unpacked_dir/bin${COLOR_ERROR} nicht gefunden!${COLOR_RESET}" + return 1 + fi + + local asset_sets_file="opensim/bin/assets/AssetSets.xml" + local libraries_file="opensim/bin/inventory/Libraries.xml" + + # Funktion zur Überprüfung und Hinzufügung eines Eintrags + add_xml_section() { + local file="$1" + local section_name="$2" + local content="$3" + + # Überprüfen ob der Abschnitt bereits existiert + if ! grep -q "
" "$file"; then + # Abschnitt vor einfügen + sed -i "s||
\n$content\n
\n|" "$file" + echo "Added $section_name to $file" + else + echo "$section_name already exists in $file - no changes made" + fi + } + + # AssetSets.xml bearbeiten + add_xml_section "$asset_sets_file" "PBR Textures AssetSet" \ + " " + + # Libraries.xml bearbeiten + add_xml_section "$libraries_file" "PBRTextures Library" \ + " \n " + + blankline + return 0 +} + +function osWebinterfacegit() { + log "${COLOR_HEADING}🌐 Webinterface Installation${COLOR_RESET}" + + webverzeichnis="oswebinterface" + + # Abfrage zur Installation mit Default "Nein" + log "${COLOR_LABEL}Möchten Sie das OpenSim Webinterface installieren? ()j/N) ${COLOR_RESET} [n]" + read -r install_webinterface + if [[ ! "$install_webinterface" =~ ^[jJ][aA]?$ ]]; then + log "${COLOR_INFO}Installation abgebrochen (Default: Nein)${COLOR_RESET}" + blankline + return 0 + fi + + setup_webserver + + # 1. PHP-Check mit Distro-Erkennung + if ! command -v php &>/dev/null; then + log "${SYM_WARNING} ${COLOR_WARNING}PHP ist nicht installiert!${COLOR_RESET}" + + # Distro-Erkennung + # shellcheck source=/dev/null + source /etc/os-release + case $ID in + debian|ubuntu|linuxmint|pop|mx|zorin|elementary) + pkg_cmd="sudo apt-get install -y php php-mysql php-gd php-curl" + ;; + arch|manjaro) + pkg_cmd="sudo pacman -S --noconfirm php php-gd php-curl" + ;; + fedora|centos|rhel) + pkg_cmd="sudo dnf install -y php php-mysqlnd php-gd php-common" + ;; + opensuse|sles) + pkg_cmd="sudo zypper install -y php7 php7-mysql php7-gd php7-curl" + ;; + *) + log "${SYM_BAD} ${COLOR_ERROR}Unterstützte Distribution nicht erkannt!${COLOR_RESET}" + echo "Manuell installieren: php php-mysql php-gd php-curl" + return 1 + ;; + esac + + log "PHP auf ${PRETTY_NAME} installieren? (j/n) " + read -r install_php + if [[ "$install_php" =~ ^[jJ] ]]; then + log "${COLOR_ACTION}Installiere PHP... (${pkg_cmd})${COLOR_RESET}" + if ! eval "$pkg_cmd"; then + log "${SYM_BAD} ${COLOR_ERROR}PHP-Installation fehlgeschlagen!${COLOR_RESET}" + return 1 + fi + log "${COLOR_OK}${SYM_OKNN} PHP erfolgreich installiert!${COLOR_RESET}" + else + log "${SYM_BAD} ${COLOR_ERROR}Abbruch: PHP ist erforderlich.${COLOR_RESET}" + return 1 + fi + else + log "${COLOR_OK}${SYM_OKNN} PHP ist installiert ($(php -v | head -n 1))${COLOR_RESET}" + fi + + # 2. Webverzeichnis-Check mit automatischer User/Group-Erkennung + local webroot="/var/www/html" + [[ -d "/var/www" && ! -d "$webroot" ]] && webroot="/var/www" # Fallback für einige Distros + + if [[ ! -d "$webroot" ]]; then + log "${SYM_BAD} ${COLOR_ERROR}Webroot nicht gefunden in:${COLOR_RESET}" + echo "/var/www/html oder /var/www" + echo "Tipp: Apache/Nginx zuerst installieren" + return 1 + fi + + # Bestimme den richtigen Benutzer und Gruppe basierend auf existierenden Dateien + local target_user target_group + # Suche nach einer existierenden Datei oder Verzeichnis im Webroot + sample_file=$(find "$webroot" -maxdepth 1 -type f -print -quit 2>/dev/null || true) + sample_dir=$(find "$webroot" -maxdepth 1 -type d ! -path "$webroot" -print -quit 2>/dev/null || true) + + if [ -n "$sample_file" ]; then + target_user=$(stat -c '%U' "$sample_file") + target_group=$(stat -c '%G' "$sample_file") + elif [ -n "$sample_dir" ]; then + target_user=$(stat -c '%U' "$sample_dir") + target_group=$(stat -c '%G' "$sample_dir") + else + # Fallback: Standard-Webserver-User je nach Distribution + # shellcheck source=/dev/null + source /etc/os-release + case $ID in + debian|ubuntu|linuxmint|pop|mx|zorin|elementary) + target_user="www-data" + target_group="www-data" + ;; + arch|manjaro) + target_user="http" + target_group="http" + ;; + fedora|centos|rhel) + target_user="apache" + target_group="apache" + ;; + opensuse|sles) + target_user="wwwrun" + target_group="www" + ;; + *) + target_user="www-data" + target_group="www-data" + ;; + esac + fi + + # 3. Git-Operationen + if [[ ! -d "oswebinterface" ]]; then + git clone https://github.com/ManfredAabye/oswebinterface.git oswebinterface || return 1 + else + cd oswebinterface || return 1 + git pull origin "$(git symbolic-ref refs/remotes/origin/HEAD | sed 's@^refs/remotes/origin/@@')" || return 1 + cd .. + fi + + # 4. Deployment mit korrekten Berechtigungen + log "${COLOR_ACTION}Kopiere Webinterface nach ${webroot}/${webverzeichnis}...${COLOR_RESET}" + if ! sudo cp -r "oswebinterface" "$webroot/$webverzeichnis"; then + log "${SYM_BAD} ${COLOR_ERROR}Kopieren fehlgeschlagen!${COLOR_RESET}" + return 1 + fi + + log "${COLOR_ACTION}Setze Besitzer auf ${target_user}:${target_group}...${COLOR_RESET}" + if ! sudo chown -R "$target_user:$target_group" "$webroot/$webverzeichnis"; then + log "${SYM_BAD} ${COLOR_ERROR}Besitzer ändern fehlgeschlagen!${COLOR_RESET}" + return 1 + fi + + log "${COLOR_ACTION}Setze Dateiberechtigungen...${COLOR_RESET}" + if ! sudo find "$webroot/$webverzeichnis" -type d -exec chmod 755 {} \; || \ + ! sudo find "$webroot/$webverzeichnis" -type f -exec chmod 644 {} \; ; then + log "${SYM_BAD} ${COLOR_ERROR}Berechtigungen setzen fehlgeschlagen!${COLOR_RESET}" + return 1 + fi + + log "${COLOR_OK}${SYM_OKNN} Erfolgreich installiert nach: ${COLOR_DIR}$webroot/$webverzeichnis${COLOR_RESET}" + log " Benutzer/Gruppe: ${COLOR_INFO}$target_user:$target_group${COLOR_RESET}" + blankline + return 0 +} + +function versionrevision() { + file="opensim/OpenSim/Framework/VersionInfo.cs" + + #xflavour="Unknown" + #xflavour="Dev" + #xflavour="RC1" + #xflavour="RC2" + #xflavour="RC3" + #xflavour="Release" + #xflavour="Post_Fixes" + xflavour="Extended" + + if [[ ! -f "$file" ]]; then + log "${SYM_BAD} ${COLOR_ERROR}Datei nicht gefunden: ${COLOR_DIR}$file${COLOR_RESET}" + return 1 + fi + + log "${SYM_OK} ${COLOR_ACTION}Bearbeite Datei: ${COLOR_DIR}$file${COLOR_RESET}" + + # Ändere Flavour.Dev + sed -i 's/public const Flavour VERSION_FLAVOUR = Flavour\.[^;]*;/public const Flavour VERSION_FLAVOUR = Flavour.'"$xflavour"';/' "$file" + + # Entferne "Nessie" aus dem Versions-String + sed -i 's/OpenSim {versionNumber} Nessie {flavour}/OpenSim {versionNumber} {flavour}/' "$file" + + log "${SYM_OK} ${COLOR_ACTION}Änderungen zu $xflavour wurden erfolgreich vorgenommen.${COLOR_RESET}" + blankline + return 0 +} + +function generate_name() { + # Arrays mit Namensbestandteilen (alle einzigartig) + + local firstnames=( + # Vornamen + "Mystic" "Arcane" "Eldritch" "Enigmatic" "Esoteric" "Occult" "Cryptic" "Celestial" "Astral" "Ethereal" + "Luminous" "Radiant" "Prismatic" "Iridescent" "Phantasmal" "Spectral" "Otherworldly" "Transcendent" "Timeless" "Unearthly" + "Enchanted" "Charmed" "Bewitched" "Mythic" "Legendary" + "Verdant" "Sylvan" "Petrified" "Thundering" "Whispering" "Howling" "Roaring" "Rumbling" "Crystalline" "Obsidian" + "Amber" "Jade" "Sapphire" "Emerald" "Ruby" "Topaz" "Opaline" "Pearlescent" "Gilded" "Argent" + "Solar" "Lunar" "Stellar" "Nebular" "Galactic" + "Aether" "Runeweaver" "Dragonheart" "Moonshadow" "Spellbinder" "Witchbloom" "Grimoire" "Shadowalker" "Starborn" "Voidwhisper" + "Dreamwalker" "Fatesender" "Glyphborn" "Hollowveil" "Ivyroot" "Jadewhisper" "Kindlespark" "Lorekeeper" "Netherflame" "Oathbound" + "Pixieglen" "Quicksilver" "Ravenoak" "Silvershade" "Twilightthorn" + "Aerendyl" "Beltharion" "Celeborn" "Diorath" "Elrondar" "Faelivrin" "Galadhor" "Haldirion" "Ithilwen" "Jarathmel" + "Kelebrind" "Lothmiriel" "Melianthar" "Nimlothiel" "Oropherion" "Pelenor" "Quelamar" "Rivendell" "Silmarion" "Thranduvar" + "Ulmoen" "Vanyarion" "Windathar" "Xylanthir" "Yavanniel" + "Andromeda" "Beowulf" "Cybernova" "Deusex" "Eclipse" "Frostbyte" "Gigawatt" "Hyperion" "Io" "Jupiter" + "Krypton" "Lunarc" "Megatron" "Nebula" "Orion" "Pulsar" "Quantum" "Robodoc" "Starbuck" "Titan" + "Uranus" "Vortex" "Warpcore" "Xenon" "Zenith" + ) + + local lastnames=( + # Nachnamen + "Forest" "Grove" "Copse" "Thicket" "Wildwood" "Jungle" "Rainforest" "Mangrove" "Taiga" "Tundra" + "Mountain" "Peak" "Summit" "Cliff" "Crag" "Bluff" "Mesa" "Plateau" "Canyon" "Ravine" + "Valley" "Dale" "Glen" "Hollow" "Basin" + "River" "Stream" "Brook" "Creek" "Fjord" "Lagoon" "Estuary" "Delta" "Bayou" "Wetland" + "Oasis" "Geyser" "Spring" "Well" "Aquifer" + "Observatory" "Planetarium" "Orrery" "Reflectory" "Conservatory" "Atrium" "Rotunda" "Gazebo" "Pavilion" "Terrace" + "Ashenwood" "Bramblethorn" "Cliffhaven" "Dewspark" "Emberglade" "Frostvein" "Goldleaf" "Hailstone" "Ironbark" "Jasperridge" + "Kelpstrand" "Lavafall" "Mossgrip" "Nettlebrook" "Oakenshield" "Pinecrest" "Quartzpeak" "Rimefrost" "Stormbreak" "Tidepool" + "Umbravale" "Vinecreep" "Willowshade" "Xylem" "Yewroot" + "Aranel" "Beleriand" "Cirdan" "Doriath" "Eregion" "Fangorn" "Gondolin" "Hithlum" "Imladris" "Jarngard" + "Kementari" "Lorien" "Mirkwood" "Nargothrond" "Osgiliath" "Pelargir" "Quendi" "Rhosgobel" "Sindar" "Tirion" + "Undomiel" "Valinor" "Westfold" "Xandoria" "Yavanna" + "Axiom" "Borg" "Coruscant" "Dyson" "Expanse" "Federation" "Genesis" "Horizon" "Infinity" "Jovian" + "Kessel" "Lazarus" "Matrix" "Nostromo" "Outland" "Prometheus" "Quadrant" "Rapture" "Serenity" "Trifid" + "Umbra" "Voyager" "Weyland" "Xenomorph" "Zodiac" + ) + + gennamefirst="${firstnames[$RANDOM % ${#firstnames[@]}]}" # übergebe das an generate_all_name() + gennamesecond="${lastnames[$RANDOM % ${#lastnames[@]}]}" # übergebe das an generate_all_name() + +} +function generate_all_name() { + # Alle Namen und Bezeichnungen für die Installation erstellen das dient dazu das die Namen im gesamten Programm verwendet werden können. + # Benutzername Vorname Nachname + generate_name + genFirstname="${gennamefirst}" + generate_name + genLastname="${gennamesecond}" + echo "$gennamefirst" + echo "$gennamesecond" + + generate_name + # database_setup + genDatabaseUserName="${gennamefirst}${gennamesecond}$((RANDOM % 900 + 100))" + echo "$genDatabaseUserName" + + # generate_name + # # Neue Region + # genRegionName="${gennamefirst}${gennamesecond}$((RANDOM % 900 + 100))" + # echo "$genRegionName" + + generate_name + genGridName="${gennamesecond}Grid" + echo "$genGridName" + + # Test Ausgabe der Variablen + #echo "genFirstname='${genFirstname}'" + #echo "genLastname='${genLastname}'" + #echo "genDatabaseUserName='${genDatabaseUserName}'" + #echo "genRegionName='${genRegionName}'" + #echo "genGridName='${genGridName}'" + #echo "${names[$RANDOM % ${#names[@]}]}" +} + +function opensimbuild() { + log "${COLOR_HEADING}🏗️ OpenSimulator Build-Prozess${COLOR_RESET}" + + log "${COLOR_LABEL}Möchten Sie den OpenSimulator jetzt erstellen? (${COLOR_OK}[ja]${COLOR_LABEL}/nein)${COLOR_RESET}" + read -r user_choice + + user_choice=${user_choice:-ja} + + if [[ "$user_choice" == "ja" ]]; then + if [[ -d "opensim" ]]; then + cd opensim || { log "${SYM_BAD} ${COLOR_ERROR}Fehler: Verzeichnis 'opensim' nicht gefunden.${COLOR_RESET}"; return 1; } + log "${COLOR_ACTION}Starte Prebuild-Skript...${COLOR_RESET}" + bash runprebuild.sh + log "${COLOR_ACTION}Baue OpenSimulator...${COLOR_RESET}" + dotnet build --configuration Release OpenSim.sln + log "${SYM_OK} ${COLOR_ACTION}OpenSimulator wurde erfolgreich erstellt.${COLOR_RESET}" + else + log "${SYM_BAD} ${COLOR_ERROR}OpenSim Build Fehler: Das Verzeichnis 'opensim' existiert nicht.${COLOR_RESET}" + return 1 + fi + else + log "${SYM_BAD} ${COLOR_ERROR}Abbruch: OpenSimulator wird nicht erstellt.${COLOR_RESET}" + fi + # Wer in ein Verzeichnis wechselt sollte auch wieder zur vorherigen Verzeichnis wechseln! + cd .. + blankline +} + +#?────────────────────────────────────────────────────────────────────────────────────────── +#* Erstellen eines OpenSimulators Grids +#?────────────────────────────────────────────────────────────────────────────────────────── + +function removeconfigfiles() { + local ini_file="${SCRIPT_DIR}/UserInfo.ini" + # Erstelle Backup wenn Datei existiert + if [[ -f "$ini_file" ]]; then + #echo "$(date +'%d.%m.%Y')_$(date +'%H:%M:%S')" + backup_file="${ini_file}.$(date +'%d.%m.%Y')_$(date +'%H:%M:%S').bak" + sudo mv -f "$ini_file" "$backup_file" + fi +} + +function createdirectory() { + log "${COLOR_HEADING}📂 Verzeichniserstellung${COLOR_RESET}" + + log "${COLOR_LABEL}Möchten Sie einen Gridserver oder einen Regionsserver erstellen? (${COLOR_OK}[grid]${COLOR_LABEL}/region)${COLOR_RESET}" + read -r server_type + + # Standardmäßig Gridserver wählen, falls keine Eingabe erfolgt + server_type=${server_type:-grid} + + if [[ "$server_type" == "grid" ]]; then + log "${COLOR_ACTION}Erstelle robust Verzeichnis...${COLOR_RESET}" + mkdir -p robust/bin + log "${SYM_OK} ${COLOR_ACTION}Robust Verzeichnis wurde erstellt.${COLOR_RESET}" + + # Nach der Erstellung des Gridservers auch die Regionsserver erstellen lassen + log "${COLOR_LABEL}Wie viele Regionsserver benötigen Sie? ${COLOR_OK}[1]${COLOR_RESET}" + read -r num_regions + # Standardmäßig 1 Region wählen, falls keine Eingabe erfolgt + num_regions=${num_regions:-1} + log "${SYM_OK} ${COLOR_ACTION}Sie haben $num_regions Regionsserver gew‰hlt.${COLOR_RESET}" + + elif [[ "$server_type" == "region" ]]; then + log "${COLOR_LABEL}Wie viele Regionsserver benötigen Sie? ${COLOR_OK}[1]${COLOR_RESET}" + read -r num_regions + log "${SYM_OK} ${COLOR_ACTION}Sie haben $num_regions Regionsserver gew‰hlt.${COLOR_RESET}" + else + log "${SYM_BAD} ${COLOR_ERROR}Ungültige Eingabe. Bitte geben Sie 'grid' oder 'region' ein.${COLOR_RESET}" + return 1 + fi + + # Überprüfen, ob die Anzahl der Regionsserver bis 999 geht + if [[ "$num_regions" =~ ^[0-9]+$ && "$num_regions" -le 999 ]]; then + for ((i=1; i<=num_regions; i++)); do + dir_name="sim$i" + if [[ ! -d "$dir_name" ]]; then + mkdir -p "$dir_name/bin" + log "${SYM_OK} ${COLOR_DIR}$dir_name${COLOR_RESET} ${COLOR_ACTION}wurde erstellt.${COLOR_RESET}" + sleep 1 + else + log "${SYM_OK} ${COLOR_DIR}$dir_name${COLOR_RESET} ${COLOR_WARNING}existiert bereits und wird übersprungen.${COLOR_RESET}" + fi + done + else + log "${SYM_BAD} ${COLOR_ERROR}Ungültige Anzahl an Regionsserver. Bitte geben Sie eine gültige Zahl zwischen 1 und 999 ein.${COLOR_RESET}" + fi + blankline +} + +function opensimcopy() { + log "${COLOR_HEADING}${SYM_PACKAGE} OpenSim Dateikopie${COLOR_RESET}" + + # Prüfen, ob das Verzeichnis "opensim" existiert + if [[ ! -d "opensim" ]]; then + log "${SYM_BAD} ${COLOR_ERROR}OpenSim Copy Fehler: Das Verzeichnis 'opensim' existiert nicht.${COLOR_RESET}" + return 1 + fi + + # Prüfen, ob das Unterverzeichnis "opensim/bin" existiert + if [[ ! -d "opensim/bin" ]]; then + log "${SYM_BAD} ${COLOR_ERROR}OpenSim Copy Fehler: Das Verzeichnis 'opensim/bin' existiert nicht.${COLOR_RESET}" + return 1 + fi + + # Prüfen, ob das Verzeichnis "robust" existiert und Dateien kopieren + if [[ -d "robust/bin" ]]; then + cp -r opensim/bin/* robust/bin + log "${SYM_OK} ${COLOR_ACTION}Dateien aus ${COLOR_DIR}opensim/bin${COLOR_RESET} ${COLOR_ACTION}wurden nach ${COLOR_DIR}robust/bin${COLOR_RESET} ${COLOR_ACTION}kopiert.${COLOR_RESET}" + else + log "${COLOR_WARNING}${SYM_WARNING} ${COLOR_ACTION}Hinweis: 'robust' Verzeichnis nicht gefunden, keine Kopie durchgeführt.${COLOR_RESET}" + fi + + # Alle simX-Verzeichnisse suchen und Dateien kopieren + for sim_dir in sim*; do + if [[ -d "$sim_dir/bin" ]]; then + cp -r opensim/bin/* "$sim_dir/bin/" + log "${SYM_OK} ${COLOR_ACTION}Dateien aus ${COLOR_DIR}opensim/bin${COLOR_RESET} ${COLOR_ACTION}wurden nach ${COLOR_DIR}$sim_dir/bin${COLOR_RESET} ${COLOR_ACTION}kopiert.${COLOR_RESET}" + fi + done + + blankline +} + +function database_setup() { + # 06.05.2025 + log "${COLOR_SECTION}=== MariaDB/MySQL Datenbank-Setup ===${COLOR_RESET}" + + # 1. Distribution Detection + detect_distro() { + if [[ -f /etc/os-release ]]; then + awk -F= '/^ID=/ {print $2}' /etc/os-release | tr -d '"' + elif [[ -f /etc/debian_version ]]; then + echo "debian" + elif [[ -f /etc/centos-release ]]; then + echo "centos" + else + echo "unknown" + fi + } + + current_distro=$(detect_distro) + #supported_distros=("debian" "ubuntu" "linuxmint" "pop" "mx" "kali" "zorin" "elementary" "raspbian" "centos" "fedora") + supported_distros=("debian" "ubuntu" "linuxmint" "pop" "mx" "kali" "zorin" "elementary" "raspbian" "centos" "fedora" "arch" "manjaro") + + # 2. Support-Check + if ! printf '%s\n' "${supported_distros[@]}" | grep -q "^${current_distro}$"; then + log "${SYM_BAD} ${COLOR_BAD}Nicht unterstützte Distribution: '${current_distro}'${COLOR_RESET}" + log "${SYM_INFO} Unterstützt: ${supported_distros[*]}" + return 1 + fi + + # 3. Installation Check (angepasste Version) + if ! command -v mariadb &> /dev/null && ! command -v mysql &> /dev/null; then + log "${SYM_WAIT} ${COLOR_WARNING}MariaDB/MySQL ist nicht installiert${COLOR_RESET}" + echo -ne "${COLOR_ACTION}MariaDB installieren? (j/n) [j] ${COLOR_RESET}" + read -r install_choice + install_choice=${install_choice:-j} + + if [[ "$install_choice" =~ ^[jJ] ]]; then + case $current_distro in + debian|ubuntu|*mint|pop|zorin|elementary|kali|mx|raspbian) + log "${SYM_INFO} ${COLOR_ACTION}Installiere MariaDB (apt)...${COLOR_RESET}" + sudo apt-get update && sudo apt-get install -y mariadb-server + ;; + centos|fedora) + log "${SYM_INFO} ${COLOR_ACTION}Installiere MariaDB (yum)...${COLOR_RESET}" + sudo yum install -y mariadb-server + sudo systemctl start mariadb + sudo systemctl enable mariadb + ;; + arch|manjaro) + log "${SYM_INFO} ${COLOR_ACTION}Installiere MariaDB (pacman)...${COLOR_RESET}" + sudo pacman -Sy --noconfirm mariadb + sudo mariadb-install-db --user=mysql --basedir=/usr --datadir=/var/lib/mysql + sudo systemctl enable --now mariadb + ;; + *) + log "${SYM_BAD} ${COLOR_BAD}Automatische Installation nicht verfügbar${COLOR_RESET}" + return 1 + ;; + esac + + # Sicherheitskonfiguration für alle Distros + log "${SYM_INFO} ${COLOR_ACTION}Führe mysql_secure_installation aus...${COLOR_RESET}" + sudo mysql_secure_installation + else + log "${SYM_BAD} Installation abgebrochen" + return 0 + fi + else + log "${SYM_OK} ${COLOR_OK}MariaDB/MySQL ist bereits installiert${COLOR_RESET}" + fi + + # 4. Benutzeranmeldedaten + log "\n${COLOR_SECTION}=== Datenbank-Zugangsdaten ===${COLOR_RESET}" + echo -ne "${COLOR_ACTION}Zufallszugangsdaten verwenden? (j/n) [j] ${COLOR_RESET}" + read -r default_cred_choice + default_cred_choice=${default_cred_choice:-j} + + if [[ "$default_cred_choice" =~ ^[jJ] ]]; then + db_user="${genDatabaseUserName}" + db_pass=$(tr -dc 'A-Za-z0-9!@#$%^&*()' < /dev/urandom | head -c 16) + log "${SYM_INFO} ${COLOR_LABEL}Generiertes Passwort: ${COLOR_VALUE}${db_pass}${COLOR_RESET}" + else + echo -ne "${COLOR_ACTION}DB_Benutzername: ${COLOR_RESET}" + read -r db_user + echo -ne "${COLOR_ACTION}DB_Passwort: ${COLOR_RESET}" + read -rs db_pass + echo + fi + + # **Speicherung der Datenbank-Zugangsdaten in UserInfo.ini** + local ini_file="${SCRIPT_DIR}/UserInfo.ini" + + if [[ ! -f "$ini_file" ]]; then + log "${COLOR_INFO}Erstelle UserInfo.ini${COLOR_RESET}" + sudo touch "$ini_file" + fi + + log "\n[DatabaseData]" | sudo tee -a "$ini_file" >/dev/null + echo "DB_Benutzername = ${db_user}" | sudo tee -a "$ini_file" >/dev/null + echo "DB_Passwort = ${db_pass}" | sudo tee -a "$ini_file" >/dev/null + + + log "${SYM_LOG} ${COLOR_LABEL}Datenbank-Zugangsdaten gespeichert in: ${COLOR_FILE}${ini_file}${COLOR_RESET}" + + # 6. Datenbankeinrichtung + log "\n${COLOR_SECTION}=== Datenbank-Konfiguration ===${COLOR_RESET}" + + # RobustServer DB + if [[ -d "robust" ]]; then + if ! sudo mysql -e "USE robust" &> /dev/null; then + sudo mysql -e "CREATE DATABASE robust CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci" + log "${SYM_OK} ${COLOR_VALUE}robust${COLOR_RESET} Datenbank angelegt" + else + log "${SYM_INFO} ${COLOR_WARNING}robust-Datenbank existiert bereits${COLOR_RESET}" + fi + fi + + # simX Server DBs + for ((i=1; i<=1000; i++)); do + if [[ -d "sim${i}" ]]; then + db_name="sim${i}" + if ! sudo mysql -e "USE ${db_name}" &> /dev/null; then + sudo mysql -e "CREATE DATABASE ${db_name} CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci" + log "${SYM_OK} ${COLOR_VALUE}${db_name}${COLOR_RESET} Datenbank angelegt" + else + log "${SYM_INFO} ${COLOR_WARNING}${db_name}-Datenbank existiert bereits${COLOR_RESET}" + fi + fi + done + + # Benutzerverwaltung + if ! sudo mysql -e "SELECT user FROM mysql.user WHERE user='${db_user}' AND host='localhost'" | grep -q "${db_user}"; then + sudo mysql -e "CREATE USER '${db_user}'@'localhost' IDENTIFIED BY '${db_pass}'" + log "${SYM_OK} ${COLOR_VALUE}${db_user}${COLOR_RESET} Benutzer angelegt" + else + log "${SYM_INFO} ${COLOR_WARNING}Benutzer ${db_user} existiert bereits${COLOR_RESET}" + fi + + # Rechte vergeben + [[ -d "robust" ]] && sudo mysql -e "GRANT ALL PRIVILEGES ON robust.* TO '${db_user}'@'localhost'" + for ((i=1; i<=1000; i++)); do + [[ -d "sim${i}" ]] && sudo mysql -e "GRANT ALL PRIVILEGES ON sim${i}.* TO '${db_user}'@'localhost'" + done + sudo mysql -e "FLUSH PRIVILEGES" + + log "${COLOR_OK} Datenbank Setup abgeschlossen!${COLOR_RESET}" +} + + +function createmasteruser() { + # 02.05.2025 Master Avatar MasterAvatar + + # RobustServer starten + echo "RobustServer starten, erster start..." + if [[ -d "robust/bin" && -f "robust/bin/Robust.dll" ]]; then + log "${SYM_OK} ${COLOR_START}Starte ${COLOR_SERVER}RobustServer ${COLOR_RESET} ${COLOR_START}aus ${COLOR_DIR}robust/bin...${COLOR_RESET}" + cd robust/bin || exit 1 + # -inifile=Robust.HG.ini + + screen -fa -S robustserver -d -U -m dotnet Robust.dll + cd - >/dev/null 2>&1 || exit 1 + sleep $RobustServer_Start_wait + else + log "${SYM_BAD} ${COLOR_SERVER}RobustServer: ${COLOR_BAD}Robust.dll nicht gefunden.${COLOR_RESET} ${COLOR_START}Überspringe Start.${COLOR_RESET}" + fi + + genPasswort=$(tr -dc 'A-Za-z0-9!@#$%^&*()' < /dev/urandom | head -c 16) + genUserid=$(command -v uuidgen >/dev/null 2>&1 && uuidgen 2>/dev/null || cat /proc/sys/kernel/random/uuid 2>/dev/null || echo "$RANDOM-$RANDOM-$RANDOM-$RANDOM") + + VORNAME="${genFirstname}" + NACHNAME="${genLastname}" + local PASSWORT="${genPasswort}" + local EMAIL="${4:-${genFirstname}@${genLastname}.com}" + userid="${5:-$genUserid}" + + log "${COLOR_INFO}Master User Erstellung (Enter für generierte Werte)${COLOR_RESET}" + + echo -n "Vorname [${VORNAME}]: " + read -r input + VORNAME="${input:-$VORNAME}" + + echo -n "Nachname [${NACHNAME}]: " + read -r input + NACHNAME="${input:-$NACHNAME}" + + echo -n "Passwort [${PASSWORT}]: " + read -r input + PASSWORT="${input:-$PASSWORT}" + + echo -n "E-Mail [${EMAIL}]: " + read -r input + EMAIL="${input:-$EMAIL}" + + echo -n "UserID [${userid}]: " + read -r input + userid="${input:-$userid}" + + # Überprüfe ob Robust läuft + if ! screen -list | grep -q "robustserver"; then + log "${COLOR_BAD}CREATEUSER: Robust existiert nicht oder läuft nicht${COLOR_RESET}" + return 1 + fi + + # Benutzer in Robust erstellen + screen -S robustserver -p 0 -X eval "stuff 'create user'^M" + screen -S robustserver -p 0 -X eval "stuff '$VORNAME'^M" + screen -S robustserver -p 0 -X eval "stuff '$NACHNAME'^M" + screen -S robustserver -p 0 -X eval "stuff '$PASSWORT'^M" + screen -S robustserver -p 0 -X eval "stuff '$EMAIL'^M" + screen -S robustserver -p 0 -X eval "stuff '$userid'^M" + screen -S robustserver -p 0 -X eval "stuff 'Ruth'^M" + + log "${COLOR_OK}Masteruser $VORNAME $NACHNAME wurde erstellt${COLOR_RESET}" + log "${COLOR_INFO}UserID: $userid${COLOR_RESET}" + + # **Speicherung der Benutzerdaten in UserInfo.ini** NEU + local ini_file="${SCRIPT_DIR}/UserInfo.ini" + + if [[ ! -f "$ini_file" ]]; then + log "${COLOR_INFO}Erstelle UserInfo.ini${COLOR_RESET}" + sudo touch "$ini_file" + fi + + log "\n[UserData]" | sudo tee -a "$ini_file" >/dev/null + echo "Vorname = ${VORNAME}" | sudo tee -a "$ini_file" >/dev/null + echo "Nachname = ${NACHNAME}" | sudo tee -a "$ini_file" >/dev/null + echo "Passwort = ${PASSWORT}" | sudo tee -a "$ini_file" >/dev/null + echo "E-Mail = ${EMAIL}" | sudo tee -a "$ini_file" >/dev/null + echo "UserID = ${userid}" | sudo tee -a "$ini_file" >/dev/null + + + log "${SYM_LOG} ${COLOR_LABEL}Benutzerdaten hinzugefügt in: ${COLOR_FILE}${ini_file}${COLOR_RESET}" + + blankline +} + +function createmasterestate() { + # 02.05.2025 Master Estate + log "${COLOR_HEADING}🏡 Master Estate Erstellung${COLOR_RESET}" + + # sim1 starten + echo "Sim1 Welcome starten, erster start..." + if [[ -d "sim1/bin" && -f "sim1/bin/OpenSim.dll" ]]; then + log "${SYM_OK} ${COLOR_START}Starte ${COLOR_SERVER}sim1 ${COLOR_RESET} ${COLOR_START}aus ${COLOR_DIR}sim1/bin...${COLOR_RESET}" + cd sim1/bin || exit 1 + screen -fa -S sim1 -d -U -m dotnet OpenSim.dll + cd - >/dev/null 2>&1 || exit 1 + sleep 10 + else + log "${SYM_BAD} ${COLOR_SERVER}sim1: ${COLOR_BAD}OpenSim.dll nicht gefunden.${COLOR_RESET} ${COLOR_START}Überspringe Start.${COLOR_RESET}" + fi + + # 1. Überprüfe ob sim1 läuft + if ! screen -list | grep -q "sim1"; then + log "${SYM_BAD} ${COLOR_ERROR}CREATEESTATE: sim1 existiert nicht oder läuft nicht${COLOR_RESET}" + log "${COLOR_INFO}Tipp: Starten Sie die Simulator-Instanz zuerst${COLOR_RESET}" + return 1 + fi + + # 1. INI-Datei auslesen mit angepasstem Parsing + local ini_file="${SCRIPT_DIR}/UserInfo.ini" + if [[ ! -f "$ini_file" ]]; then + log "${COLOR_BAD}UserInfo.ini nicht gefunden unter: $ini_file${COLOR_RESET}" + return 1 + fi + + # Parsing-Funktion mit Trim und Leerzeichen im Pattern + ini_get() { + local section="$1" key="$2" + awk -F ' = ' ' + /^\[/ { current_section = substr($0, 2, length($0)-2) } + current_section == "'"$section"'" && $1 == "'"$key"'" { + sub(/^[ \t]+/, "", $2) # Trim leading whitespace + sub(/[ \t\r]+$/, "", $2) # Trim trailing whitespace + print $2 + exit + } + ' "$ini_file" + } + + # Werte auslesen + gridname=$(ini_get "ServerData" "GridName") + VORNAME=$(ini_get "UserData" "Vorname") + NACHNAME=$(ini_get "UserData" "Nachname") + + # Debug-Ausgabe + log "${COLOR_DEBUG}Gelesene Werte:" + log "GridName: '${gridname:-NICHT GEFUNDEN}'" + log "Vorname: '${VORNAME:-NICHT GEFUNDEN}'" + log "Nachname: '${NACHNAME:-NICHT GEFUNDEN}'${COLOR_RESET}" + + if [[ -z "$gridname" || -z "$VORNAME" || -z "$NACHNAME" ]]; then + log "${COLOR_BAD}Fehlende Daten in UserInfo.ini${COLOR_RESET}" + log "Bitte überprüfen Sie folgende Einträge:" + log "[ServerData]" + log "GridName = ..." + log "[UserData]" + log "Vorname = ..." + log "Nachname = ..." + return 1 + fi + + # 3. Logging vor der Ausführung + log "${COLOR_INFO}Erstelle Master Estate für:" + log "- Grid: ${COLOR_VALUE}$gridname" + log "- Avatar: ${COLOR_VALUE}$VORNAME $NACHNAME${COLOR_RESET}" + + # 4. Befehle an sim1 senden mit Fehlerprüfung + if ! screen -S sim1 -p 0 -X eval "stuff '$gridname Estate'^M"; then + log "${SYM_BAD} ${COLOR_ERROR}Fehler beim Senden des Estate-Befehls${COLOR_RESET}" + return 1 + fi + sleep 1 # Kurze Pause zwischen den Befehlen + + if ! screen -S sim1 -p 0 -X eval "stuff '$VORNAME'^M"; then + log "${SYM_BAD} ${COLOR_ERROR}Fehler beim Senden des Vornamens${COLOR_RESET}" + return 1 + fi + sleep 1 + + if ! screen -S sim1 -p 0 -X eval "stuff '$NACHNAME'^M"; then + log "${SYM_BAD} ${COLOR_ERROR}Fehler beim Senden des Nachnamens${COLOR_RESET}" + return 1 + fi + + # 5. Erfolgsmeldung + log "${SYM_OK} ${COLOR_OK}Master Estate erfolgreich erstellt${COLOR_RESET}" + log "${COLOR_INFO}Starte nun die Estate-Akzeptierung für alle Regionen...${COLOR_RESET}" + blankline + + # 6. Alle anderen Starten und Estate akzeptieren + if ! createmasterestateall; then + log "${SYM_BAD} ${COLOR_ERROR}Fehler bei createmasterestateall${COLOR_RESET}" + return 1 + fi + + return 0 +} + +function createmasterestateall() { + # 02.05.2025 Master Estate Bestätigung mit Simulator-Start + log "${COLOR_HEADING}🌍 Starte Simulatoren & bestätige Master Estate${COLOR_RESET}" + + # 1. INI-Datei auslesen mit angepasstem Parsing + local ini_file="${SCRIPT_DIR}/UserInfo.ini" + if [[ ! -f "$ini_file" ]]; then + log "${COLOR_BAD}UserInfo.ini nicht gefunden unter: $ini_file${COLOR_RESET}" + return 1 + fi + + # Parsing-Funktion mit Trim und Leerzeichen im Pattern + ini_get() { + local section="$1" key="$2" + awk -F ' = ' ' + /^\[/ { current_section = substr($0, 2, length($0)-2) } + current_section == "'"$section"'" && $1 == "'"$key"'" { + sub(/^[ \t]+/, "", $2) # Trim leading whitespace + sub(/[ \t\r]+$/, "", $2) # Trim trailing whitespace + print $2 + exit + } + ' "$ini_file" + } + + # Werte auslesen + gridname=$(ini_get "ServerData" "GridName") + VORNAME=$(ini_get "UserData" "Vorname") + NACHNAME=$(ini_get "UserData" "Nachname") + + # Debug-Ausgabe + log "${COLOR_DEBUG}Gelesene Werte:" + log "GridName: '${gridname:-NICHT GEFUNDEN}'" + log "Vorname: '${VORNAME:-NICHT GEFUNDEN}'" + log "Nachname: '${NACHNAME:-NICHT GEFUNDEN}'${COLOR_RESET}" + + if [[ -z "$gridname" || -z "$VORNAME" || -z "$NACHNAME" ]]; then + log "${COLOR_BAD}Fehlende Daten in UserInfo.ini${COLOR_RESET}" + log "Bitte überprüfen Sie folgende Einträge:" + log "[ServerData]" + log "GridName = ..." + log "[UserData]" + log "Vorname = ..." + log "Nachname = ..." + return 1 + fi + + # 2. Starte alle Simulatoren + log "${COLOR_ACTION}Starte Simulator-Instanzen...${COLOR_RESET}" + local started_sims=0 + + for ((i=2; i<=999; i++)); do + sim_dir="sim$i/bin" + if [[ -d "$sim_dir" && -f "$sim_dir/OpenSim.dll" ]]; then + echo -n -e "${SYM_INFO} ${COLOR_DIR}sim$i: ${COLOR_RESET}" + + if cd "$sim_dir" && screen -fa -S "sim$i" -d -U -m dotnet OpenSim.dll; then + log "${COLOR_OK}gestartet${COLOR_RESET}" + ((started_sims++)) + sleep "${Simulator_Start_wait:-5}" + else + log "${COLOR_ERROR}start fehlgeschlagen${COLOR_RESET}" + fi + cd - >/dev/null 2>&1 + fi + done + + # 3. Estate-Bestätigung für alle Regionen + if [[ $started_sims -gt 0 ]]; then + log "${COLOR_ACTION}Bestätige Estate für $started_sims Simulator(en)...${COLOR_RESET}" + + for ((i=2; i<=999; i++)); do + if screen -list | grep -q "sim$i"; then + regions_dir="${SCRIPT_DIR}/sim$i/bin/Regions" + if [[ -d "$regions_dir" ]]; then + # Zähle die Anzahl der Regionen-Konfigurationen + region_count=$(find "$regions_dir" -maxdepth 1 -name "*.ini" | wc -l) + log "${SYM_INFO} ${COLOR_DIR}sim$i: ${region_count} Region(en) gefunden${COLOR_RESET}" + + # Für jede Region die Bestätigung senden + for ((r=1; r<=region_count; r++)); do + echo -n -e " ${SYM_INFO} Region ${r}: ${COLOR_RESET}" + + if ! screen -S "sim$i" -p 0 -X eval "stuff 'yes'^M"; then + log "${SYM_BAD} ${COLOR_ERROR}Fehler beim Senden von yes${COLOR_RESET}" + return 1 + fi + sleep 1 + + if ! screen -S "sim$i" -p 0 -X eval "stuff '$gridname Estate'^M"; then + log "${SYM_BAD} ${COLOR_ERROR}Fehler beim Senden von $gridname Estate${COLOR_RESET}" + return 1 + fi + log "${COLOR_OK}bestätigt${COLOR_RESET}" + sleep 1 + done + else + log "${SYM_WARNING} ${COLOR_WARNING}sim$i: Kein Regions-Verzeichnis gefunden${COLOR_RESET}" + sleep 1 + fi + fi + done + else + log "${SYM_WARNING} ${COLOR_WARNING}Keine Simulatoren gestartet!${COLOR_RESET}" + return 1 + fi + + log "${SYM_OK} ${COLOR_OK}$started_sims Simulatoren gestartet & Estate für alle Regionen bestätigt${COLOR_RESET}" + sleep 1 + blankline + return 0 +} + +function firststart() { + # 02.05.2025 + # Sind wir im Skriptverzeichnis oder noch in opensim? + cd "$SCRIPT_DIR" || exit 1 + + # Master Avatar Registrieren. + createmasteruser + + # Estate erstellen + createmasterestate + + # Erststart stoppen + #opensimstop + #killall screen + + # Regulären Start nach installation und setup. + #opensimrestart + + blankline + +} + +# bash osmtool.sh setcrontab +function setcrontab() { + # Strict Mode: Fehler sofort erkennen + set -euo pipefail + + log "${COLOR_HEADING}⏰ Cron-Job-Einrichtung (Interaktiv)${COLOR_RESET}" + + # Sicherheitsabfrage: Nur als root/sudo ausführen + if [ "$(id -u)" -ne 0 ]; then + log "${SYM_BAD} ${COLOR_ERROR}FEHLER: Dieses Skript benötigt root-Rechte! (sudo verwenden)${COLOR_RESET}" >&2 + return 1 + fi + + # Prüfen, ob SCRIPT_DIR gesetzt und gültig ist + if [ -z "${SCRIPT_DIR:-}" ]; then + log "${SYM_BAD} ${COLOR_ERROR}FEHLER: 'SCRIPT_DIR' muss gesetzt sein!${COLOR_RESET}" >&2 + return 1 + fi + + if [ ! -d "$SCRIPT_DIR" ]; then + log "${SYM_BAD} ${COLOR_ERROR}FEHLER: Verzeichnis '${COLOR_DIR}$SCRIPT_DIR${COLOR_ERROR}' existiert nicht!${COLOR_RESET}" >&2 + return 1 + fi + + # Temporäre Datei für neue Cron-Jobs + local temp_cron + temp_cron=$(mktemp) || { + log "${SYM_BAD} ${COLOR_ERROR}FEHLER: Temp-Datei konnte nicht erstellt werden.${COLOR_RESET}" >&2 + return 1 + } + + ### Interaktive Abfragen ### + log "\n${COLOR_VALUE}=== Wartung am 1. des Monats ===${COLOR_RESET}" + log "Welche Aktionen sollen ausgeführt werden? (Mehrfachauswahl möglich)" + log " ${SYM_INFO} 1) Nichts (deaktivieren)" + log " ${SYM_INFO} 2) cacheclean (inkl. automatischem stop vorher)" + log " ${SYM_INFO} 3) mapclean (inkl. automatischem stop vorher)" + log " ${SYM_INFO} 4) logclean (inkl. automatischem stop vorher)" + log " ${SYM_INFO} 5) Vollständiger Neustart (stop → cacheclean → mapclean → logclean → reboot)" + log " ${SYM_INFO} 6) Nur Server neustarten (reboot)" + read -r -p "Auswahl (z.B. '2 3' oder '5'): " -a monthly_actions + + # Abfrage ob nach clean-Operationen ein reboot erfolgen soll + local needs_reboot=false + if [[ " ${monthly_actions[*]} " =~ [234] ]]; then + read -r -p "Soll nach den Clean-Operationen ein Neustart erfolgen? (j/n) " add_reboot + [[ "$add_reboot" =~ [jJ] ]] && needs_reboot=true + fi + + log "\n${COLOR_VALUE}=== Tägliche Wartung ===${COLOR_RESET}" + read -r -p "Soll ein täglicher Restart durchgeführt werden? (j/n) " daily_restart + if [[ "$daily_restart" =~ [jJ] ]]; then + read -r -p "Uhrzeit (Stunde, 0-23): " daily_hour + daily_hour=${daily_hour:-5} # Default: 5 Uhr + fi + + log "\n${COLOR_VALUE}=== Überwachung ===${COLOR_RESET}" + read -r -p "Soll die Überwachung aktiviert werden? (j/n) " enable_monitoring + if [[ "$enable_monitoring" =~ [jJ] ]]; then + read -r -p "Intervall in Minuten (Standard: 30): " monitor_interval + monitor_interval=${monitor_interval:-30} + fi + + ### Cron-Job-Generierung ### + { + echo "# === OpenSimGrid-Automatisierung ===" + echo "# Bedeutung: Minute Stunde Tag Monat Jahr Befehlskette" + echo "" + + # Monatliche Wartung (1. des Monats) + if [[ " ${monthly_actions[*]} " =~ "1" ]]; then + echo "# Deaktiviert: Monatliche Wartung" + else + echo "# Monatliche Wartung am 1. des Monats" + + # Berechnung der Minuten relativ zum täglichen Restart + local restart_hour=${daily_hour:-5} + local restart_minute=0 + + # Stop 30 Minuten vor täglichem Restart + if [[ " ${monthly_actions[*]} " =~ [2345] ]]; then + stop_time=$(calculate_relative_time "$restart_hour" $restart_minute -30) + echo "$stop_time 1 * * bash '$SCRIPT_DIR/osmtool.sh' stop" + fi + + # Cacheclean 25 Minuten vor täglichem Restart (Option 2 oder 5) + if [[ " ${monthly_actions[*]} " =~ [25] ]]; then + cacheclean_time=$(calculate_relative_time "$restart_hour" $restart_minute -25) + echo "$cacheclean_time 1 * * bash '$SCRIPT_DIR/osmtool.sh' cacheclean" + fi + + # Mapclean 20 Minuten vor täglichem Restart (Option 3 oder 5) + if [[ " ${monthly_actions[*]} " =~ [35] ]]; then + mapclean_time=$(calculate_relative_time "$restart_hour" $restart_minute -20) + echo "$mapclean_time 1 * * bash '$SCRIPT_DIR/osmtool.sh' mapclean" + fi + + # Logclean 15 Minuten vor täglichem Restart (Option 4 oder 5) + if [[ " ${monthly_actions[*]} " =~ [45] ]]; then + logclean_time=$(calculate_relative_time "$restart_hour" $restart_minute -15) + echo "$logclean_time 1 * * bash '$SCRIPT_DIR/osmtool.sh' logclean" + fi + + # Reboot 10 Minuten vor täglichem Restart (Option 5 oder 6 oder manuelle Bestätigung) + if [[ " ${monthly_actions[*]} " =~ [56] ]] || $needs_reboot; then + reboot_time=$(calculate_relative_time "$restart_hour" $restart_minute -10) + echo "$reboot_time 1 * * bash '$SCRIPT_DIR/osmtool.sh' reboot" + fi + fi + + # Tägliche Wartung + if [[ "$daily_restart" =~ [jJ] ]]; then + log "\n# Täglicher Restart" + echo "0 ${daily_hour} * * * bash '$SCRIPT_DIR/osmtool.sh' restart" + else + log "\n# Deaktiviert: Täglicher Restart" + fi + + # Überwachung + if [[ "$enable_monitoring" =~ [jJ] ]]; then + log "\n# Überwachung" + echo "*/${monitor_interval} * * * * bash '$SCRIPT_DIR/osmtool.sh' check_screens" + else + log "\n# Deaktiviert: Überwachung" + fi + } > "$temp_cron" + + # Cron-Jobs installieren + if crontab "$temp_cron"; then + rm -f "$temp_cron" + log "\n${SYM_OK} ${COLOR_OK}Cron-Jobs wurden aktualisiert:${COLOR_RESET}" + crontab -l | grep -v '^#' | sed '/^$/d' | while read -r line; do + log "${COLOR_DIR}$line${COLOR_RESET}" + done + return 0 + else + log "${SYM_BAD} ${COLOR_ERROR}FEHLER: Installation fehlgeschlagen. Prüfe $temp_cron manuell.${COLOR_RESET}" >&2 + return 1 + fi +} + +# Hilfsfunktion zur Berechnung relativer Zeiten +calculate_relative_time() { + local hour=$1 + local minute=$2 + local offset=$3 # in Minuten (kann negativ sein) + + # Gesamtminuten berechnen + local total_minutes=$((hour * 60 + minute + offset)) + + # Handle negative Zeiten (vor Mitternacht) + while (( total_minutes < 0 )); do + total_minutes=$((total_minutes + 1440)) # 24 Stunden in Minuten + done + + # Zurück in Stunden und Minuten umrechnen + local new_hour=$((total_minutes / 60 % 24)) + local new_minute=$((total_minutes % 60)) + + echo "$new_minute $new_hour" +} + +#?────────────────────────────────────────────────────────────────────────────────────────── +#* Backup und Upgrade des OpenSimulators Grids +#?────────────────────────────────────────────────────────────────────────────────────────── + +function opensimupgrade() { + log "\n${COLOR_WARNING}${SYM_WARNING} Der OpenSimulator muss zuerst im Verzeichnis 'opensim' vorliegen!${COLOR_RESET}" + log "${COLOR_LABEL}Möchten Sie den OpenSimulator aktualisieren? (${COLOR_BAD}[no]${COLOR_LABEL}/yes)${COLOR_RESET}" + read -r user_choice + user_choice=${user_choice:-no} + + # Prüfe, ob das Verzeichnis vorhanden ist + if [[ ! -d "opensim" ]]; then + log "${SYM_BAD} ${COLOR_ERROR}Fehler: Das Verzeichnis '${COLOR_DIR}opensim${COLOR_ERROR}' existiert nicht${COLOR_RESET}" + return 1 + fi + + # Prüfe, ob im Verzeichnis 'opensim/bin' die Dateien OpenSim.dll und Robust.dll vorhanden sind + if [[ ! -f "opensim/bin/OpenSim.dll" || ! -f "opensim/bin/Robust.dll" ]]; then + log "${SYM_BAD} ${COLOR_ERROR}Fehler: Benötigte Dateien (OpenSim.dll und/oder Robust.dll) fehlen im Verzeichnis '${COLOR_DIR}opensim/bin${COLOR_ERROR}'${COLOR_RESET}" + log "\n${COLOR_WARNING}❓ Haben Sie vergessen den OpenSimulator zuerst zu Kompilieren?${COLOR_RESET}" + return 1 + fi + + if [[ "$user_choice" == "yes" ]]; then + log "${COLOR_ACTION}OpenSimulator wird gestoppt...${COLOR_RESET}" + opensimstop + sleep $Simulator_Stop_wait + + log "${COLOR_ACTION}OpenSimulator wird kopiert...${COLOR_RESET}" + opensimcopy + + log "${COLOR_ACTION}OpenSimulator wird gestartet...${COLOR_RESET}" + opensimstart + + log "${COLOR_OK}✔ ${COLOR_ACTION}Upgrade abgeschlossen.${COLOR_RESET}" + else + log "${COLOR_WARNING}Upgrade vom Benutzer abgebrochen.${COLOR_RESET}" + fi + blankline +} + +function regionbackup() { + DATUM=$(date +%Y-%m-%d_%H-%M) + BACKUPDIR="$SCRIPT_DIR/backup" + mkdir -p "$BACKUPDIR" + + log "${SYM_TOOLS} ${COLOR_HEADING}Starte automatisches Backup aller Regionen...${COLOR_RESET}" + + for ((i=1; i<=999; i++)); do + SIMNAME="sim$i" + SIMBIN="$SCRIPT_DIR/$SIMNAME/bin" + REGIONSDIR="$SIMBIN/Regions" + TARGETDIR="$BACKUPDIR/sim$i" + + if [[ -d "$REGIONSDIR" ]]; then + mkdir -p "$TARGETDIR" + log "${SYM_SERVER} ${COLOR_START}Backup für ${COLOR_SERVER}$SIMNAME${COLOR_RESET} wird vorbereitet (${COLOR_DIR}$SIMBIN${COLOR_RESET})..." + + # ➕ OpenSim.ini sichern + if [[ -f "$SIMBIN/OpenSim.ini" ]]; then + cp "$SIMBIN/OpenSim.ini" "$TARGETDIR/${DATUM}-OpenSim.ini" + log "${SYM_CONFIG} ${COLOR_LABEL}OpenSim.ini gesichert:${COLOR_RESET} ${COLOR_FILE}${DATUM}-OpenSim.ini${COLOR_RESET}" + fi + + # ➕ GridCommon.ini sichern + if [[ -f "$SIMBIN/config-include/GridCommon.ini" ]]; then + cp "$SIMBIN/config-include/GridCommon.ini" "$TARGETDIR/${DATUM}-GridCommon.ini" + log "${SYM_CONFIG} ${COLOR_LABEL}GridCommon.ini gesichert:${COLOR_RESET} ${COLOR_FILE}${DATUM}-GridCommon.ini${COLOR_RESET}" + fi + + for ini_file in "$REGIONSDIR"/*.ini; do + [[ -f "$ini_file" ]] || continue + + region_names=$(grep -oP '^\[\K[^\]]+' "$ini_file") + + for REGIONNAME in $region_names; do + blankline + log "${SYM_FOLDER} ${COLOR_LABEL}Sichere Region:${COLOR_RESET} ${COLOR_VALUE}$REGIONNAME${COLOR_RESET}" + + screen -S "$SIMNAME" -p 0 -X eval "stuff 'change region ${REGIONNAME//\"/}'^M" + sleep 1 + + screen -S "$SIMNAME" -p 0 -X eval "stuff 'save oar ${TARGETDIR}/${DATUM}-${REGIONNAME}.oar'^M" + log "${SYM_FILE} ${COLOR_LABEL}OAR gespeichert:${COLOR_RESET} ${COLOR_FILE}${DATUM}-${REGIONNAME}.oar${COLOR_RESET}" + + screen -S "$SIMNAME" -p 0 -X eval "stuff 'save xml2 ${TARGETDIR}/${DATUM}-${REGIONNAME}.xml2'^M" + log "${SYM_FILE} ${COLOR_LABEL}XML2 gespeichert:${COLOR_RESET} ${COLOR_FILE}${DATUM}-${REGIONNAME}.xml2${COLOR_RESET}" + + screen -S "$SIMNAME" -p 0 -X eval "stuff 'terrain save ${TARGETDIR}/${DATUM}-${REGIONNAME}.png'^M" + screen -S "$SIMNAME" -p 0 -X eval "stuff 'terrain save ${TARGETDIR}/${DATUM}-${REGIONNAME}.raw'^M" + log "${SYM_FILE} ${COLOR_LABEL}Terrain (PNG & RAW) gespeichert:${COLOR_RESET} ${COLOR_FILE}${DATUM}-${REGIONNAME}.{png,raw}${COLOR_RESET}" + + cp "$ini_file" "$TARGETDIR/${DATUM}-$(basename "$ini_file")" + log "${SYM_CONFIG} ${COLOR_LABEL}Konfig gesichert:${COLOR_RESET} ${COLOR_FILE}${DATUM}-$(basename "$ini_file")${COLOR_RESET}" + + sleep 1 + done + done + fi + done + + log "${SYM_OK} ${COLOR_ACTION}Automatisches Regionen-Backup abgeschlossen.${COLOR_RESET}" + blankline +} + +# todo: Testen, ob alles funktioniert. +# Erstellt ein strukturiertes Backup der Robust-Datenbank sowie wichtiger Konfigurationsdateien. +# Aufruf: +function robustbackup() { + # Variablen der Funktion. + local DB_NAME="robust" + local BACKUP_YEARS=30 + SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + local ini_file="$SCRIPT_DIR/UserInfo.ini" + local BACKUP_DIR="$SCRIPT_DIR/backup/robust" + local ROBUST_INI_DIR="$SCRIPT_DIR/robust/bin" + TIMESTAMP=$(date +%F_%H-%M) + TIMESTAMP_GRENZE=$(date -d "-$BACKUP_YEARS years" +%s) + ARCHIVIEREN="nein" + + # Root-Privilegien erforderlich + if [[ "$EUID" -ne 0 ]]; then + echo "Dieses Skript muss als Root ausgeführt werden (z.B. mit sudo)." >&2 + echo "Ohne Root-Privilegien müssen Sie das Passwort der Datenbank bei jedem Mal eingeben." >&2 + echo -n "Möchten Sie trotzdem ohne Root-Privilegien fortfahren? (ja/N): " >&2 + read -r answer + if [[ "$answer" != "ja" ]]; then + echo "Abbruch." >&2 + exit 1 + fi + fi + + # Zugangsdaten auslesen, falls vorhanden + local DB_USER DB_PASS + if [[ -f "$ini_file" ]]; then + DB_USER=$(grep -oP '^DB_Benutzername\s*=\s*\K\S+' "$ini_file" | tr -d '\r\n') + DB_PASS=$(grep -oP '^DB_Passwort\s*=\s*\K\S+' "$ini_file" | tr -d '\r\n') + fi + + # Falls nicht vorhanden: Benutzer fragen + if [[ -z "$DB_USER" ]]; then + echo "Bitte DB-Benutzername eingeben:" + read -r DB_USER + fi + + if [[ -z "$DB_PASS" ]]; then + echo "Bitte DB-Passwort eingeben:" + read -r DB_PASS + fi + + declare -A ASSET_NAMES=( + [-1]="NoneUnknown" [-2]="LLmaterialIAR" [0]="Texture" [1]="Sound" [2]="CallingCard" [3]="Landmark" + [4]="Unknown4" [5]="Clothing" [6]="Object" [7]="Notecard" [8]="Folder" [9]="Unknown9" + [10]="LSLText" [11]="LSLBytecode" [12]="TextureTGA" [13]="Bodypart" [14]="Unknown14" + [15]="Unknown15" [16]="Unknown16" [17]="SoundWAV" [18]="ImageTGA" [19]="ImageJPEG" + [20]="Animation" [21]="Gesture" [22]="Simstate" [23]="Unknown23" [24]="Link" + [25]="LinkFolder" [26]="MarketplaceFolder" [49]="Mesh" [56]="Settings" [57]="Material" + ) + + mkdir -p "$BACKUP_DIR/tables" "$BACKUP_DIR/assettypen" "$BACKUP_DIR/configs" + + log "${COLOR_HEADING}${SYM_PACKAGE} Starte Robust-Datenbank-Backup mit Zeitraumfilter ($BACKUP_YEARS Jahre)${COLOR_RESET}" + + #* Reparatur + mysqlcheck -u"$DB_USER" -p"$DB_PASS" --auto-repair "$DB_NAME" + + #* Tabellen sichern (außer assets) mit Zählung + local tabellen + local total_nonasset_rows=0 + tabellen=$(mysql -u"$DB_USER" -p"$DB_PASS" -N -e "SHOW TABLES IN $DB_NAME;" | grep -v '^assets$') + for table in $tabellen; do + row_count=$(mysql -u"$DB_USER" -p"$DB_PASS" -N -e "SELECT COUNT(*) FROM $table;" "$DB_NAME") + ((total_nonasset_rows += row_count)) + log "${SYM_LOG} Tabelleneinträge: $table (${row_count} Einträge)${COLOR_RESET}" + mysqldump -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" "$table" | gzip > "$BACKUP_DIR/tables/${table}.sql.gz" + done + + log "${SYM_OK}${COLOR_INFO} Tabellen (außer assets): Insgesamt $total_nonasset_rows Einträge gesichert${COLOR_RESET}" + + #* Assettypen sichern mit Zählung + log "${SYM_PUZZLE} Starte assetType-Datensicherung mit Zeitraumfilter ab $(date -d "@$TIMESTAMP_GRENZE")" + + total_found=0 + total_saved=0 + + for assettype in "${!ASSET_NAMES[@]}"; do + local name="${ASSET_NAMES[$assettype]}" + log "${SYM_FOLDER} Exportiere assetType=$assettype ($name)${COLOR_RESET}" + + # Anzahl Datensätze in diesem Typ und Zeitraum + count=$(mysql -u"$DB_USER" -p"$DB_PASS" -N -e \ + "SELECT COUNT(*) FROM assets WHERE assetType = $assettype AND create_time >= $TIMESTAMP_GRENZE;" "$DB_NAME") + + if [[ "$count" -gt 0 ]]; then + ((total_found += count)) + ((total_saved += count)) + mysqldump -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" assets \ + --where="assetType = $assettype AND create_time >= $TIMESTAMP_GRENZE" \ + | gzip > "$BACKUP_DIR/assettypen/assets_${name}.sql.gz" + log "${SYM_OK} Gespeichert: $count Einträge für $name" + else + log "${SYM_FORWARD} Keine Daten für assetType=$assettype ($name)" + fi + done + + log "${SYM_OK}${COLOR_INFO} Asset-Sicherung abgeschlossen: $total_found gefunden, $total_saved gespeichert, Differenz: $((total_found - total_saved))${COLOR_RESET}" + + #* Konfig-Dateien sichern + log "${SYM_LOG} Sicherung der Konfigurationsdateien im robust/bin Verzeichnis${COLOR_RESET}" + find "$ROBUST_INI_DIR" -maxdepth 1 -type f \( \ + -name "MoneyServer.ini" -o -name "MoneyServer.ini.*" \ + -o -name "Robust.ini" -o -name "Robust.HG.*" -o -name "Robust.local.*" \) \ + -exec cp {} "$BACKUP_DIR/configs/" \; + + #* Archiv erstellen (Was passiert hier wird das unützerweise alles in einer Datei gepackt?) + if [ "$ARCHIVIEREN" = "ja" ]; then + log "${COLOR_HEADING}${SYM_PACKAGE} Erstelle tgz-Archiv: robustbackup_${TIMESTAMP}.tgz${COLOR_RESET}" + tar czf "$BACKUP_DIR/robustbackup_${TIMESTAMP}.tgz" -C "$BACKUP_DIR" . + log "${SYM_OKNN} Backup abgeschlossen: $BACKUP_DIR/robustbackup_${TIMESTAMP}.tgz${COLOR_RESET}" + fi +} + +# todo: Testen, ob alles funktioniert. +# Wiederherstellt eine Robust-Datenbank aus einer Dump-Datei. +# Aufruf: +#* restoreRobustDump +function restoreRobustDump() { + local DB_USER=$1 + local DB_PASS=$2 + local DUMP_FILE=$3 + local TARGET_DB=$4 + + SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + local TMPDIR + TMPDIR=$(mktemp -d) + local total_tables=0 + + log "${COLOR_HEADING}${SYM_PACKAGE} Starte Wiederherstellung von Dump-Datei ${COLOR_FILE}${DUMP_FILE}${COLOR_RESET} in Datenbank ${COLOR_VALUE}${TARGET_DB}${COLOR_RESET}" + + if [[ ! -f "$DUMP_FILE" ]]; then + log "${SYM_BAD} ${COLOR_BAD}Fehler:${COLOR_RESET} Dump-Datei nicht gefunden: ${COLOR_FILE}$DUMP_FILE${COLOR_RESET}" + return 1 + fi + + #* Datenbank vorbereiten + log "${SYM_TOOLS} Entferne vorhandene Datenbank (falls vorhanden) und erstelle neu: ${COLOR_VALUE}$TARGET_DB${COLOR_RESET}" + mysql -u"$DB_USER" -p"$DB_PASS" -e "DROP DATABASE IF EXISTS \`$TARGET_DB\`; CREATE DATABASE \`$TARGET_DB\`;" + + #* Dump-Datei aufteilen + log "${SYM_SCRIPT} Zerlege Dump-Datei in einzelne Tabellen im Temp-Verzeichnis: ${COLOR_FILE}$TMPDIR${COLOR_RESET}" + csplit -z -f "$TMPDIR/table_" -b "%03d.sql" "$DUMP_FILE" '/DROP TABLE IF EXISTS/' '{*}' > /dev/null + + #* Import der Tabellen + for sqlfile in "$TMPDIR"/table_*.sql; do + local tablename + tablename=$(grep -i "CREATE TABLE" "$sqlfile" | awk -F'\`' '{print $2}') + if [[ -n "$tablename" ]]; then + log "${SYM_FILE} Importiere Tabelle: ${COLOR_FILE}$tablename${COLOR_RESET}" + mysql -u"$DB_USER" -p"$DB_PASS" "$TARGET_DB" < "$sqlfile" + ((total_tables++)) + fi + done + + log "${SYM_OK}${COLOR_INFO} Import abgeschlossen: $total_tables Tabellen importiert${COLOR_RESET}" + + #* Asset-Typen prüfen und reparieren + log "${SYM_PUZZLE} Prüfe asset.assetType auf ungültige Werte..." + invalid_count=$(mysql -u"$DB_USER" -p"$DB_PASS" -sse "SELECT COUNT(*) FROM asset WHERE assetType NOT BETWEEN 0 AND 49;" "$TARGET_DB") + + if ((invalid_count > 0)); then + log "${SYM_WARNING} Ungültige assetType-Werte gefunden: $invalid_count — korrigiere auf 0" + mysql -u"$DB_USER" -p"$DB_PASS" -e "UPDATE asset SET assetType = 0 WHERE assetType NOT BETWEEN 0 AND 49;" "$TARGET_DB" + else + log "${SYM_OK} Alle assetType-Werte sind gültig" + fi + + #* Datenbank reparieren + log "${SYM_TOOLS} Führe mysqlcheck mit Reparatur durch..." + mysqlcheck -u"$DB_USER" -p"$DB_PASS" --auto-repair "$TARGET_DB" + + log "${SYM_OK} ${COLOR_OK}Wiederherstellung erfolgreich abgeschlossen für:${COLOR_RESET} ${COLOR_VALUE}$TARGET_DB${COLOR_RESET}" +} + + +# todo: Testen, ob alles funktioniert. +# robustrestore +# dbuser: SQL-Benutzername +# dbpass: Passwort des SQL-Benutzers +# all: Wiederherstellung aller Nicht-Asset-Tabellen und Konfig-Dateien +# tables: Wiederherstellung aller Nicht-Asset-Tabellen +# assets: Wiederherstellung der assetType-Tabellen und Konfig-Dateien +# assettype: Wiederherstellung der angegebenen assetType-Tabellen +function robustrestore() { + local DB_USER=$1 + local DB_PASS=$2 + local DB_NAME="robust" + local RESTORE_SCOPE=$3 # "all", "tables", "assets" oder Asset-Typ-Name + SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + local BACKUP_DIR="$SCRIPT_DIR/backup/robust" + + log "${COLOR_HEADING}${SYM_PACKAGE} Starte Wiederherstellung für: ${COLOR_VALUE}${RESTORE_SCOPE}${COLOR_RESET}" + + case "$RESTORE_SCOPE" in + all) + log "${SYM_LOG} ${COLOR_LABEL}Wiederherstellung aller Nicht-Asset-Tabellen...${COLOR_RESET}" + for file in "$BACKUP_DIR/tables/"*.sql.gz; do + table_name=$(basename "$file" .sql.gz) + log "${SYM_FILE} ${COLOR_FILE}${file}${COLOR_RESET} ${COLOR_ACTION}→ Importiere Tabelle: ${COLOR_LABEL}${table_name}${COLOR_RESET}" + gunzip -c "$file" | mysql -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" + done + + log "${SYM_LOG} ${COLOR_LABEL}Wiederherstellung aller Asset-Typen...${COLOR_RESET}" + for file in "$BACKUP_DIR/assettypen/"*.sql.gz; do + asset_name=$(basename "$file" .sql.gz) + log "${SYM_FILE} ${COLOR_FILE}${file}${COLOR_RESET} ${COLOR_ACTION}→ Importiere Asset-Typ: ${COLOR_LABEL}${asset_name}${COLOR_RESET}" + gunzip -c "$file" | mysql -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" + done + ;; + tables) + log "${SYM_LOG} ${COLOR_LABEL}Wiederherstellung nur der Nicht-Asset-Tabellen...${COLOR_RESET}" + for file in "$BACKUP_DIR/tables/"*.sql.gz; do + table_name=$(basename "$file" .sql.gz) + log "${SYM_FILE} ${COLOR_FILE}${file}${COLOR_RESET} ${COLOR_ACTION}→ Importiere Tabelle: ${COLOR_LABEL}${table_name}${COLOR_RESET}" + gunzip -c "$file" | mysql -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" + done + ;; + assets) + log "${SYM_LOG} ${COLOR_LABEL}Wiederherstellung aller Asset-Typen...${COLOR_RESET}" + for file in "$BACKUP_DIR/assettypen/"*.sql.gz; do + asset_name=$(basename "$file" .sql.gz) + log "${SYM_FILE} ${COLOR_FILE}${file}${COLOR_RESET} ${COLOR_ACTION}→ Importiere Asset-Typ: ${COLOR_LABEL}${asset_name}${COLOR_RESET}" + gunzip -c "$file" | mysql -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" + done + ;; + *) + local file="$BACKUP_DIR/assettypen/assets_${RESTORE_SCOPE}.sql.gz" + if [[ -f "$file" ]]; then + log "${SYM_FOLDER} ${COLOR_LABEL}Wiederherstellung für Asset-Typ: ${COLOR_VALUE}${RESTORE_SCOPE}${COLOR_RESET}" + log "${SYM_FILE} ${COLOR_FILE}${file}${COLOR_RESET}" + gunzip -c "$file" | mysql -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" + else + log "${SYM_BAD} ${COLOR_BAD}Fehler: Kein Backup für Asset-Typ '${RESTORE_SCOPE}' gefunden!${COLOR_RESET}" + return 1 + fi + ;; + esac + + log "${SYM_OK} ${COLOR_OK}Wiederherstellung erfolgreich abgeschlossen für Bereich: ${COLOR_VALUE}${RESTORE_SCOPE}${COLOR_RESET}" +} + +# todo: Testen, ob alles funktioniert. +# robustrepair +# dbuser: SQL-Benutzername +# dbpass: Passwort des SQL-Benutzers +# check: Prüft alle Tabellen auf Fehler +# repair: Repariert alle Tabellen automatisch +# truncate: Leert alle Inhalte, behält aber Tabellenstrukturen +# dropassets: Löscht alle Assets und Asset-Typen +# dropall: Löscht alle Tabelleninhalte +function robustrepair() { + local DB_USER=$1 + local DB_PASS=$2 + local DB_NAME="robust" + local ACTION=$3 # "check", "repair", "truncate", "dropassets", "dropall" + + log "${COLOR_HEADING}${SYM_TOOLS} Starte Datenbankwartung: ${COLOR_VALUE}${ACTION}${COLOR_RESET}" + + case "$ACTION" in + check) + log "${SYM_WAIT} ${COLOR_LABEL}Überprüfe Tabellen auf Fehler...${COLOR_RESET}" + mysqlcheck -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" + ;; + repair) + log "${SYM_BOOK} ${COLOR_LABEL}Repariere beschädigte Tabellen automatisch...${COLOR_RESET}" + mysqlcheck -u"$DB_USER" -p"$DB_PASS" --auto-repair "$DB_NAME" + ;; + truncate) + log "${SYM_WARNING} ${COLOR_WARNING}${SYM_WARNING} Achtung: Leert alle Inhalte, behält aber Tabellenstrukturen!${COLOR_RESET}" + echo -ne "${COLOR_BAD}Fortfahren? (ja/nein): ${COLOR_RESET}" + read -r confirm + if [[ "$confirm" == "ja" ]]; then + local tables + tables=$(mysql -u"$DB_USER" -p"$DB_PASS" -N -e "SHOW TABLES IN $DB_NAME;") + for t in $tables; do + log "${SYM_CLEAN} Leere Tabelle: ${COLOR_LABEL}${t}${COLOR_RESET}" + mysql -u"$DB_USER" -p"$DB_PASS" -e "TRUNCATE TABLE $DB_NAME.$t;" + done + log "${SYM_OK} ${COLOR_OK}Alle Tabellen wurden geleert.${COLOR_RESET}" + else + log "${SYM_BAD} ${COLOR_BAD}Abgebrochen.${COLOR_RESET}" + fi + ;; + dropassets) + log "${SYM_WARNING} ${COLOR_WARNING}${SYM_WARNING} Achtung: Löscht ALLE Einträge in der 'assets'-Tabelle!${COLOR_RESET}" + echo -ne "${COLOR_BAD}Fortfahren? (ja/nein): ${COLOR_RESET}" + read -r confirm + if [[ "$confirm" == "ja" ]]; then + mysql -u"$DB_USER" -p"$DB_PASS" -e "DELETE FROM $DB_NAME.assets;" + log "${SYM_OK} ${COLOR_OK}Tabelle 'assets' wurde geleert.${COLOR_RESET}" + else + log "${SYM_BAD} ${COLOR_BAD}Abgebrochen.${COLOR_RESET}" + fi + ;; + dropall) + log "${SYM_WARNING} ${COLOR_WARNING}${SYM_WARNING} Achtung: Alle Tabellen der Datenbank werden gelöscht!${COLOR_RESET}" + echo -ne "${COLOR_BAD}Wirklich ALLE Tabellen löschen? (ja/nein): ${COLOR_RESET}" + read -r confirm + if [[ "$confirm" == "ja" ]]; then + local tables + tables=$(mysql -u"$DB_USER" -p"$DB_PASS" -N -e "SHOW TABLES IN $DB_NAME;") + for t in $tables; do + log "${SYM_CLEAN} Lösche Tabelle: ${COLOR_LABEL}${t}${COLOR_RESET}" + mysql -u"$DB_USER" -p"$DB_PASS" -e "DROP TABLE $DB_NAME.$t;" + done + log "${SYM_OK} ${COLOR_OK}Alle Tabellen gelöscht.${COLOR_RESET}" + else + log "${SYM_BAD} ${COLOR_BAD}Abgebrochen.${COLOR_RESET}" + fi + ;; + *) + log "${SYM_BAD} ${COLOR_BAD}Unbekannte Aktion: ${ACTION}${COLOR_RESET}" + log "${SYM_INFO} Mögliche Optionen: check | repair | truncate | dropassets | dropall" + return 1 + ;; + esac +} + +# todo: Wenn die Parameter der Eingabe nicht stimmt, ausgeben was fehlt und der Aufruf der Funktion anzeigen: + +###* load und save oar und iar + +# - $1: Der Name des Besitzers. +# - $2: Das Verzeichnis, in das das IAR geladen werden soll +# - $3: Das Passwort fuer das IAR +# - $4: Der Dateiname des IAR +# bash osmtool.sh loadinventar "Vorname Nachname" "/pfad/zum/verzeichnis" "Benutzerpasswort" "/pfad/zum/Inventarname.iar" +function loadinventar() { + # Letzte Bearbeitung 30.09.2023 + LOADINVSCREEN="sim1" + NAME=$1 + VERZEICHNIS=$2 + local PASSWORD=$3 + DATEI=$4 + + if [ $# -ne 4 ]; then + echo "Falsche Anzahl an Parametern." + echo 'Verwendung: bash osmtool.sh loadinventar "Vorname Nachname" "/pfad/zum/verzeichnis" "Benutzerpasswort" "/pfad/zum/Inventarname.iar"' + return 1 + fi + + # Benutzer um Bestätigung fragen + echo "Soll die Datei '$DATEI' von '$VERZEICHNIS' für '$NAME' hochgeladen werden? (j/n)" + read -r ANTWORT + + if [[ "$ANTWORT" != "j" ]]; then + echo "Upload abgebrochen." + return 1 + fi + + # ueberpruefen, ob der 'screen'-Prozess existiert. + if screen -list | grep -q "$LOADINVSCREEN"; then + log "OSCOMMAND: load iar $NAME $VERZEICHNIS ***** $DATEI" + screen -S "$LOADINVSCREEN" -p 0 -X eval "stuff 'load iar $NAME $VERZEICHNIS $PASSWORD $DATEI'^M" + return 0 + else + log error "OSCOMMAND: Der Screen $LOADINVSCREEN existiert nicht" + return 1 + fi +} + +# $1 - Name des Inventars +# $2 - Verzeichnis, in dem das Inventar gespeichert werden soll +# $3 - Passwort (optional) +# $4 - Dateiname, unter dem das Inventar gespeichert wird +#? Verwendungsbeispiel: +# bash osmtool.sh saveinventar "Vorname Nachname" "/pfad/zum/verzeichnis" "Benutzerpasswort" "/pfad/zum/Inventarname.iar" +function saveinventar() { + # Letzte Bearbeitung 01.10.2023 + SAVEINVSCREEN="sim1" + NAME=$1 + VERZEICHNIS=$2 + local PASSWORD=$3 + DATEI=$4 + + if [ $# -ne 4 ]; then + echo "Falsche Anzahl an Parametern." + echo 'Verwendung: bash osmtool.sh saveinventar "Vorname Nachname" "/pfad/zum/verzeichnis" "Benutzerpasswort" "/pfad/zum/Inventarname.iar"' + return 1 + fi + + # Benutzer um Bestätigung fragen + echo "Soll die Datei '$DATEI' von '$VERZEICHNIS' für '$NAME' wirklich gespeichert werden? (j/n)" + read -r ANTWORT + + if [[ "$ANTWORT" != "j" ]]; then + echo "Speichern abgebrochen." + return 1 + fi + + # todo: fragen j/n ob tatsächlich gespeichert werden soll und die Parameter dabei anzeigen + if screen -list | grep -q "$SAVEINVSCREEN"; then + log "OSCOMMAND: save iar $NAME $VERZEICHNIS ***** $DATEI " + screen -S "$SAVEINVSCREEN" -p 0 -X eval "stuff 'save iar $NAME $VERZEICHNIS $PASSWORD $DATEI'^M" + return 0 + else + log "OSCOMMAND: Der Screen $SAVEINVSCREEN existiert nicht" + return 1 + fi +} + +#? Verwendungsbeispiel: +# bash osmtool.sh loadoar "sim1" "TestRegion" "backup.oar" +function loadoar() { + RESTOREVERZEICHNISSCREENNAME=$1 + REGIONSNAME=$2 + OARFILE=$3 + + if [ $# -ne 3 ]; then + echo "Falsche Anzahl an Parametern." + echo 'Verwendung: bash osmtool.sh loadoar "sim1" "TestRegion" "backup.oar"' + return 1 + fi + + # Benutzer um Bestätigung fragen + echo "Soll die Region '$REGIONSNAME' mit der Datei '$OARFILE.oar' wirklich wiederhergestellt werden? (j/n)" + read -r ANTWORT + + if [[ "$ANTWORT" != "j" ]]; then + echo "Wiederherstellung abgebrochen." + return 1 + fi + + log "OSRESTORE: Region $NSDATEINAME wiederherstellen" + cd /"$SCRIPT_DIR"/"$RESTOREVERZEICHNISSCREENNAME"/bin || return 1 + log "Ich kann nicht pruefen ob die Region im OpenSimulator vorhanden ist." + log "Sollte sie nicht vorhanden sein wird root also alle Regionen wiederhergestellt." + # todo: fragen j/n ob tatsächlich hochgeladen werden soll und die Parameter dabei anzeigen + screen -S "$RESTOREVERZEICHNISSCREENNAME" -p 0 -X eval "stuff 'change region ${REGIONSNAME//\"/}'^M" + screen -S "$RESTOREVERZEICHNISSCREENNAME" -p 0 -X eval "stuff 'load oar $OARFILE'^M" + + log "OSRESTORE: Region $REGIONSNAME wird wiederhergestellt" + return 0 +} + +# Aufruf: saveoar "sim1" "testregion" "/pfad/zum/backup.oar" +function saveoar() { + local SCREENNAME=$1 + local REGIONNAME=$2 + local OARFILE=$3 + + if [ $# -ne 3 ]; then + echo "Falsche Anzahl an Parametern." + echo 'Verwendung: bash osmtool.sh saveoar "sim1" "testregion" "/pfad/zum/backup.oar"' + return 1 + fi + + # Prüfen, ob alle Parameter gesetzt sind + if [[ -z "$SCREENNAME" || -z "$REGIONNAME" || -z "$OARFILE" ]]; then + echo "Fehler: Screen-Name, Regionsname und OAR-Datei müssen angegeben werden!" + return 1 + fi + + # Benutzer um Bestätigung fragen + echo "Soll die Region '$REGIONNAME' wirklich in '$OARFILE' gesichert werden? (j/n)" + read -r ANTWORT + + if [[ "$ANTWORT" != "j" ]]; then + echo "Speicherung abgebrochen." + return 1 + fi + + # Prüfen, ob der Screen existiert + if ! screen -list | grep -q "$SCREENNAME"; then + echo "$SCREENNAME" + echo "Fehler: Der Screen '$SCREENNAME' existiert nicht!" + return 1 + fi + + # Region auswählen und OAR-Datei speichern + echo "Sichere Region '$REGIONNAME' in '$OARFILE'..." + screen -S "$SCREENNAME" -p 0 -X eval "stuff 'change region $REGIONNAME'^M" + screen -S "$SCREENNAME" -p 0 -X eval "stuff 'save oar \"$OARFILE\"'^M" + + echo "Region erfolgreich gesichert!" + return 0 +} + +# bash osmtool.sh autoregionbackup +function autoregionbackup() { + # Konfiguration + local BACKUP_ROOT="$SCRIPT_DIR/backup" # Relativer Pfad + local BACKUP_WAIT=180 # 180, 300 , 500 Wartezeit zwischen Backups, zu kleine Werte uberfordern den Server. + DATUM=$(date +%Y%m%d_%H%M%S) + + # Backup-Verzeichnis erstellen + mkdir -p "$BACKUP_ROOT" || { + echo "FEHLER: Backup-Verzeichnis konnte nicht erstellt werden" + return 1 + } + + # Funktion zum Extrahieren des Regionsnamens aus der INI-Datei + get_region_name() { + local ini_file="$1" + # Extrahiere den Namen zwischen [ ] + grep -oP '^\[\K[^\]]+' "$ini_file" | head -1 + } + + # Durchsuche alle simX/bin/Regions-Verzeichnisse + for ((i=1; i<=999; i++)); do + local sim_name="sim$i" + local regions_dir="$sim_name/bin/Regions" + + if [[ -d "$sim_name/bin" && -d "$regions_dir" ]]; then + echo "Verarbeite Simulator: $sim_name" + + # Backup-Verzeichnis erstellen + mkdir -p "$BACKUP_ROOT/$sim_name" || { + echo "FEHLER: Backup-Verzeichnis konnte nicht erstellt werden" + return 1 + } + + # Verarbeite jede Regions-Konfiguration + for region_file in "$regions_dir"/*.ini; do + [[ -f "$region_file" ]] || continue + + # Extrahiere den echten Regionsnamen aus der INI-Datei (mit Leerzeichen) + display_name=$(get_region_name "$region_file") + safe_name=$(basename "$region_file" .ini) # Dateiname als Fallback + + # Verwende den echten Namen wenn vorhanden, sonst Dateinamen + local region_name=${display_name:-$safe_name} + safe_filename=$(echo "$region_name" | tr ' ' '_') # Für Dateinamen + + # Überspringe Offline-Regionen + if [[ -f "$region_file.offline" ]]; then + echo "Überspringe offline Region: $region_name" + continue + fi + + echo "Starte Backup von: $sim_name $region_name" + + # 1. Region auswählen (mit Originalname und Leerzeichen) + screen -S "$sim_name" -p 0 -X eval "stuff 'change region \"${region_name//\"/}\"'^M" + echo "change region \"$region_name\"" + sleep 1 + + # 2. OAR-Backup (mit safe_filename) + screen -S "$sim_name" -p 0 -X eval "stuff 'save oar \"$BACKUP_ROOT/$sim_name/$DATUM-${safe_filename}.oar\"'^M" + echo "Backup: $DATUM-${safe_filename}.oar" + sleep 1 + + # 3. XML2-Backup + screen -S "$sim_name" -p 0 -X eval "stuff 'save xml2 \"$BACKUP_ROOT/$sim_name/$DATUM-${safe_filename}.xml2\"'^M" + echo "Backup: $DATUM-${safe_filename}.xml2" + sleep 1 + + # 4. Terrain-Backups + screen -S "$sim_name" -p 0 -X eval "stuff 'terrain save \"$BACKUP_ROOT/$sim_name/$DATUM-${safe_filename}.png\"'^M" + screen -S "$sim_name" -p 0 -X eval "stuff 'terrain save \"$BACKUP_ROOT/$sim_name/$DATUM-${safe_filename}.raw\"'^M" + echo "Terrain: $DATUM-${safe_filename}.png/.raw" + sleep 1 + + # 5. Regions-Konfiguration kopieren + if [[ -f "$region_file" ]]; then + cp "$region_file" "$BACKUP_ROOT/$sim_name/$DATUM-${safe_filename}.ini" + echo "Kopiere: $DATUM-${safe_filename}.ini" + sleep 1 + fi + + echo "Erstelle Backup: $BACKUP_ROOT/$sim_name/$DATUM-${safe_filename}.*" + sleep "$BACKUP_WAIT" + done + fi + done + + echo "Backup aller Regionen abgeschlossen" + return 0 +} + +#?────────────────────────────────────────────────────────────────────────────────────────── +#* Bereinigen des OpenSimulators Grids +#?────────────────────────────────────────────────────────────────────────────────────────── + +function dataclean() { + log "${COLOR_HEADING}🧹 Datenbereinigung wird durchgeführt...${COLOR_RESET}" + + # RobustServer bereinigen + if [[ -d "robust/bin" ]]; then + log "${SYM_OK} ${COLOR_ACTION}Lösche Dateien im RobustServer...${COLOR_RESET}" + find "robust/bin" -maxdepth 1 -type f \( -name "*.log" -o -name "*.dll" -o -name "*.exe" -o -name "*.so" -o -name "*.xml" -o -name "*.dylib" -o -name "*.example" -o -name "*.sample" -o -name "*.txt" -o -name "*.config" -o -name "*.py" -o -name "*.old" -o -name "*.pdb" \) -delete + fi + + # Alle simX-Server bereinigen + for ((i=1; i<=999; i++)); do + sim_dir="sim$i/bin" + if [[ -d "$sim_dir" ]]; then + log "${SYM_OK} ${COLOR_ACTION}Lösche Dateien in ${COLOR_DIR}$sim_dir...${COLOR_RESET}" + find "$sim_dir" -maxdepth 1 -type f \( -name "*.log" -o -name "*.dll" -o -name "*.exe" -o -name "*.so" -o -name "*.xml" -o -name "*.dylib" -o -name "*.example" -o -name "*.sample" -o -name "*.txt" -o -name "*.config" -o -name "*.py" -o -name "*.old" -o -name "*.pdb" \) -delete + sleep 1 + fi + done + log "${COLOR_HEADING}${SYM_OKNN} Datenbereinigung abgeschlossen${COLOR_RESET}" + blankline +} + +function pathclean() { + directories=("assetcache" "maptiles" "MeshCache" "j2kDecodeCache" "ScriptEngines" "bakes" "addon-modules") + wildcard_dirs=("addin-db-*") # Separate Liste für Wildcard-Verzeichnisse + + log "${COLOR_HEADING}🗂️ Verzeichnisbereinigung wird durchgeführt...${COLOR_RESET}" + + # RobustServer bereinigen + if [[ -d "robust/bin" ]]; then + log "${SYM_OK} ${COLOR_ACTION}Lösche komplette Verzeichnisse im RobustServer...${COLOR_RESET}" + + # Normale Verzeichnisse + for dir in "${directories[@]}"; do + target="robust/bin/$dir" + if [[ -d "$target" ]]; then + rm -rf "$target" + log " ${COLOR_ACTION}Verzeichnis gelöscht: ${COLOR_DIR}$target${COLOR_RESET}" + fi + done + + # Wildcard-Verzeichnisse + for pattern in "${wildcard_dirs[@]}"; do + for target in robust/bin/$pattern; do + if [[ -d "$target" ]]; then + rm -rf "$target" + log " ${COLOR_WARNING}Verzeichnis gelöscht: ${COLOR_DIR}$target${COLOR_RESET}" + fi + done + done + fi + + # Alle simX-Server bereinigen + for ((i=1; i<=999; i++)); do + sim_dir="sim$i/bin" + if [[ -d "$sim_dir" ]]; then + log "${SYM_OK} ${COLOR_ACTION}Lösche komplette Verzeichnisse in ${COLOR_DIR}$sim_dir...${COLOR_RESET}" + + # Normale Verzeichnisse + for dir in "${directories[@]}"; do + target="$sim_dir/$dir" + if [[ -d "$target" ]]; then + rm -rf "$target" + log " ${COLOR_ACTION}Verzeichnis gelöscht: ${COLOR_DIR}$target${COLOR_RESET}" + sleep 1 + fi + done + + # Wildcard-Verzeichnisse + for pattern in "${wildcard_dirs[@]}"; do + for target in $sim_dir/$pattern; do + if [[ -d "$target" ]]; then + rm -rf "$target" + log " ${COLOR_WARNING}Verzeichnis gelöscht: ${COLOR_DIR}$target${COLOR_RESET}" + fi + done + done + fi + done + log "${COLOR_HEADING}${SYM_OKNN} Verzeichnisbereinigung abgeschlossen${COLOR_RESET}" + blankline +} + +function cacheclean() { + #cache_dirs=("assetcache" "maptiles" "MeshCache" "j2kDecodeCache" "ScriptEngines") + cache_dirs=("assetcache" "MeshCache" "j2kDecodeCache" "ScriptEngines") + + log "${COLOR_HEADING}♻️ Cache-Bereinigung wird durchgeführt...${COLOR_RESET}" + + # RobustServer bereinigen + if [[ -d "robust/bin" ]]; then + log "${SYM_OK} ${COLOR_ACTION}Leere Cache-Verzeichnisse im RobustServer...${COLOR_RESET}" + for dir in "${cache_dirs[@]}"; do + target="robust/bin/$dir" + if [[ -d "$target" ]]; then + find "$target" -mindepth 1 -delete + log " ${COLOR_ACTION}Inhalt geleert: ${COLOR_DIR}$target${COLOR_RESET}" + fi + done + fi + + # Alle simX-Server bereinigen + for ((i=1; i<=999; i++)); do + sim_dir="sim$i/bin" + if [[ -d "$sim_dir" ]]; then + log "${SYM_OK} ${COLOR_ACTION}Leere Cache-Verzeichnisse in ${COLOR_DIR}$sim_dir...${COLOR_RESET}" + for dir in "${cache_dirs[@]}"; do + target="$sim_dir/$dir" + if [[ -d "$target" ]]; then + find "$target" -mindepth 1 -delete + log " ${COLOR_ACTION}Inhalt geleert: ${COLOR_DIR}$target${COLOR_RESET}" + sleep 1 + fi + done + fi + done + log "${COLOR_HEADING}${SYM_OKNN} Cache-Bereinigung abgeschlossen${COLOR_RESET}" + blankline +} + +function logclean() { + log "${SYM_LOG}${COLOR_HEADING} Log-Bereinigung wird durchgeführt...${COLOR_RESET}" + + # RobustServer bereinigen + if [[ -d "robust/bin" ]]; then + log "${SYM_OK} ${COLOR_ACTION}Lösche Log-Dateien in ${COLOR_DIR}robust/bin...${COLOR_RESET}" + rm -f robust/bin/*.log + fi + + # Alle simX-Server bereinigen (sim1 bis sim999) + for ((i=1; i<=999; i++)); do + sim_dir="sim$i/bin" + if [[ -d "$sim_dir" ]]; then + log "${SYM_OK} ${COLOR_ACTION}Lösche Log-Dateien in ${COLOR_DIR}$sim_dir...${COLOR_RESET}" + rm -f "$sim_dir"/*.log + sleep 1 + fi + done + + log "${COLOR_HEADING}Log-Bereinigung abgeschlossen${COLOR_RESET}" + blankline +} + +function mapclean() { + log "${COLOR_HEADING}🗺️ Map-Tile-Bereinigung wird durchgeführt...${COLOR_RESET}" + + # Sicherheitscheck für robust/bin/maptiles + if [[ -d "robust/bin/maptiles" ]]; then + rm -rf -- "robust/bin/maptiles/"* + log "${SYM_OK} ${COLOR_ACTION}robust/bin/maptiles geleert${COLOR_RESET}" + fi + + # Sicherheitscheck für alle simX/bin/maptiles + for ((i=1; i<=999; i++)); do + local sim_dir="sim$i/bin/maptiles" + if [[ -d "$sim_dir" ]]; then + # shellcheck disable=SC2115 + rm -rf -- "${sim_dir}/"* + log "${SYM_OK} ${COLOR_ACTION}${COLOR_DIR}$sim_dir${COLOR_RESET} ${COLOR_ACTION}geleert${COLOR_RESET}" + sleep 1 + fi + done + + log "${COLOR_HEADING}${SYM_OKNN} Map-Tile-Bereinigung abgeschlossen${COLOR_RESET}" + blankline +} + +function autoallclean() { + local confirm + + # Warnung in Gelb (konsistenter mit \e anstelle von \033) + log "\e[33m" + cat >&2 <<'WARNUNG' +=== WARNUNG: Alle Daten-, Verzeichnis-, Cache-, Log- und Map-Daten werden bereinigt! === +=== DIESE OPERATION KANN NICHT RÜCKGÄNGIG GEMACHT WERDEN! === +=== DER OPENSIMULATOR MUSS IM ANSCHLUSS NEU INSTALLIERT WERDEN! === +WARNUNG + log "\e[0m" + + # Bestätigung mit Timeout (10 Sekunden) für Sicherheit + read -t 30 -r -p "Fortfahren? (ja/NEIN): " confirm || { + log "\n\e[31mTimeout: Keine Bestätigung erhalten. Abbruch.\e[0m" >&2 + return 1 + } + + case "${confirm,,}" in + ja|j|y|yes) + log "\e[32mBereinigung wird gestartet...\e[0m" >&2 + # Jede Clean-Funktion mit Fehlerprüfung + local clean_functions=(dataclean pathclean cacheclean logclean mapclean) + for func in "${clean_functions[@]}"; do + if ! command -v "$func" &>/dev/null; then + log "\e[31mFehler: '$func' ist keine gültige Funktion!\e[0m" >&2 + return 1 + fi + "$func" || { + log "\e[31mFehler bei $func!\e[0m" >&2 + return 1 + } + done + log "\e[32mBereinigung abgeschlossen.\e[0m" >&2 + ;; + *) + log "\e[33mAbbruch: Bereinigung wurde nicht durchgeführt.\e[0m" >&2 + return 1 + ;; + esac + blankline +} + +function clean_linux_logs() { + local log_files=() + log "${COLOR_SECTION}${SYM_LOG} Suche nach alten Log-Dateien...${COLOR_RESET}" + + # Find and list files to be deleted + while IFS= read -r -d $'\0' file; do + log_files+=("$file") + log "${COLOR_FILE}${SYM_INFO} ${COLOR_LABEL}Gelöscht wird:${COLOR_RESET} ${COLOR_VALUE}$file${COLOR_RESET}" + done < <(find "/var/log" -name "*.log" -type f -mtime +7 -print0) + + if [ ${#log_files[@]} -eq 0 ]; then + log "${SYM_OK} ${COLOR_LABEL}Keine alten Log-Dateien gefunden.${COLOR_RESET}" + return 0 + fi + + log "${COLOR_WARNING}${#log_files[@]} Log-Dateien werden gelöscht. Fortfahren? (j/N) ${COLOR_RESET}" + read -r confirm + case "${confirm,,}" in + j|ja|y|yes) + find "/var/log" -name "*.log" -type f -mtime +7 -delete + log "${SYM_OK} ${COLOR_OK}${#log_files[@]} Log-Dateien wurden gelöscht.${COLOR_RESET}" + ;; + *) + log "${SYM_BAD} ${COLOR_BAD}Bereinigung abgebrochen.${COLOR_RESET}" + return 1 + ;; + esac +} + +function delete_opensim() { + log "${COLOR_HEADING}🗺️ Das komplette löschen vom OpenSimulator wird durchgeführt...${COLOR_RESET}" + + # Sicherheitsabfrage hinzufügen + read -rp "Sind Sie sicher, dass Sie ALLE OpenSimulator-Daten löschen möchten? (ja/N) " answer + [[ ${answer,,} != "ja" ]] && { echo "Abbruch."; return 1; } + + # Robust-Verzeichnis sicher löschen + if [[ -d "robust" ]]; then + rm -rf -- "robust" # Keine Wildcard mehr + log "${SYM_OK} ${COLOR_ACTION}robust komplett entfernt${COLOR_RESET}" + fi + + # Simulator-Verzeichnisse sicher löschen + for ((i=1; i<=999; i++)); do + local sim_dir="sim$i" + if [[ -d "$sim_dir" ]]; then + rm -rf -- "$sim_dir" # Keine Wildcard mehr + log "${SYM_OK} ${COLOR_ACTION}${COLOR_DIR}$sim_dir${COLOR_RESET} ${COLOR_ACTION}komplett entfernt${COLOR_RESET}" + sleep 1 + fi + done + + log "${COLOR_HEADING}${SYM_OKNN} Das komplette löschen vom OpenSimulator abgeschlossen${COLOR_RESET}" + blankline +} + +#?────────────────────────────────────────────────────────────────────────────────────────── +#* Konfigurationen +#?────────────────────────────────────────────────────────────────────────────────────────── + +# [Abschnitt] +# Schlüssel = Wert +# ; Kommentar +# [Section] +# Key = Value +# ; Comment + +# Überprüft, ob ein bestimmter Abschnitt (Section) in einer INI-Datei existiert, und gibt das Ergebnis als Exit-Code zurück. +# Aufruf: bash osmtool.sh verify_ini_section "test.ini" "Const" +# Letzte Prüfung am: 24.04.2025 +function verify_ini_section() { + local file="$1" + local section="$2" + + if [ ! -f "$file" ]; then + log "${SYM_BAD} Error: File ${file} does not exist" >&2 + return 2 + fi + + if grep -q "^\[${section}\]" "$file"; then + log "${SYM_OK} Section [${section}] exists in ${file}" + return 0 + else + log "${SYM_BAD} Section [${section}] not found in ${file}" + return 1 + fi +} + +# Überprüft, ob ein bestimmten Wert (key) im Abschnitt (Section) in einer INI-Datei existiert, und gibt das Ergebnis als Exit-Code zurück. +# Aufruf: bash osmtool.sh verify_ini_key "test.ini" "DatabaseService" "Include-Storage" +# Letzte Prüfung am: 24.04.2025 Repariert: 24.04.2025 +function verify_ini_key() { + local file="$1" + local section="$2" + local key="$3" + + if [ ! -f "$file" ]; then + log "${SYM_BAD} Error: File ${file} does not exist" >&2 + return 2 + fi + + # Einfacher grep-Befehl, der Leerzeichen & Kommentare ignoriert + if grep -Eq "^[[:space:]]*\[${section}\][[:space:]]*$" "$file" && \ + grep -Eq "^[[:space:]]*${key}[[:space:]]*=" "$file"; then + log "${SYM_OK} Key '${key}' exists in section [${section}]" + return 0 + else + log "${SYM_BAD} Key '${key}' NOT FOUND in section [${section}]" >&2 + return 1 + fi +} + +# Fügt einene Abschnitt hinzu, wenn es noch nicht vorhanden ist am ende der Datei. +# Aufruf: bash osmtool.sh add_ini_section "test.ini" "Const" +# Letzte Prüfung am: 24.04.2025 +function add_ini_section() { + local file="$1" + local section="$2" + + if verify_ini_section "$file" "$section" >/dev/null; then + return 0 + fi + + log "${SYM_INFO} Adding section [${section}] to ${file}" + + # Abschnitt am Ende der Datei hinzufügen + if printf "\n[%s]\n" "$section" >> "$file"; then + log "${SYM_OK} Successfully added section [${section}] to ${file}" + return 0 + else + log "${SYM_BAD} Failed to add section [${section}] to ${file}" >&2 + return 1 + fi +} + +# Fügt einene Abschnitt, wenn es noch nicht vorhanden ist vor dem Zielabschnitt hinzu. +# Also [Const] wird vor [DatabaseService] hinzugefügt. +# Aufruf: bash osmtool.sh add_ini_before_section "test.ini" "Const" "DatabaseService" +function add_ini_before_section() { + local file="$1" + local section="$2" + local target_section="$3" # Neuer Parameter für den Zielabschnitt + + if verify_ini_section "$file" "$section" >/dev/null; then + return 0 + fi + + #log "${SYM_INFO} Adding section [${section}] to ${file} before [${target_section}]" + + # Temporäre Datei erstellen + temp_file=$(mktemp) + + # Zielabschnitt suchen und neuen Abschnitt davor einfügen + local found=0 + while IFS= read -r line; do + if [[ "$line" =~ ^\[${target_section}\] ]]; then + printf "[%s]\n\n" "$section" >> "$temp_file" + found=1 + fi + echo "$line" >> "$temp_file" + done < "$file" + + # Falls Zielabschnitt nicht gefunden, am Ende einfügen (Fallback) + if [[ $found -eq 0 ]]; then + printf "\n[%s]\n" "$section" >> "$temp_file" + log "${SYM_WARNING} Target section [${target_section}] not found, appended [${section}] at end of file" + fi + + # Originaldatei ersetzen + if mv "$temp_file" "$file"; then + log "${SYM_OK} Successfully added section [${section}] to ${file}" + return 0 + else + log "${SYM_BAD} Failed to update ${file}" >&2 + return 1 + fi +} + +# Setzt den Wert des Keys in der gegebenen Sektion +# bash osmtool.sh set_ini_key "test.ini" "Const" "GridSize" "256" +# [Const] +# GridSize = 256 +# +# Wenn die Sektion oder der Key nicht vorhanden ist, wird er hinzugefügt. +# Falls der Key bereits vorhanden ist, wird der Wert aktualisiert. +function set_ini_key() { + local file="$1" section="$2" key="$3" value="$4" + local temp_file + temp_file=$(mktemp) || { + log "${COLOR_BAD}Fehler beim Erstellen der temporären Datei${COLOR_RESET}" >&2 + return 1 + } + + # Key für Regex absichern + local key_escaped + key_escaped=$(printf '%s\n' "$key" | sed 's/[]\/$*.^[]/\\&/g') + + # Verarbeitung mit awk + awk -v section="[$section]" -v key="$key" -v value="$value" -v key_re="$key_escaped" ' + BEGIN { in_section = 0; key_set = 0 } + + { + trimmed = $0 + sub(/^[[:space:]]+/, "", trimmed) + if (trimmed == section) { + print + in_section = 1 + next + } + + if (in_section && trimmed ~ /^\[.*\]/) { + if (!key_set) { + printf "%s = %s\n\n", key, value + key_set = 1 + } + in_section = 0 + } + + if (in_section && $0 ~ "^[[:blank:]]*" key_re "[[:blank:]]*=") { + printf "%s = %s\n", key, value + key_set = 1 + next + } + + print + } + + END { + if (in_section && !key_set) { + printf "%s = %s\n", key, value + } else if (!key_set) { + printf "\n[%s]\n%s = %s\n", section, key, value + } + }' "$file" > "$temp_file" || { + log "${COLOR_BAD}AWK-Verarbeitung fehlgeschlagen${COLOR_RESET}" >&2 + rm -f "$temp_file" + return 1 + } + + if ! mv "$temp_file" "$file"; then + log "${COLOR_BAD}Konnte Datei ${COLOR_FILE}${file}${COLOR_RESET} nicht aktualisieren" >&2 + rm -f "$temp_file" + return 1 + fi + + log "${COLOR_OK}Gesetzt: ${COLOR_KEY}${key} = ${COLOR_VALUE}${value}${COLOR_OK} in [${section}]${COLOR_RESET}" + return 0 +} + +# Setze einen Key in einer INI-Datei +# Die INI-Datei wird nach dem Schlüssel durchsucht und der Key wird +# am passenden Ort eingefügt. +function add_ini_key() { + local file="$1" section="$2" key="$3" value="$4" + local temp_file + temp_file=$(mktemp) || { + log "${COLOR_BAD}Failed to create temp file${COLOR_RESET}" >&2 + return 1 + } + + # Verarbeitung mit awk + if ! awk -v section="[${section}]" -v key="$key" -v value="$value" ' + BEGIN { in_section = 0; modified = 0 } + $0 == section { in_section = 1; print; next } + in_section && /^\[/ { + # Neue Sektion beginnt, Key einfügen (mit Leerzeichen um das =) + printf "%s = %s\n\n", key, value + modified = 1 + in_section = 0 + } + in_section && $0 ~ "^[[:blank:]]*" key "[[:blank:]]*=" { + # Existierenden Key ersetzen (mit Leerzeichen um das =) + printf "%s = %s\n", key, value + modified = 1 + in_section = 0 + next + } + { print } + END { + if (!modified && in_section) { + # Am Ende der Sektion einfügen (mit Leerzeichen um das =) + printf "%s = %s\n", key, value + } else if (!modified) { + # Neue Sektion am Dateiende (mit Leerzeichen um das =) + printf "\n[%s]\n%s = %s\n", section, key, value + } + } + ' "$file" > "$temp_file"; then + log "${COLOR_BAD}AWK processing failed${COLOR_RESET}" >&2 + rm -f "$temp_file" + return 1 + fi + + if ! mv "$temp_file" "$file"; then + log "${COLOR_BAD}Failed to update ${COLOR_FILE}${file}${COLOR_RESET}" >&2 + return 1 + fi + + log "${COLOR_OK}Set ${COLOR_KEY}${key} = ${COLOR_VALUE}${value}${COLOR_OK} in [${section}]${COLOR_RESET}" + return 0 +} + +function del_ini_section() { + local file="$1" + local section="$2" + + # Verifizieren ob der Abschnitt existiert (still, ohne Ausgabe) + if ! verify_ini_section "$file" "$section" >/dev/null; then + log "${SYM_INFO} Section [${section}] not found in ${COLOR_FILE}${file}${COLOR_RESET}" + return 0 + fi + + log "${SYM_INFO} Removing section [${COLOR_SECTION}${section}${COLOR_RESET}] from ${COLOR_FILE}${file}${COLOR_RESET}" + + # Temporäre Datei mit Fehlerbehandlung + local temp_file + temp_file=$(mktemp) || { + log "${SYM_BAD} Failed to create temporary file" >&2 + return 1 + } + + # Abschnitt entfernen + if awk -v section="[${section}]" ' + $0 == section { skip=1; next } + skip && /^\[/ { skip=0 } + !skip { print } + ' "$file" > "$temp_file"; then + + if mv "$temp_file" "$file"; then + log "${SYM_OK} Successfully removed section [${COLOR_SECTION}${section}${COLOR_RESET}]" + return 0 + else + log "${SYM_BAD} Failed to update ${COLOR_FILE}${file}${COLOR_RESET}" >&2 + rm -f "$temp_file" + return 1 + fi + + else + log "${SYM_BAD} AWK processing failed" >&2 + rm -f "$temp_file" + return 1 + fi +} + +function uncomment_ini_line() { + local file="$1" + local search_key="$2" + + # Sicherheitsprüfungen + [ ! -f "$file" ] && { log "${COLOR_BAD}Datei nicht gefunden: ${file}${COLOR_RESET}" >&2; return 1; } + [ -z "$search_key" ] && { log "${COLOR_BAD}Suchschlüssel darf nicht leer sein${COLOR_RESET}" >&2; return 1; } + + # Temporäre Datei erstellen + local temp_file + temp_file=$(mktemp) || { log "${COLOR_BAD}Temp-Datei konnte nicht erstellt werden${COLOR_RESET}" >&2; return 1; } + + # Entkommentieren der Zeile + sed -E "/^[[:space:]]*;[[:space:]]*${search_key}[[:space:]]*=/s/^[[:space:]]*;//" "$file" > "$temp_file" || { + rm -f "$temp_file" + log "${COLOR_BAD}Sed-Verarbeitung fehlgeschlagen${COLOR_RESET}" >&2 + return 1 + } + + # Originaldatei ersetzen + if mv "$temp_file" "$file"; then + log "${COLOR_OK}Zeile '${search_key}' erfolgreich entkommentiert${COLOR_RESET}" + return 0 + else + log "${COLOR_BAD}Datei konnte nicht aktualisiert werden${COLOR_RESET}" >&2 + return 1 + fi +} + +function uncomment_ini_section_line() { + local file="$1" + local section="$2" + local search_key="$3" + + # Sicherheitsprüfungen + [ ! -f "$file" ] && { log "${COLOR_BAD}Datei nicht gefunden: ${file}${COLOR_RESET}" >&2; return 1; } + [ -z "$section" ] && { log "${COLOR_BAD}Sektion darf nicht leer sein${COLOR_RESET}" >&2; return 1; } + [ -z "$search_key" ] && { log "${COLOR_BAD}Suchschlüssel darf nicht leer sein${COLOR_RESET}" >&2; return 1; } + + # Temporäre Datei erstellen + local temp_file + temp_file=$(mktemp) || { log "${COLOR_BAD}Temp-Datei konnte nicht erstellt werden${COLOR_RESET}" >&2; return 1; } + + # Entkommentieren der Zeile in der richtigen Sektion + awk -v section="[$section]" -v key="$search_key" ' + BEGIN { in_section = 0 } + $0 ~ "^\\[" section "\\]" { in_section = 1; print; next } + in_section && /^\[/ { in_section = 0 } # Sektion beendet + in_section && /^[[:space:]]*;[[:space:]]*"key"[[:space:]]*=[[:space:]]*/ { + sub(/^[[:space:]]*;/, "", $0) + } + { print } + ' "$file" > "$temp_file" || { + rm -f "$temp_file" + log "${COLOR_BAD}AWK-Verarbeitung fehlgeschlagen${COLOR_RESET}" >&2 + return 1 + } + + # Originaldatei ersetzen + if mv "$temp_file" "$file"; then + log "${COLOR_OK}Zeile '${search_key}' in Sektion '${section}' erfolgreich entkommentiert${COLOR_RESET}" + return 0 + else + log "${COLOR_BAD}Datei konnte nicht aktualisiert werden${COLOR_RESET}" >&2 + return 1 + fi +} +function comment_ini_line() { + local file="$1" + local section="$2" + local search_key="$3" + + # Sicherheitsprüfungen + [ ! -f "$file" ] && { log "${COLOR_BAD}Datei nicht gefunden: ${file}${COLOR_RESET}" >&2; return 1; } + [ -z "$section" ] && { log "${COLOR_BAD}Sektion darf nicht leer sein${COLOR_RESET}" >&2; return 1; } + [ -z "$search_key" ] && { log "${COLOR_BAD}Suchschlüssel darf nicht leer sein${COLOR_RESET}" >&2; return 1; } + + # Temporäre Datei erstellen + local temp_file + temp_file=$(mktemp) || { log "${COLOR_BAD}Temp-Datei konnte nicht erstellt werden${COLOR_RESET}" >&2; return 1; } + + # Zeile in der richtigen Sektion kommentieren + awk -v section="[$section]" -v key="$search_key" ' + BEGIN { in_section = 0 } + $0 ~ "^\\[" section "\\]" { in_section = 1; print; next } + in_section && /^\[/ { in_section = 0 } + in_section && $0 ~ key && !/^[[:space:]]*;/ { + # Nur nicht bereits kommentierte Zeilen kommentieren + sub(/^[[:space:]]*/, "&;", $0) + print $0 + next + } + { print } + ' "$file" > "$temp_file" || { + rm -f "$temp_file" + log "${COLOR_BAD}AWK-Verarbeitung fehlgeschlagen${COLOR_RESET}" >&2 + return 1 + } + + # Originaldatei ersetzen + if mv "$temp_file" "$file"; then + log "${COLOR_OK}Zeile '${search_key}' in Sektion '${section}' erfolgreich kommentiert${COLOR_RESET}" + return 0 + else + log "${COLOR_BAD}Datei konnte nicht aktualisiert werden${COLOR_RESET}" >&2 + return 1 + fi +} + +function clear_ini_section() { + local file="$1" + local section="$2" + + # Sicherheitsprüfungen + [ ! -f "$file" ] && { log "${COLOR_BAD}Datei nicht gefunden: ${file}${COLOR_RESET}" >&2; return 1; } + [ -z "$section" ] && { log "${COLOR_BAD}Sektion darf nicht leer sein${COLOR_RESET}" >&2; return 1; } + + # Temporäre Datei erstellen + local temp_file + temp_file=$(mktemp) || { log "${COLOR_BAD}Temp-Datei konnte nicht erstellt werden${COLOR_RESET}" >&2; return 1; } + + # AWK zum Leeren der Sektion (behält Header) + awk -v section="$section" ' + BEGIN { in_section = 0 } + + # Sektionsanfang erkennen + $0 ~ "^\\[" section "\\]" { + in_section = 1 + print $0 # Header ausgeben + next + } + + # Sektionsende erkennen + in_section && /^\[/ { + in_section = 0 + } + + # Innerhalb der Sektion: nichts ausgeben (löschen) + in_section { next } + + # Außerhalb der Sektion: normale Ausgabe + { print } + ' "$file" > "$temp_file" + + # Originaldatei ersetzen + if mv "$temp_file" "$file"; then + log "${COLOR_OK}Sektion '[${section}]' erfolgreich geleert${COLOR_RESET}" + return 0 + else + log "${COLOR_BAD}Datei konnte nicht aktualisiert werden${COLOR_RESET}" >&2 + return 1 + fi +} + +#?────────────────────────────────────────────────────────────────────────────────────────── +#* Konfigurationen der einzelnen Dienste +#?────────────────────────────────────────────────────────────────────────────────────────── + +# [Abschnitt] +# Schlüssel = Wert +# ; Kommentar +# [Section] +# Key = Value +# ; Comment + + +function database_set_iniconfig() { + # 30.04.2025 + local ini_file="${SCRIPT_DIR}/UserInfo.ini" + local section="DatabaseService" + + # Zugangsdaten auslesen + local db_user db_pass + # Zeilenumbrüche entfernen + db_user=$(grep -oP '^DB_Benutzername\s*=\s*\K\S+' "$ini_file" | tr -d '\r\n') + db_pass=$(grep -oP '^DB_Passwort\s*=\s*\K\S+' "$ini_file" | tr -d '\r\n') + + + if [[ -z "$db_user" || -z "$db_pass" ]]; then + log "${COLOR_BAD}DB_Benutzername oder DB_Passwort fehlt in UserInfo.ini${COLOR_RESET}" + return 1 + fi + + # === Robust-Datenbanken === + local db_database="robust" + local robust_conn="Data Source=localhost;Database=${db_database};User ID=${db_user};Password=${db_pass};Old Guids=true;SslMode=None;" + + # Robust.HG.ini + local robust_hg_ini="robust/bin/Robust.HG.ini" + if [[ -f "$robust_hg_ini" ]]; then + add_ini_key "$robust_hg_ini" "$section" "ConnectionString" "\"$robust_conn\"" + else + log "${COLOR_WARNING}Datei nicht gefunden: $robust_hg_ini${COLOR_RESET}" + fi + + # Robust.local.ini + local robust_ini="robust/bin/Robust.local.ini" + if [[ -f "$robust_ini" ]]; then + add_ini_key "$robust_ini" "$section" "ConnectionString" "\"$robust_conn\"" + else + log "${COLOR_WARNING}Datei nicht gefunden: $robust_ini${COLOR_RESET}" + fi + + # === simX-Datenbanken === + for ((i=1; i<=1000; i++)); do + local sim_ini="${SCRIPT_DIR}/sim${i}/bin/config-include/GridCommon.ini" + if [[ -f "$sim_ini" ]]; then + local sim_conn="Data Source=localhost;Database=sim${i};User ID=${db_user};Password=${db_pass};Old Guids=true;SslMode=None;" + add_ini_key "$sim_ini" "$section" "ConnectionString" "\"$sim_conn\"" + fi + done + + log "${COLOR_OK}Datenbanken erfolgreich konfiguriert${COLOR_RESET}" + blankline +} + + + +# Welcome-Region konfigurieren wenn sie noch nicht existiert. +function welcomeiniconfig() { + # 25.04.2025 + local ip="$1" + gridname="$2" + + # Gridname bereinigen + gridname=$(echo "$gridname" | sed -e 's/ä/ae/g' -e 's/ö/oe/g' -e 's/ü/ue/g' -e 's/[-&]/_/g' -e 's/ */_/g' -e 's/__\+/_/g') + + local welcome_ini="${SCRIPT_DIR}/sim1/bin/Regions/$gridname.ini" + + # Überprüfen ob die Welcome-Region bereits existiert + if [[ -f "$welcome_ini" ]]; then + log "${COLOR_INFO}Überspringe Erstellung der Welcome-Region: '$gridname.ini' existiert bereits${COLOR_RESET}" + # Vorsichtshalber Region in Robust-Konfigurationen eintragen. + set_ini_key "${SCRIPT_DIR}/robust/bin/Robust.HG.ini" "GridService" "Region_$gridname" "\"DefaultRegion, DefaultHGRegion, FallbackRegion\"" + set_ini_key "${SCRIPT_DIR}/robust/bin/Robust.local.ini" "GridService" "Region_$gridname" "\"DefaultRegion, FallbackRegion\"" + return + fi + + # region_uuid=$(uuidgen) + #region_uuid=$(command -v uuidgen >/dev/null && uuidgen || cat /proc/sys/kernel/random/uuid 2>/dev/null || echo "$RANDOM-$RANDOM-$RANDOM-$RANDOM") + region_uuid=$(command -v uuidgen >/dev/null 2>&1 && uuidgen 2>/dev/null || cat /proc/sys/kernel/random/uuid 2>/dev/null || echo "$RANDOM-$RANDOM-$RANDOM-$RANDOM") + + # Datei erstellen + #touch "$welcome_ini" + #touch "$welcome_ini" || echo "" > "$welcome_ini" + touch "$welcome_ini" 2>/dev/null || echo "" > "$welcome_ini" + + # 2. Sektion hinzufügen + add_ini_section "$welcome_ini" "$gridname" + + # 3. Alle Key-Value Paare setzen + set_ini_key "$welcome_ini" "$gridname" "RegionUUID" "$region_uuid" + set_ini_key "$welcome_ini" "$gridname" "Location" "3000,3000" + set_ini_key "$welcome_ini" "$gridname" "SizeX" "256" + set_ini_key "$welcome_ini" "$gridname" "SizeY" "256" + set_ini_key "$welcome_ini" "$gridname" "SizeZ" "256" + set_ini_key "$welcome_ini" "$gridname" "InternalPort" "9015" + set_ini_key "$welcome_ini" "$gridname" "ExternalHostName" "$system_ip" + set_ini_key "$welcome_ini" "$gridname" "MaxPrims" "15000" + set_ini_key "$welcome_ini" "$gridname" "MaxAgents" "40" + set_ini_key "$welcome_ini" "$gridname" "MaptileStaticUUID" "$region_uuid" + set_ini_key "$welcome_ini" "$gridname" "InternalAddress" "0.0.0.0" + set_ini_key "$welcome_ini" "$gridname" "AllowAlternatePorts" "False" + + set_ini_key "$welcome_ini" "$gridname" ";NonPhysicalPrimMax" "512" + set_ini_key "$welcome_ini" "$gridname" ";PhysicalPrimMax" "128" + set_ini_key "$welcome_ini" "$gridname" ";ClampPrimSize" "false" + set_ini_key "$welcome_ini" "$gridname" ";MaxPrimsPerUser" "-1" + set_ini_key "$welcome_ini" "$gridname" ";ScopeID" "$region_uuid" + set_ini_key "$welcome_ini" "$gridname" ";RegionType" "Mainland" + set_ini_key "$welcome_ini" "$gridname" ";RenderMinHeight" "-1" + set_ini_key "$welcome_ini" "$gridname" ";RenderMaxHeight" "100" + set_ini_key "$welcome_ini" "$gridname" ";MapImageModule" "Warp3DImageModule" + set_ini_key "$welcome_ini" "$gridname" ";TextureOnMapTile" "true" + set_ini_key "$welcome_ini" "$gridname" ";DrawPrimOnMapTile" "true" + set_ini_key "$welcome_ini" "$gridname" ";GenerateMaptiles" "true" + set_ini_key "$welcome_ini" "$gridname" ";MaptileRefresh" "0" + set_ini_key "$welcome_ini" "$gridname" ";MaptileStaticFile" "path/to/SomeFile.png" + set_ini_key "$welcome_ini" "$gridname" ";MasterAvatarFirstName" "John" + set_ini_key "$welcome_ini" "$gridname" ";MasterAvatarLastName" "Doe" + set_ini_key "$welcome_ini" "$gridname" ";MasterAvatarSandboxPassword" "passwd" + + # 4. Region in Robust-Konfigurationen eintragen + # DefaultRegion, DefaultHGRegion, FallbackRegion, NoDirectLogin, Persistent, LockedOut, Reservation, NoMove, Authenticate + set_ini_key "${SCRIPT_DIR}/robust/bin/Robust.HG.ini" "GridService" "Region_$gridname" "\"DefaultRegion, DefaultHGRegion, FallbackRegion\"" + set_ini_key "${SCRIPT_DIR}/robust/bin/Robust.local.ini" "GridService" "Region_$gridname" "\"DefaultRegion, FallbackRegion\"" + + log "${COLOR_OK}Welcome_Area.ini Konfiguration abgeschlossen für $gridname${COLOR_RESET}" + blankline +} + +function moneyserveriniconfig() { + # 30.04.2025 + local ip="$1" + gridname="$2" + + # Gridname bereinigen + gridname=$(echo "$gridname" | sed -e 's/ä/ae/g' -e 's/ö/oe/g' -e 's/ü/ue/g' -e 's/[-&]/_/g' -e 's/ */_/g' -e 's/__\+/_/g') + + # Zugangsdaten aus UserInfo.ini laden + local ini_file="${SCRIPT_DIR}/UserInfo.ini" + local db_user db_pass + db_user=$(grep -oP '^DB_Benutzername\s*=\s*\K\S+' "$ini_file") + db_pass=$(grep -oP '^DB_Passwort\s*=\s*\K\S+' "$ini_file") + + # Falls keine Benutzer- und Passwortwerte gelesen wurden, abbrechen + if [[ -z "$db_user" || -z "$db_pass" ]]; then + log "${COLOR_BAD}Fehler: DB_Benutzername oder DB_Passwort fehlen in UserInfo.ini${COLOR_RESET}" + return 1 + fi + + local dir="$SCRIPT_DIR/robust/bin" + local file="$dir/MoneyServer.ini" + + # Sicherstellen, dass die Vorlage existiert + if [[ ! -f "$file.example" ]]; then + log "${COLOR_BAD}FEHLER: Konfigurationsvorlage '$file.example' fehlt!${COLOR_RESET}" >&2 + exit 1 + fi + + # Kopieren der Vorlage + cp "$file.example" "$file" || { + log "${COLOR_BAD}FEHLER: Konnte '$file' nicht erstellen${COLOR_RESET}" >&2 + exit 1 + } + + # [Startup] + set_ini_key "$file" "Startup" "PIDFile" "\"/tmp/MoneyServer.exe.pid\"" + + # [MySql] → Daten aus UserInfo.ini verwenden + set_ini_key "$file" "MySql" "hostname" "\"localhost\"" + set_ini_key "$file" "MySql" "database" "\"robust\"" + set_ini_key "$file" "MySql" "username" "\"$db_user\"" + set_ini_key "$file" "MySql" "password" "\"$db_pass\"" + set_ini_key "$file" "MySql" "pooling" "\"true\"" + set_ini_key "$file" "MySql" "port" "\"3306\"" + set_ini_key "$file" "MySql" "MaxConnection" "\"25\"" + + # [MoneyServer] + set_ini_key "$file" "MoneyServer" "ServerPort" "\"8008\"" + set_ini_key "$file" "MoneyServer" "CurrencyOnOff" "\"on\"" + set_ini_key "$file" "MoneyServer" "CurrencyMaximum" "\"20000\"" + set_ini_key "$file" "MoneyServer" "MoneyServerIPaddress" "\"http://$ip:8008\"" + set_ini_key "$file" "MoneyServer" "DefaultBalance" "\"1000\"" + + # [Certificate] + set_ini_key "$file" "Certificate" "CheckClientCert" "\"false\"" + set_ini_key "$file" "Certificate" "CheckServerCert" "\"false\"" + + log "${COLOR_OK}MoneyServer.ini Konfiguration abgeschlossen${COLOR_RESET}" + blankline +} + + +# Konfiguriert OpenSim.ini für alle sim1 bis sim99-Verzeichnisse +function opensiminiconfig() { + # 24.04.2025 + local ip="$1" + local gridname="$2" + local base_port=9010 + local sim_counter=1 + + # Gridname bereinigen + gridname=$(echo "$gridname" | sed -e 's/ä/ae/g' -e 's/ö/oe/g' -e 's/ü/ue/g' -e 's/[-&]/_/g' -e 's/ */_/g' -e 's/__\+/_/g') + + # Iteration über alle sim1 bis sim99 Verzeichnisse + for i in $(seq 1 99); do + sim_dir="$SCRIPT_DIR/sim$i" + if [[ -d "$sim_dir/bin" ]]; then + local dir="$sim_dir/bin" + local file="$dir/OpenSim.ini" + + if [[ ! -f "$file.example" ]]; then + echo "FEHLER: Konfigurationsvorlage '$file.example' fehlt!" >&2 + echo "Das Programm benötigt diese Datei zum Starten." >&2 + exit 1 # Programm mit Fehlercode beenden + fi + + # Nur wenn Vorlage existiert: Kopieren + cp "$file.example" "$file" || { + echo "FEHLER: Konnte '$file' nicht erstellen" >&2 + exit 1 + } + + sleep 1 + + log "${COLOR_INFO}Konfiguriere OpenSim.ini in $dir${COLOR_RESET}" + + # Portberechnung pro Instanz + local public_port=$((base_port + (sim_counter - 1) * 100)) + + # Werte setzen + # [Const] + set_ini_key "$file" "Const" "BaseHostname" "$ip" + set_ini_key "$file" "Const" "PublicPort" "8002" + + # [Startup] + #uncomment_ini_section_line "$file" "Startup" "PIDFile" + set_ini_key "$file" "Startup" "PIDFile" "\"/tmp/OpenSim.$i.exe.pid\"" + + # [Estates] + #set_ini_key "$file" "Estates" "DefaultEstateName" "$gridname Estate" + #set_ini_key "$file" "Estates" "DefaultEstateOwnerName" "$VORNAME $NACHNAME" + + # [Network] + # http_listener_port = 9010 + uncomment_ini_section_line "$file" "Network" "http_listener_port" + set_ini_key "$file" "Network" "http_listener_port" "$public_port" + + # [SimulatorFeatures] + uncomment_ini_line "$file" "SearchServerURI" + + # [ClientStack.LindenUDP] Begrenzt die Viewer damit sie die Server leistung nicht kippen können. + set_ini_key "$file" "ClientStack.LindenUDP" "DisableFacelights" "true" + # 10.000.000 bps = 10 Mbit/s pro Client gute Einstellung ist "350.000" bis "500.000" + set_ini_key "$file" "ClientStack.LindenUDP" "client_throttle_max_bps" "350000" + # 10.000.000 bps = 10 Mbit/s pro Region "70.000.000" gute Einstellung ist "70.000.000" bis "1.000.000.000" + set_ini_key "$file" "ClientStack.LindenUDP" "scene_throttle_max_bps" "70000000" + + # [SimulatorFeatures] + set_ini_key "$file" "SimulatorFeatures" "SearchServerURI" "\${Const|BaseURL}:\${Const|PublicPort}" + set_ini_key "$file" "SimulatorFeatures" "DestinationGuideURI" "\${Const|BaseURL}:\${Const|PublicPort}" + + # [Messaging] + set_ini_key "$file" "Messaging" "OfflineMessageModule" "Offline Message Module V2" + set_ini_key "$file" "Messaging" "OfflineMessageURL" "\${Const|PrivURL}:\${Const|PrivatePort}" + set_ini_key "$file" "Messaging" "StorageProvider" "OpenSim.Data.MySQL.dll" + set_ini_key "$file" "Messaging" "MuteListModule" "MuteListModule" + set_ini_key "$file" "Messaging" "ForwardOfflineGroupMessages" "true" + + # [Materials] + set_ini_key "$file" "Materials" "MaxMaterialsPerTransaction" "250" + + # [DataSnapshot] + set_ini_key "$file" "DataSnapshot" "gridname" "$gridname" + + # [Economy] + set_ini_key "$file" "Economy" "SellEnabled" "true" + set_ini_key "$file" "Economy" "EconomyModule" "DTLNSLMoneyModule" + set_ini_key "$file" "Economy" "CurrencyServer" "\${Const|BaseURL}:8008/" + set_ini_key "$file" "Economy" "UserServer" "\${Const|BaseURL}:8002/" + set_ini_key "$file" "Economy" "CheckServerCert" "false" + set_ini_key "$file" "Economy" "PriceUpload" "0" + set_ini_key "$file" "Economy" "MeshModelUploadCostFactor" "1.0" + set_ini_key "$file" "Economy" "MeshModelUploadTextureCostFactor" "1.0" + set_ini_key "$file" "Economy" "MeshModelMinCostFactor" "1.0" + set_ini_key "$file" "Economy" "CheckServerCert" "false" + set_ini_key "$file" "Economy" "PriceUpload" "0" + set_ini_key "$file" "Economy" "MeshModelUploadCostFactor" "1.0" + set_ini_key "$file" "Economy" "MeshModelUploadTextureCostFactor" "1.0" + set_ini_key "$file" "Economy" "MeshModelMinCostFactor" "1.0" + set_ini_key "$file" "Economy" "PriceGroupCreate" "0" + set_ini_key "$file" "Economy" "ObjectCount" "0" + set_ini_key "$file" "Economy" "PriceEnergyUnit" "0" + set_ini_key "$file" "Economy" "PriceObjectClaim" "0" + set_ini_key "$file" "Economy" "PricePublicObjectDecay" "0" + set_ini_key "$file" "Economy" "PricePublicObjectDelete" "0" + set_ini_key "$file" "Economy" "PriceParcelClaim" "0" + set_ini_key "$file" "Economy" "PriceParcelClaimFactor" "1" + set_ini_key "$file" "Economy" "PriceRentLight" "0" + set_ini_key "$file" "Economy" "TeleportMinPrice" "0" + set_ini_key "$file" "Economy" "TeleportPriceExponent" "2" + set_ini_key "$file" "Economy" "EnergyEfficiency" "1" + set_ini_key "$file" "Economy" "PriceObjectRent" "0" + set_ini_key "$file" "Economy" "PriceObjectScaleFactor" "10" + set_ini_key "$file" "Economy" "PriceParcelRent" "0" + + # [Groups] + set_ini_key "$file" "Groups" "Enabled" "true" + set_ini_key "$file" "Groups" "LevelGroupCreate" "0" + set_ini_key "$file" "Groups" "Module" "\"Groups Module V2\"" + set_ini_key "$file" "Groups" "StorageProvider" "OpenSim.Data.MySQL.dll" + set_ini_key "$file" "Groups" "ServicesConnectorModule" "\"Groups HG Service Connector\"" + set_ini_key "$file" "Groups" "LocalService" "remote" + set_ini_key "$file" "Groups" "GroupsServerURI" "\${Const|BaseURL}:\${Const|PrivatePort}" + set_ini_key "$file" "Groups" "HomeURI" "\"\${Const|BaseURL}:\${Const|PublicPort}\"" + set_ini_key "$file" "Groups" "MessagingEnabled" "true" + set_ini_key "$file" "Groups" "MessagingModule" "\"Groups Messaging Module V2\"" + set_ini_key "$file" "Groups" "NoticesEnabled" "true" + set_ini_key "$file" "Groups" "MessageOnlineUsersOnly" "true" + set_ini_key "$file" "Groups" "XmlRpcServiceReadKey" "1234" + set_ini_key "$file" "Groups" "XmlRpcServiceWriteKey" "1234" + + # [InterestManagement] + set_ini_key "$file" "InterestManagement" "UpdatePrioritizationScheme" "BestAvatarResponsiveness" + set_ini_key "$file" "InterestManagement" "ObjectsCullingByDistance" "true" + + # [MediaOnAPrim] + set_ini_key "$file" "MediaOnAPrim" "Enabled" "true" + + # [NPC] + set_ini_key "$file" "NPC" "Enabled" "true" + set_ini_key "$file" "NPC" "MaxNumberNPCsPerScene" "40" + set_ini_key "$file" "NPC" "AllowNotOwned" "true" + set_ini_key "$file" "NPC" "AllowSenseAsAvatar" "true" + set_ini_key "$file" "NPC" "AllowCloneOtherAvatars" "true" + set_ini_key "$file" "NPC" "NoNPCGroup" "true" + + # [Terrain] + set_ini_key "$file" "Terrain" "InitialTerrain" "\"flat\"" + + # [LandManagement] + set_ini_key "$file" "LandManagement" "ShowParcelBansLines" "true" + + # [UserProfiles] + set_ini_key "$file" "UserProfiles" "ProfileServiceURL" "\${Const|BaseURL}:\${Const|PublicPort}" + set_ini_key "$file" "UserProfiles" "AllowUserProfileWebURLs" "true" + + # [XBakes] + set_ini_key "$file" "XBakes" "URL" "\${Const|PrivURL}:\${Const|PrivatePort}" + + # [Architecture] + set_ini_key "$file" "Architecture" "Include-Architecture" "\"config-include/GridHypergrid.ini\"" + + ((sim_counter++)) + sleep 1 + fi + done + + log "${COLOR_OK}${sim_counter} Simulationen konfiguriert.${COLOR_RESET}" + blankline +} + +# Konfiguration von Robust.HG.ini im robust/bin Verzeichnis +function robusthginiconfig() { + # 23.04.2025 + local ip="$1" + local gridname="$2" + local dir="$SCRIPT_DIR/robust/bin" + local file="$dir/Robust.HG.ini" + webverzeichnis="oswebinterface" + + # Gridname bereinigen + gridname=$(echo "$gridname" | sed -e 's/ä/ae/g' -e 's/ö/oe/g' -e 's/ü/ue/g' -e 's/[-&]/_/g' -e 's/ */_/g' -e 's/__\+/_/g') + + if [[ ! -f "$file.example" ]]; then + echo "FEHLER: Konfigurationsvorlage '$file.example' fehlt!" >&2 + echo "Das Programm benötigt diese Datei zum Starten." >&2 + exit 1 # Programm mit Fehlercode beenden + fi + + # Nur wenn Vorlage existiert: Kopieren + cp "$file.example" "$file" || { + echo "FEHLER: Konnte '$file' nicht erstellen" >&2 + exit 1 + } + + # [Const] + set_ini_key "$file" "Const" "BaseHostname" "$ip" + + # [Startup] + # PID-Dateien gehören grundsätzlich in /tmp warum: tmp Wird beim Neustart automatisch geleert. Keine Berechtigungsprobleme (normalerweise schreibbar für alle). + # Standard-PID-Datei: /tmp/Robust.exe.pid + uncomment_ini_line "$file" "PIDFile" + + # [ServiceList] + uncomment_ini_line "$file" "OfflineIMServiceConnector" + uncomment_ini_line "$file" "GroupsServiceConnector" + uncomment_ini_line "$file" "BakedTextureService" + uncomment_ini_line "$file" "UserProfilesServiceConnector" + uncomment_ini_line "$file" "HGGroupsServiceConnector" + + # [Network] + + # [Hypergrid] + uncomment_ini_line "$file" "HomeURI" + uncomment_ini_line "$file" "GatekeeperURI" + + # [DatabaseService] wird in database_set_iniconfig eingetragen. + + # [GridService] für osWebinterface + uncomment_ini_line "$file" "MapTileDirectory" + + # [LoginService] für osWebinterface etc. "\${Const|BaseURL}:\${Const|PublicPort}" + uncomment_ini_line "$file" "SearchURL" + uncomment_ini_line "$file" "DestinationGuide" + uncomment_ini_line "$file" "AvatarPicker" + uncomment_ini_line "$file" "MinLoginLevel" + uncomment_ini_line "$file" "Currency" + uncomment_ini_line "$file" "ClassifiedFee" + + set_ini_key "$file" "LoginService" "WelcomeMessage" "\"Willkommen im $gridname!\"" + set_ini_key "$file" "LoginService" "MapTileURL" "\"\${Const|BaseURL}:\${Const|PublicPort}/\"" + set_ini_key "$file" "LoginService" "SearchURL" "\"\${Const|BaseURL}:\${Const|PublicPort}/$webverzeichnis/searchservice.php\"" + set_ini_key "$file" "LoginService" "DestinationGuide" "\"\${Const|BaseURL}/$webverzeichnis/guide.php\"" + set_ini_key "$file" "LoginService" "AvatarPicker" "\"\${Const|BaseURL}/$webverzeichnis/avatarpicker.php\"" + set_ini_key "$file" "LoginService" "Currency" "\"OS$\"" + set_ini_key "$file" "LoginService" "DSTZone" "\"local\"" + + # [GatekeeperService] + uncomment_ini_line "$file" "ExternalName" + + # [GridInfoService] + set_ini_key "$file" "GridInfoService" "gridname" "\"$gridname\"" + set_ini_key "$file" "GridInfoService" "gridnick" "\"$gridname\"" + set_ini_key "$file" "GridInfoService" "welcome" "\"\${Const|BaseURL}/$webverzeichnis/welcomesplashpage.php\"" + set_ini_key "$file" "GridInfoService" "economy" "\"\${Const|BaseURL}:8008/\"" + set_ini_key "$file" "GridInfoService" "about" "\"\${Const|BaseURL}/$webverzeichnis/aboutinformation.php\"" + set_ini_key "$file" "GridInfoService" "register" "\"\${Const|BaseURL}/$webverzeichnis/createavatar.php\"" + set_ini_key "$file" "GridInfoService" "help" "\"\${Const|BaseURL}/$webverzeichnis/help.php\"" + set_ini_key "$file" "GridInfoService" "password" "\"\${Const|BaseURL}/$webverzeichnis/passwordreset.php\"" + set_ini_key "$file" "GridInfoService" "GridStatusRSS" "\"\${Const|BaseURL}/$webverzeichnis/gridstatusrss.php\"" + + # [UserAgentService] + uncomment_ini_line "$file" "LevelOutsideContacts" + uncomment_ini_line "$file" "ShowUserDetailsInHGProfile" + + # [Messaging] + set_ini_key "$file" "Messaging" "MaxOfflineIMs" "250" + + # [Groups] + set_ini_key "$file" "Groups" "MaxAgentGroups" "100" + + log "${COLOR_OK}Robust.HG.ini konfiguriert.${COLOR_RESET}" + blankline +} + +# Funktion zur Generierung eines 16-stelligen Passworts +function genPass() { + tr -dc 'A-Za-z0-9' "$php_config_file" < +EOF + + # config.example.php in config.php umbenennen, falls es existiert + if [[ -f "$config_example_file" ]]; then + #mv "$config_example_file" "$config_file" + cp "$config_example_file" "$config_file" + log "${SYM_OK} ${COLOR_ACTION}Konfigurationsdatei umbenannt: ${COLOR_DIR}$config_file${COLOR_RESET}" + + # Konfiguration in config.php einfügen + if [[ -f "$config_file" ]]; then + # BASE_URL ersetzen oder hinzufügen + if grep -q "define('BASE_URL'" "$config_file"; then + sed -i "s|define('BASE_URL'.*|define('BASE_URL', 'http://${server_ip}');|" "$config_file" + else + sed -i "/&2 + log "${COLOR_BAD}Das Programm benötigt diese Datei zum Starten.${COLOR_RESET}" >&2 + exit 1 + fi + + # Vorlage nach Robust.local.ini kopieren + cp "$source_file" "$target_file" || { + log "${COLOR_BAD}FEHLER: Konnte '$target_file' nicht erstellen${COLOR_RESET}" >&2 + exit 1 + } + + # [Const] + set_ini_key "$target_file" "Const" "BaseHostname" "$ip" + + # [ServiceList] + uncomment_ini_line "$target_file" "OfflineIMServiceConnector" + uncomment_ini_line "$target_file" "GroupsServiceConnector" + uncomment_ini_line "$target_file" "BakedTextureService" + uncomment_ini_line "$target_file" "UserProfilesServiceConnector" + + # [LoginService] Konfiguration + set_ini_key "$target_file" "LoginService" "Currency" "\"OS$\"" + set_ini_key "$target_file" "LoginService" "WelcomeMessage" "\"Willkommen im $gridname!\"" + set_ini_key "$target_file" "LoginService" "MapTileURL" "\"\${Const|BaseURL}:\${Const|PublicPort}/\"" + set_ini_key "$target_file" "LoginService" "SearchURL" "\"\${Const|BaseURL}/searchservice.php\"" + set_ini_key "$target_file" "LoginService" "DestinationGuide" "\"\${Const|BaseURL}/guide.php\"" + set_ini_key "$target_file" "LoginService" "AvatarPicker" "\"\${Const|BaseURL}/avatarpicker.php\"" + + # [GridInfoService] + set_ini_key "$target_file" "GridInfoService" "gridname" "\"$gridname\"" + set_ini_key "$target_file" "GridInfoService" "gridnick" "\"$gridname\"" + set_ini_key "$target_file" "GridInfoService" "welcome" "\"\${Const|BaseURL}/welcome\"" + set_ini_key "$target_file" "GridInfoService" "economy" "\"\${Const|BaseURL}:8008/\"" + set_ini_key "$target_file" "GridInfoService" "about" "\"\${Const|BaseURL}/about\"" + set_ini_key "$target_file" "GridInfoService" "register" "\"\${Const|BaseURL}/register\"" + set_ini_key "$target_file" "GridInfoService" "help" "\"\${Const|BaseURL}/help\"" + set_ini_key "$target_file" "GridInfoService" "password" "\"\${Const|BaseURL}/password\"" + set_ini_key "$target_file" "GridInfoService" "GridStatusRSS" "\"\${Const|BaseURL}:\${Const|PublicPort}/GridStatusRSS\"" + + log "${COLOR_OK}Robust.local.ini erfolgreich konfiguriert.${COLOR_RESET}" + blankline +} + +# Konfiguriert die FlotsamCache.ini Dateien +# - Erstellt die Dateien in den bin/config-include Ordner der Simulatoren +# - Setzt Optimierte Parameter den FlotsamCache.ini Dateien +function flotsaminiconfig() { + # 25.04.2025 + for i in $(seq 1 99); do + local config_dir="$SCRIPT_DIR/sim$i/bin/config-include" + local file="$config_dir/FlotsamCache.ini" + + if [[ -d "$config_dir" ]]; then + log "${COLOR_FILE}Erstelle $file${COLOR_RESET}" + mkdir -p "$config_dir" + + # Datei erstellen + #touch "$welcome_ini" + #touch "$welcome_ini" || echo "" > "$welcome_ini" + touch "$file" 2>/dev/null || echo "" > "$file" + + # 2. Sektion hinzufügen + add_ini_section "$file" "AssetCache" + + sleep 1 + + # 3. Alle Key-Value Paare setzen + set_ini_key "$file" "AssetCache" "CacheDirectory" "./assetcache" + set_ini_key "$file" "AssetCache" "LogLevel" "0" + set_ini_key "$file" "AssetCache" "HitRateDisplay" "100" + set_ini_key "$file" "AssetCache" "MemoryCacheEnabled" "false" + set_ini_key "$file" "AssetCache" "UpdateFileTimeOnCacheHit" "false" + set_ini_key "$file" "AssetCache" "NegativeCacheEnabled" "true" + set_ini_key "$file" "AssetCache" "NegativeCacheTimeout" "120" + set_ini_key "$file" "AssetCache" "NegativeCacheSliding" "false" + set_ini_key "$file" "AssetCache" "FileCacheEnabled" "true" + set_ini_key "$file" "AssetCache" "MemoryCacheTimeout" ".016" + set_ini_key "$file" "AssetCache" "FileCacheTimeout" "48" + set_ini_key "$file" "AssetCache" "FileCleanupTimer" "1.0" + + log "${COLOR_OK}→ FlotsamCache.ini neu geschrieben für sim$i${COLOR_RESET}" + fi + done + + log "${COLOR_OK}FlotsamCache.ini konfiguriert.${COLOR_RESET}" + blankline +} + +function gridcommoniniconfig() { + local ip="$1" + local gridname="$2" + + # Gridname bereinigen + gridname=$(echo "$gridname" | sed -e 's/ä/ae/g' -e 's/ö/oe/g' -e 's/ü/ue/g' -e 's/[-&]/_/g' -e 's/ */_/g' -e 's/__\+/_/g') + + for i in $(seq 1 99); do + local config_dir="$SCRIPT_DIR/sim$i/bin/config-include" + local file="$config_dir/GridCommon.ini" + local example_file="$config_dir/GridCommon.ini.example" + + # Strikte Prüfung - Abbruch wenn etwas fehlt + if [[ ! -d "$config_dir" ]] || [[ ! -f "$example_file" ]]; then + return 1 + fi + + # Vorlage kopieren + cp "$example_file" "$file" || return 1 + + sleep 1 + + # [Const] + add_ini_before_section "$file" "Const" "DatabaseService" + set_ini_key "$file" "Const" "BaseHostname" "\"$ip\"" + set_ini_key "$file" "Const" "BaseURL" "\"http://\${Const|BaseHostname}\"" + set_ini_key "$file" "Const" "PublicPort" "\"8002\"" + set_ini_key "$file" "Const" "PrivatePort" "\"8003\"" + + # [DatabaseService] wird in database_set_iniconfig eingetragen. + clear_ini_section "$file" "DatabaseService" + set_ini_key "$file" "DatabaseService" "StorageProvider" "\"OpenSim.Data.MySQL.dll\"" + + # [Hypergrid] + set_ini_key "$file" "Hypergrid" "GatekeeperURI" "\"\${Const|BaseURL}:\${Const|PublicPort}\"" + set_ini_key "$file" "Hypergrid" "HomeURI" "\"\${Const|BaseURL}:\${Const|PublicPort}\"" + + # [Modules] + set_ini_key "$file" "Modules" "AssetCaching" "\"FlotsamAssetCache\"" + set_ini_key "$file" "Modules" "Include-FlotsamCache" "\"config-include/FlotsamCache.ini\"" + set_ini_key "$file" "Modules" "AuthorizationServices" "\"RemoteAuthorizationServicesConnector\"" + + # [AssetService] + set_ini_key "$file" "AssetService" "DefaultAssetLoader" "\"OpenSim.Framework.AssetLoader.Filesystem.dll\"" + set_ini_key "$file" "AssetService" "AssetLoaderArgs" "\"assets/AssetSets.xml\"" + set_ini_key "$file" "AssetService" "AssetServerURI" "\"\${Const|PrivURL}:\${Const|PrivatePort}\"" + + # [InventoryService] + set_ini_key "$file" "InventoryService" "InventoryServerURI" "\"\${Const|PrivURL}:\${Const|PrivatePort}\"" + + # [GridInfo] + set_ini_key "$file" "GridInfo" "GridInfoURI" "\"\${Const|BaseURL}:\${Const|PublicPort}\"" + + # [GridService] + set_ini_key "$file" "GridService" "GridServerURI" "\"\${Const|PrivURL}:\${Const|PrivatePort}\"" + set_ini_key "$file" "GridService" "MapTileDirectory" "\"./maptiles\"" + set_ini_key "$file" "GridService" "Gatekeeper" "\"\${Const|BaseURL}:\${Const|PublicPort}\"" + + # [EstateDataStore] + set_ini_key "$file" "EstateService" "EstateServerURI" "\"\${Const|PrivURL}:\${Const|PrivatePort}\"" + + # [Messaging] + set_ini_key "$file" "Messaging" "Gatekeeper" "\"\${Const|BaseURL}:\${Const|PublicPort}\"" + + # [AvatarService] + set_ini_key "$file" "AvatarService" "AvatarServerURI" "\"\${Const|PrivURL}:\${Const|PrivatePort}\"" + + # [AgentPreferencesService] + set_ini_key "$file" "AgentPreferencesService" "AgentPreferencesServerURI" "\"\${Const|PrivURL}:\${Const|PrivatePort}\"" + + # [PresenceService] + set_ini_key "$file" "PresenceService" "PresenceServerURI" "\"\${Const|PrivURL}:\${Const|PrivatePort}\"" + + # [UserAccountService] + set_ini_key "$file" "UserAccountService" "UserAccountServerURI" "\"\${Const|PrivURL}:\${Const|PrivatePort}\"" + + # [GridUserService] + set_ini_key "$file" "GridUserService" "GridUserServerURI" "\"\${Const|PrivURL}:\${Const|PrivatePort}\"" + + # [AuthenticationService] + set_ini_key "$file" "AuthenticationService" "AuthenticationServerURI" "\"\${Const|PrivURL}:\${Const|PrivatePort}\"" + + # [FriendsService] + set_ini_key "$file" "FriendsService" "FriendsServerURI" "\"\${Const|PrivURL}:\${Const|PrivatePort}\"" + + # [HGInventoryAccessModule] + set_ini_key "$file" "HGInventoryAccessModule" "HomeURI" "\"\${Const|BaseURL}:\${Const|PublicPort}\"" + set_ini_key "$file" "HGInventoryAccessModule" "Gatekeeper" "\"\${Const|BaseURL}:\${Const|PublicPort}\"" + + # [HGAssetService] + set_ini_key "$file" "HGAssetService" "HomeURI" "\"\${Const|BaseURL}:\${Const|PublicPort}\"" + + # [HGFriendsModule] + set_ini_key "$file" "HGFriendsModule" "LevelHGFriends" "0" + + # [MapImageService] + set_ini_key "$file" "MapImageService" "MapImageServerURI" "\"\${Const|PrivURL}:\${Const|PrivatePort}\"" + + # [AuthorizationService] + set_ini_key "$file" "AuthorizationService" "AuthorizationServerURI" "\"\${Const|PrivURL}/hgauth.php\"" + + # [MuteListService] + set_ini_key "$file" "MuteListService" "MuteListServerURI" "\"\${Const|PrivURL}:\${Const|PrivatePort}\"" + + log "${COLOR_OK}✓ sim$i: GridCommon.ini erfolgreich${COLOR_RESET}" + + sleep 1 + done + + log "${COLOR_OK}GridCommon.ini konfiguriert.${COLOR_RESET}" + blankline +} + +function osslenableiniconfig() { + # 25.04.2025 + for i in $(seq 1 99); do + local config_dir="$SCRIPT_DIR/sim$i/bin/config-include" + local file="$config_dir/osslEnable.ini" + + if [[ -d "$config_dir" ]]; then + log "${COLOR_FILE}Erstelle $file${COLOR_RESET}" + mkdir -p "$config_dir" + + touch "$file" 2>/dev/null || echo "" > "$file" + + # Sektion hinzufügen + add_ini_section "$file" "OSSL" + + sleep 1 + + # Basis-Key-Value Paare + set_ini_key "$file" "OSSL" "AllowOSFunctions" "true" + set_ini_key "$file" "OSSL" "AllowMODFunctions" "true" + set_ini_key "$file" "OSSL" "AllowLightShareFunctions" "true" + set_ini_key "$file" "OSSL" "PermissionErrorToOwner" "false" + set_ini_key "$file" "OSSL" "OSFunctionThreatLevel" "VeryHigh" + set_ini_key "$file" "OSSL" "osslParcelO" "\"PARCEL_OWNER,\"" + set_ini_key "$file" "OSSL" "osslParcelOG" "\"PARCEL_GROUP_MEMBER,PARCEL_OWNER,\"" + set_ini_key "$file" "OSSL" "osslNPC" "\${OSSL|osslParcelOG}ESTATE_MANAGER,ESTATE_OWNER" + + # Alle Allow_* Einträge + set_ini_key "$file" "OSSL" "Allow_osGetAgents" "true" + set_ini_key "$file" "OSSL" "Allow_osGetAvatarList" "true" + set_ini_key "$file" "OSSL" "Allow_osGetNPCList" "true" + set_ini_key "$file" "OSSL" "Allow_osNpcGetOwner" "\${OSSL|osslNPC}" + set_ini_key "$file" "OSSL" "Allow_osSetSunParam" "ESTATE_MANAGER,ESTATE_OWNER" + set_ini_key "$file" "OSSL" "Allow_osTeleportOwner" "true" + set_ini_key "$file" "OSSL" "Allow_osSetEstateSunSettings" "ESTATE_MANAGER,ESTATE_OWNER" + set_ini_key "$file" "OSSL" "Allow_osSetRegionSunSettings" "ESTATE_MANAGER,ESTATE_OWNER" + set_ini_key "$file" "OSSL" "Allow_osEjectFromGroup" "true" + set_ini_key "$file" "OSSL" "Allow_osForceBreakAllLinks" "true" + set_ini_key "$file" "OSSL" "Allow_osForceBreakLink" "true" + set_ini_key "$file" "OSSL" "Allow_osGetWindParam" "true" + set_ini_key "$file" "OSSL" "Allow_osInviteToGroup" "true" + set_ini_key "$file" "OSSL" "Allow_osReplaceString" "true" + set_ini_key "$file" "OSSL" "Allow_osSetDynamicTextureData" "true" + set_ini_key "$file" "OSSL" "Allow_osSetDynamicTextureDataFace" "true" + set_ini_key "$file" "OSSL" "Allow_osSetDynamicTextureDataBlend" "true" + set_ini_key "$file" "OSSL" "Allow_osSetDynamicTextureDataBlendFace" "true" + set_ini_key "$file" "OSSL" "Allow_osSetParcelMediaURL" "true" + set_ini_key "$file" "OSSL" "Allow_osSetParcelMusicURL" "true" + set_ini_key "$file" "OSSL" "Allow_osSetParcelSIPAddress" "true" + set_ini_key "$file" "OSSL" "Allow_osSetPrimFloatOnWater" "true" + set_ini_key "$file" "OSSL" "Allow_osSetWindParam" "true" + set_ini_key "$file" "OSSL" "Allow_osTerrainFlush" "ESTATE_MANAGER,ESTATE_OWNER" + set_ini_key "$file" "OSSL" "Allow_osAvatarName2Key" "\${OSSL|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER" + set_ini_key "$file" "OSSL" "Allow_osFormatString" "true" + set_ini_key "$file" "OSSL" "Allow_osKey2Name" "\${OSSL|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER" + set_ini_key "$file" "OSSL" "Allow_osListenRegex" "true" + set_ini_key "$file" "OSSL" "Allow_osLoadedCreationDate" "true" + set_ini_key "$file" "OSSL" "Allow_osLoadedCreationID" "true" + set_ini_key "$file" "OSSL" "Allow_osLoadedCreationTime" "true" + set_ini_key "$file" "OSSL" "Allow_osMessageObject" "true" + set_ini_key "$file" "OSSL" "Allow_osRegexIsMatch" "true" + set_ini_key "$file" "OSSL" "Allow_osGetAvatarHomeURI" "true" + set_ini_key "$file" "OSSL" "Allow_osNpcSetProfileAbout" "\${OSSL|osslNPC}" + set_ini_key "$file" "OSSL" "Allow_osNpcSetProfileImage" "\${OSSL|osslNPC}" + set_ini_key "$file" "OSSL" "Allow_osDie" "true" + set_ini_key "$file" "OSSL" "Allow_osDetectedCountry" "\${OSSL|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER" + set_ini_key "$file" "OSSL" "Allow_osDropAttachment" "\${OSSL|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER" + set_ini_key "$file" "OSSL" "Allow_osDropAttachmentAt" "\${OSSL|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER" + set_ini_key "$file" "OSSL" "Allow_osGetAgentCountry" "\${OSSL|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER" + set_ini_key "$file" "OSSL" "Allow_osGetGridCustom" "\${OSSL|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER" + set_ini_key "$file" "OSSL" "Allow_osGetGridGatekeeperURI" "\${OSSL|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER" + set_ini_key "$file" "OSSL" "Allow_osGetGridHomeURI" "\${OSSL|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER" + set_ini_key "$file" "OSSL" "Allow_osGetGridLoginURI" "\${OSSL|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER" + set_ini_key "$file" "OSSL" "Allow_osGetGridName" "true" + set_ini_key "$file" "OSSL" "Allow_osGetGridNick" "true" + set_ini_key "$file" "OSSL" "Allow_osGetNumberOfAttachments" "\${OSSL|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER" + set_ini_key "$file" "OSSL" "Allow_osGetRegionStats" "\${OSSL|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER" + set_ini_key "$file" "OSSL" "Allow_osGetSimulatorMemory" "\${OSSL|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER" + set_ini_key "$file" "OSSL" "Allow_osGetSimulatorMemoryKB" "\${OSSL|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER" + set_ini_key "$file" "OSSL" "Allow_osMessageAttachments" "\${OSSL|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER" + set_ini_key "$file" "OSSL" "Allow_osReplaceAgentEnvironment" "\${OSSL|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER" + set_ini_key "$file" "OSSL" "Allow_osSetSpeed" "\${OSSL|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER" + set_ini_key "$file" "OSSL" "Allow_osSetOwnerSpeed" "\${OSSL|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER" + set_ini_key "$file" "OSSL" "Allow_osRequestURL" "\${OSSL|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER" + set_ini_key "$file" "OSSL" "Allow_osRequestSecureURL" "\${OSSL|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER" + set_ini_key "$file" "OSSL" "Allow_osCauseDamage" "\${OSSL|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER" + set_ini_key "$file" "OSSL" "Allow_osCauseHealing" "\${OSSL|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER" + set_ini_key "$file" "OSSL" "Allow_osSetHealth" "\${OSSL|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER" + set_ini_key "$file" "OSSL" "Allow_osSetHealRate" "\${OSSL|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER" + set_ini_key "$file" "OSSL" "Allow_osForceAttachToAvatar" "\${OSSL|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER" + set_ini_key "$file" "OSSL" "Allow_osForceAttachToAvatarFromInventory" "\${OSSL|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER" + set_ini_key "$file" "OSSL" "Allow_osForceCreateLink" "\${OSSL|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER" + set_ini_key "$file" "OSSL" "Allow_osForceDropAttachment" "\${OSSL|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER" + set_ini_key "$file" "OSSL" "Allow_osForceDropAttachmentAt" "\${OSSL|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER" + set_ini_key "$file" "OSSL" "Allow_osGetLinkPrimitiveParams" "\${OSSL|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER" + set_ini_key "$file" "OSSL" "Allow_osGetPhysicsEngineType" "true" + set_ini_key "$file" "OSSL" "Allow_osGetRegionMapTexture" "\${OSSL|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER" + set_ini_key "$file" "OSSL" "Allow_osGetScriptEngineName" "true" + set_ini_key "$file" "OSSL" "Allow_osGetSimulatorVersion" "true" + set_ini_key "$file" "OSSL" "Allow_osMakeNotecard" "\${OSSL|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER" + set_ini_key "$file" "OSSL" "Allow_osMatchString" "true" + set_ini_key "$file" "OSSL" "Allow_osNpcCreate" "\${OSSL|osslNPC}" + set_ini_key "$file" "OSSL" "Allow_osNpcGetPos" "\${OSSL|osslNPC}" + set_ini_key "$file" "OSSL" "Allow_osNpcGetRot" "\${OSSL|osslNPC}" + set_ini_key "$file" "OSSL" "Allow_osNpcLoadAppearance" "\${OSSL|osslNPC}" + set_ini_key "$file" "OSSL" "Allow_osNpcMoveTo" "\${OSSL|osslNPC}" + set_ini_key "$file" "OSSL" "Allow_osNpcMoveToTarget" "\${OSSL|osslNPC}" + set_ini_key "$file" "OSSL" "Allow_osNpcPlayAnimation" "\${OSSL|osslNPC}" + set_ini_key "$file" "OSSL" "Allow_osNpcRemove" "\${OSSL|osslNPC}" + set_ini_key "$file" "OSSL" "Allow_osNpcSaveAppearance" "\${OSSL|osslNPC}" + set_ini_key "$file" "OSSL" "Allow_osNpcSay" "\${OSSL|osslNPC}" + set_ini_key "$file" "OSSL" "Allow_osNpcSayTo" "\${OSSL|osslNPC}" + set_ini_key "$file" "OSSL" "Allow_osNpcSetRot" "\${OSSL|osslNPC}" + set_ini_key "$file" "OSSL" "Allow_osNpcShout" "\${OSSL|osslNPC}" + set_ini_key "$file" "OSSL" "Allow_osNpcSit" "\${OSSL|osslNPC}" + set_ini_key "$file" "OSSL" "Allow_osNpcStand" "\${OSSL|osslNPC}" + set_ini_key "$file" "OSSL" "Allow_osNpcStopAnimation" "\${OSSL|osslNPC}" + set_ini_key "$file" "OSSL" "Allow_osNpcStopMoveToTarget" "\${OSSL|osslNPC}" + set_ini_key "$file" "OSSL" "Allow_osNpcTouch" "\${OSSL|osslNPC}" + set_ini_key "$file" "OSSL" "Allow_osNpcWhisper" "\${OSSL|osslNPC}" + set_ini_key "$file" "OSSL" "Allow_osOwnerSaveAppearance" "\${OSSL|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER" + set_ini_key "$file" "OSSL" "Allow_osParcelJoin" "ESTATE_MANAGER,ESTATE_OWNER" + set_ini_key "$file" "OSSL" "Allow_osParcelSubdivide" "ESTATE_MANAGER,ESTATE_OWNER" + set_ini_key "$file" "OSSL" "Allow_osRegionRestart" "ESTATE_MANAGER,ESTATE_OWNER" + set_ini_key "$file" "OSSL" "Allow_osRegionNotice" "ESTATE_MANAGER,ESTATE_OWNER" + set_ini_key "$file" "OSSL" "Allow_osSetProjectionParams" "true" + set_ini_key "$file" "OSSL" "Allow_osSetRegionWaterHeight" "ESTATE_MANAGER,ESTATE_OWNER" + set_ini_key "$file" "OSSL" "Allow_osSetTerrainHeight" "ESTATE_MANAGER,ESTATE_OWNER" + set_ini_key "$file" "OSSL" "Allow_osSetTerrainTexture" "ESTATE_MANAGER,ESTATE_OWNER" + set_ini_key "$file" "OSSL" "Allow_osSetTerrainTextureHeight" "ESTATE_MANAGER,ESTATE_OWNER" + set_ini_key "$file" "OSSL" "Allow_osAgentSaveAppearance" "ESTATE_MANAGER,ESTATE_OWNER" + set_ini_key "$file" "OSSL" "Allow_osAvatarPlayAnimation" "true" + set_ini_key "$file" "OSSL" "Allow_osAvatarStopAnimation" "true" + set_ini_key "$file" "OSSL" "Allow_osForceAttachToOtherAvatarFromInventory" "true" + set_ini_key "$file" "OSSL" "Allow_osForceDetachFromAvatar" "true" + set_ini_key "$file" "OSSL" "Allow_osForceOtherSit" "true" + set_ini_key "$file" "OSSL" "Allow_osGetNotecard" "\${OSSL|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER" + set_ini_key "$file" "OSSL" "Allow_osGetNotecardLine" "\${OSSL|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER" + set_ini_key "$file" "OSSL" "Allow_osGetNumberOfNotecardLines" "\${OSSL|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER" + set_ini_key "$file" "OSSL" "Allow_osSetDynamicTextureURL" "ESTATE_MANAGER,ESTATE_OWNER" + set_ini_key "$file" "OSSL" "Allow_osSetDynamicTextureURLBlend" "ESTATE_MANAGER,ESTATE_OWNER" + set_ini_key "$file" "OSSL" "Allow_osSetDynamicTextureURLBlendFace" "ESTATE_MANAGER,ESTATE_OWNER" + set_ini_key "$file" "OSSL" "Allow_osSetRot" "true" + set_ini_key "$file" "OSSL" "Allow_osSetParcelDetails" "\${OSSL|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER" + + log "${COLOR_OK}→ osslEnable.ini neu geschrieben für sim$i${COLOR_RESET}" + + sleep 1 + fi + done + + log "${COLOR_OK}osslEnable.ini konfiguriert.${COLOR_RESET}" + blankline +} + +# Erstellt eine StandaloneCommon.ini +function standalonecommoniniconfig() { + local ip="$1" + local gridname="$2" + + # Gridname bereinigen + gridname=$(echo "$gridname" | sed -e 's/ä/ae/g' -e 's/ö/oe/g' -e 's/ü/ue/g' -e 's/[-&]/_/g' -e 's/ */_/g' -e 's/__\+/_/g') + + local config_dir="$SCRIPT_DIR/opensim/bin/config-include" + local file="$config_dir/StandaloneCommon.ini" + local example_file="$config_dir/StandaloneCommon.ini.example" + + log "${COLOR_FILE}Erstelle StandaloneCommon.ini in opensim/bin${COLOR_RESET}" + mkdir -p "$config_dir" + + # Datei erstellen + #touch "$welcome_ini" + #touch "$welcome_ini" || echo "" > "$welcome_ini" + touch "$file" 2>/dev/null || echo "" > "$file" + + # 2. Const-Sektion hinzufügen + add_ini_section "$file" "Const" + + # 3. Werte für Const-Sektion setzen + set_ini_key "$file" "Const" "BaseHostname" "\"$ip\"" + set_ini_key "$file" "Const" "BaseURL" "http://\${Const|BaseHostname}" + set_ini_key "$file" "Const" "PublicPort" "\"9000\"" + set_ini_key "$file" "Const" "PrivatePort" "\"9003\"" + set_ini_key "$file" "Const" "PrivURL" "http://\${Const|BaseHostname}:\${Const|PrivatePort}" + + # 4. Hypergrid- und GridInfo-Werte setzen + set_ini_key "$file" "Hypergrid" "GatekeeperURI" "\"http://$ip:8002\"" + set_ini_key "$file" "Hypergrid" "HomeURI" "\"http://$ip:8002\"" + set_ini_key "$file" "GridInfoService" "GridName" "\"$gridname\"" + set_ini_key "$file" "GridInfoService" "GridLoginURI" "\"http://$ip:8002\"" + + log "${COLOR_OK}→ StandaloneCommon.ini erfolgreich erstellt${COLOR_RESET}" + blankline +} + +# Random Position (mit Simulator-Offset) +function random_position() { + local center_x=$1 + local center_y=$2 + local n=$3 # Region-Nummer + local sim_offset=$4 # Simulator-Offset + + # Zufallsposition mit Simulator-basiertem Seed + RANDOM=$(( n + sim_offset * 1000 )) # Reproduzierbarer Zufall + local offset_x=$(( (RANDOM % 200) - 100 )) # Kleinerer Bereich (±100 Einheiten) + local offset_y=$(( (RANDOM % 200) - 100 )) + + echo "$((center_x + offset_x + sim_offset * 10)),$((center_y + offset_y + sim_offset * 10))" +} + +# Fibonacci Grid (mit Simulator-Offset) +function fibonacci_grid_position() { + local center_x=$1 + local center_y=$2 + local n=$3 + local sim_offset=$4 + + local a=0; local b=1 + for ((i=0; i&2 + return 1 + fi + + log "${SYM_OK} ${COLOR_ACTION}Sie haben ${COLOR_OK}$regions_per_sim${COLOR_ACTION} Regionen pro Simulator gewählt.${COLOR_RESET}" + + system_ip=$(hostname -I | awk '{print $1}') + + log "\n${SYM_SERVER}${COLOR_HEADING} Starte Regionserstellung...${COLOR_RESET}" + + # Simulatoren durchlaufen (beginnt bei 2 um sim1 für Welcome-Region freizuhalten) + for ((sim_num=2; sim_num<=999; sim_num++)); do + sim_dir="${SCRIPT_DIR}/sim${sim_num}/bin/Regions" + + if [[ -d "$sim_dir" ]]; then + log "${SYM_FOLDER}${COLOR_SERVER} Simulator ${sim_num}:${COLOR_RESET} ${COLOR_ACTION}Erstelle ${regions_per_sim} Region(en)${COLOR_RESET}" >&2 + + # Bestehende Ports erfassen + local existing_port_count=0 + if [[ -d "$sim_dir" ]]; then + for existing_file in "$sim_dir"/*.ini; do + if [[ -f "$existing_file" ]]; then + existing_port=$(grep -oP 'InternalPort\s*=\s*\K\d+' "$existing_file" 2>/dev/null) + if [[ -n "$existing_port" ]]; then + used_ports["$existing_port"]=1 + ((existing_port_count++)) + fi + fi + done + fi + + # Regionen erstellen Random Fibonacci Raute Square Hexagon Spiral Checkerboard + for ((region_num=1; region_num<=regions_per_sim; region_num++)); do + # Position berechnen (mit Kollisionsprüfung) + local attempts=0 + local max_attempts=100 + + while true; do + # Hier wird die entsprechende externe Funktion aufgerufen + if [[ "$position" == "Random" ]]; then + # Zufalls-Gitter: + location=$(random_position "$center_x" "$center_y" "$region_num" "$sim_num") + elif [[ "$position" == "Fibonacci" ]]; then + # Fibonacci-Gitter: + location=$(fibonacci_grid_position "$center_x" "$center_y" "$region_num" "$sim_num") + elif [[ "$position" == "Raute" ]]; then + # Rauten-Gitter: + location=$(diamond_position "$center_x" "$center_y" "$region_num" "$sim_num") + elif [[ "$position" == "Square" ]]; then + # Square-Gitter: + location=$(square_grid_position "$center_x" "$center_y" "$region_num" "$sim_num") + elif [[ "$position" == "Hexagon" ]]; then + # Hexagon-Gitter: + location=$(hex_grid_position "$center_x" "$center_y" "$region_num" "$sim_num") + elif [[ "$position" == "Spiral" ]]; then + # Spirale-Gitter: + location=$(spiral_grid_position "$center_x" "$center_y" "$region_num" "$sim_num") + else + # Fallback zu Random bei ungültiger Eingabe + location=$(random_position "$center_x" "$center_y" "$region_num" "$sim_num") + fi + + if [[ -z "${used_locations[$location]}" ]]; then + used_locations[$location]=1 + break + fi + + attempts=$((attempts + 1)) + if (( attempts >= max_attempts )); then + log "${SYM_BAD} ${COLOR_WARNING}Fehler: Konnte nach ${max_attempts} Versuchen keine eindeutige Position finden${COLOR_RESET}" >&2 + return 1 + fi + done + + # Eindeutigen Port finden + local port_attempts=0 + local max_port_attempts=100 + local port_base=$((base_port + sim_num * 100)) # 9100, 9200, 9300 usw. + + while true; do + # Port innerhalb des reservierten Blocks (9100-9199 etc.) + port=$((port_base + region_num + existing_port_count)) + + # Sicherstellen, dass wir im Block bleiben + if (( port >= port_base + 100 )); then + log "${SYM_BAD} ${COLOR_WARNING}Fehler: Keine freien Ports mehr im Block ${port_base}-$((port_base+99))${COLOR_RESET}" >&2 + return 1 + fi + + # Prüfen ob Port verfügbar ist + if [[ -z "${used_ports[$port]}" ]] && ! nc -z localhost "$port" 2>/dev/null; then + used_ports["$port"]=1 + break + fi + + ((existing_port_count++)) + ((port_attempts++)) + + if (( port_attempts >= max_port_attempts )); then + log "${SYM_BAD} ${COLOR_WARNING}Fehler: Konnte in Simulator ${sim_num} nach ${max_port_attempts} Versuchen keinen freien Port finden (Block ${port_base}-$((port_base+99))${COLOR_RESET}" >&2 + return 1 + fi + done + + # Neu generierter Name + generate_name + genRegionName="${gennamefirst}${gennamesecond}$((RANDOM % 900 + 100))" + echo "$genRegionName" + region_name=${genRegionName} + + region_uuid=$(command -v uuidgen >/dev/null 2>&1 && uuidgen 2>/dev/null || cat /proc/sys/kernel/random/uuid 2>/dev/null || echo "$RANDOM-$RANDOM-$RANDOM-$RANDOM") + + config_file="${sim_dir}/${region_name}.ini" + + # Prüfen ob Region existiert + if [[ -f "$config_file" ]]; then + log "${SYM_WAIT} ${COLOR_WARNING}Überspringe ${COLOR_VALUE}${region_name}${COLOR_RESET}${COLOR_WARNING} - existiert bereits${COLOR_RESET}" >&2 + continue + fi + + touch "$config_file" 2>/dev/null || echo "" > "$config_file" + + # Regionseinstellungen hinzufügen + add_ini_section "$config_file" "$region_name" + + # Regionseinstellungen setzen + set_ini_key "$config_file" "$region_name" "RegionUUID" "$region_uuid" + set_ini_key "$config_file" "$region_name" "Location" "$location" + set_ini_key "$config_file" "$region_name" "SizeX" "256" + set_ini_key "$config_file" "$region_name" "SizeY" "256" + set_ini_key "$config_file" "$region_name" "SizeZ" "256" + set_ini_key "$config_file" "$region_name" "InternalPort" "$port" + set_ini_key "$config_file" "$region_name" "ExternalHostName" "$system_ip" + set_ini_key "$config_file" "$region_name" "MaxPrims" "15000" + set_ini_key "$config_file" "$region_name" "MaxAgents" "40" + set_ini_key "$config_file" "$region_name" "MaptileStaticUUID" "$region_uuid" + set_ini_key "$config_file" "$region_name" "InternalAddress" "0.0.0.0" + set_ini_key "$config_file" "$region_name" "AllowAlternatePorts" "False" + set_ini_key "$config_file" "$region_name" "NonPhysicalPrimMax" "512" + set_ini_key "$config_file" "$region_name" "PhysicalPrimMax" "128" + set_ini_key "$config_file" "$region_name" ";ClampPrimSize" "false" + set_ini_key "$config_file" "$region_name" ";MaxPrimsPerUser" "-1" + set_ini_key "$config_file" "$region_name" ";ScopeID" "$region_uuid" + set_ini_key "$config_file" "$region_name" ";RegionType" "Mainland" + set_ini_key "$config_file" "$region_name" ";RenderMinHeight" "-1" + set_ini_key "$config_file" "$region_name" ";RenderMaxHeight" "100" + set_ini_key "$config_file" "$region_name" ";MapImageModule" "Warp3DImageModule" + set_ini_key "$config_file" "$region_name" ";TextureOnMapTile" "true" + set_ini_key "$config_file" "$region_name" ";DrawPrimOnMapTile" "true" + set_ini_key "$config_file" "$region_name" ";GenerateMaptiles" "true" + set_ini_key "$config_file" "$region_name" ";MaptileRefresh" "0" + set_ini_key "$config_file" "$region_name" ";MaptileStaticFile" "path/to/SomeFile.png" # Bitte verwenden sie nicht den Pfad des OpenSimulators sondern ein externen Pfad! + set_ini_key "$config_file" "$region_name" ";MasterAvatarFirstName" "John" + set_ini_key "$config_file" "$region_name" ";MasterAvatarLastName" "Doe" + set_ini_key "$config_file" "$region_name" ";MasterAvatarSandboxPassword" "passwd" + + log "${SYM_OK} ${COLOR_VALUE}${region_name} ${COLOR_DIR}(${location}, Port ${port})${COLOR_RESET}" >&2 + done + fi + done + + log "${SYM_OK}${COLOR_OK} Regionserstellung abgeschlossen!${COLOR_RESET}" + blankline + return 0 +} + +# Hauptfunktion, fragt Benutzer nach IP & Gridnamen und ruft alle Teilfunktionen auf +function iniconfig() { + # 30.04.2025 - Server-Konfiguration + + echo "Das Arbeitsverzeichnis ist: $SCRIPT_DIR" + + echo "Wie ist Ihre IP oder DNS-Adresse? ($system_ip)" + read -r ip + + # Falls die IP leer bleibt, wird system_ip verwendet + if [[ -z "$ip" ]]; then + ip=$system_ip + fi + + echo "Wie heißt Ihr Grid?" + read -r gridname + + # Falls der Gridname leer bleibt, wird eine generierte Name verwendet + if [[ -z "$gridname" ]]; then + gridname=${genGridName} + fi + + echo "Verwendete IP: $ip" + echo "Gridname: $gridname" + + # **Speicherung der Server-Daten in UserInfo.ini** + local ini_file="${SCRIPT_DIR}/UserInfo.ini" + + if [[ ! -f "$ini_file" ]]; then + log "${COLOR_INFO}Erstelle UserInfo.ini${COLOR_RESET}" + sudo touch "$ini_file" + fi + + log "\n[ServerData]" | sudo tee -a "$ini_file" >/dev/null + echo "Arbeitsverzeichnis = ${SCRIPT_DIR}" | sudo tee -a "$ini_file" >/dev/null + echo "ServerIP = ${ip}" | sudo tee -a "$ini_file" >/dev/null + echo "GridName = ${gridname}" | sudo tee -a "$ini_file" >/dev/null + + log "${SYM_LOG} ${COLOR_LABEL}Serverdaten gespeichert in: ${COLOR_FILE}${ini_file}${COLOR_RESET}" + + #! ${SYM_WARNING} **Wichtige Sicherheitsinformation!** + # Die Datei UserInfo.ini enthält Zugangsdaten. + # Sie **sollte danach unbedingt von eurem Server gelöscht und sicher auf eurem PC gespeichert werden**. + + # Konfigurationsfunktionen für verschiedene Komponenten aufrufen + echo "Starte moneyserveriniconfig ..." + moneyserveriniconfig "$ip" "$gridname" + echo "Starte opensiminiconfig ..." + opensiminiconfig "$ip" "$gridname" + echo "Starte robusthginiconfig ..." + robusthginiconfig "$ip" "$gridname" + echo "Starte robustiniconfig ..." + robustiniconfig "$ip" "$gridname" + echo "Starte gridcommoniniconfig ..." + gridcommoniniconfig "$ip" "$gridname" + echo "Starte standalonecommoniniconfig ..." + standalonecommoniniconfig "$ip" "$gridname" + echo "Starte flotsaminiconfig ..." + flotsaminiconfig "$ip" "$gridname" + echo "Starte osslenableiniconfig ..." + osslenableiniconfig "$ip" "$gridname" + echo "Starte welcomeiniconfig ..." + welcomeiniconfig "$ip" "$gridname" + echo "Starte database_set_iniconfig ..." + database_set_iniconfig + echo "Starte oswebinterfaceconfig ..." + oswebinterfaceconfig + + # Auswahl des Modus Hypergrid oder Geschlossener Grid. + hypergrid "hypergrid" + #hypergrid "closed" +} + + +#?────────────────────────────────────────────────────────────────────────────────────────── +#* XML Konfigurationen für Addon Pakete wie Texturen, Avatare oder Skripte +#?────────────────────────────────────────────────────────────────────────────────────────── + +# shellcheck disable=SC2317 +function verify_xml_section() { + + local file="$1" + local section_name="$2" + + if [ ! -f "$file" ]; then + log "${SYM_BAD} ${COLOR_BAD}Error: File ${COLOR_DIR}${file}${COLOR_BAD} does not exist${COLOR_RESET}" >&2 + return 2 + fi + + if grep -q "
" "$file"; then + log "${SYM_OK} ${COLOR_LABEL}Section ${COLOR_VALUE}'$section_name'${COLOR_LABEL} exists in ${COLOR_DIR}${file}${COLOR_RESET}" + return 0 + else + log "${SYM_BAD} ${COLOR_LABEL}Section ${COLOR_VALUE}'$section_name'${COLOR_LABEL} not found in ${COLOR_DIR}${file}${COLOR_RESET}" + return 1 + fi +} + +# shellcheck disable=SC2317 +function add_xml_section() { + local file="$1" + local section_name="$2" + local content="$3" + local closing_tag="${4:-}" # Standard ist , kann überschrieben werden + + # Verifizieren ob der Abschnitt bereits existiert + if verify_xml_section "$file" "$section_name" >/dev/null; then + return 0 + fi + + # Temporäre Datei für Sicherheitskopie + local temp_file + temp_file=$(mktemp) + + log "${SYM_INFO} ${COLOR_ACTION}Adding section ${COLOR_VALUE}'$section_name'${COLOR_ACTION} to ${COLOR_DIR}${file}${COLOR_RESET}" + + # Abschnitt vor dem schließenden Tag einfügen + if sed "s|$closing_tag|
\n$content\n
\n$closing_tag|" "$file" > "$temp_file"; then + if mv "$temp_file" "$file"; then + log "${SYM_OK} ${COLOR_OK}Successfully added section ${COLOR_VALUE}'$section_name'${COLOR_OK} to ${COLOR_DIR}${file}${COLOR_RESET}" + return 0 + fi + fi + + log "${SYM_BAD} ${COLOR_BAD}Failed to add section ${COLOR_VALUE}'$section_name'${COLOR_BAD} to ${COLOR_DIR}${file}${COLOR_RESET}" >&2 + return 1 +} + +# shellcheck disable=SC2317 +function del_xml_section() { + local file="$1" + local section_name="$2" + + # Verifizieren ob der Abschnitt existiert + if ! verify_xml_section "$file" "$section_name" >/dev/null; then + return 0 # Abschnitt existiert nicht - nichts zu tun + fi + + # Temporäre Datei für Sicherheitskopie + local temp_file + temp_file=$(mktemp) + + log "${SYM_INFO} ${COLOR_ACTION}Removing section ${COLOR_VALUE}'$section_name'${COLOR_ACTION} from ${COLOR_DIR}${file}${COLOR_RESET}" + + # Abschnitt entfernen (inklusive aller Zeilen zwischen Section-Tags) + if sed "/
/,/<\/Section>/d" "$file" > "$temp_file"; then + if mv "$temp_file" "$file"; then + log "${SYM_OK} ${COLOR_OK}Successfully removed section ${COLOR_VALUE}'$section_name'${COLOR_OK} from ${COLOR_DIR}${file}${COLOR_RESET}" + return 0 + fi + fi + + log "${SYM_BAD} ${COLOR_BAD}Failed to remove section ${COLOR_VALUE}'$section_name'${COLOR_BAD} from ${COLOR_DIR}${file}${COLOR_RESET}" >&2 + return 1 +} + +#* Beispiel Implementierung für PBR Textures ohne Test eingefügt. +function configure_pbr_textures() { + local asset_sets_file="opensim/bin/assets/AssetSets.xml" + local libraries_file="opensim/bin/inventory/Libraries.xml" + + log "\n${COLOR_HEADING}=== Configuring PBR Textures ===${COLOR_RESET}" + + # AssetSets.xml bearbeiten + add_xml_section "$asset_sets_file" "PBR Textures AssetSet" \ + " " || return 1 + + # Libraries.xml bearbeiten + add_xml_section "$libraries_file" "PBRTextures Library" \ + " \n " || return 1 + + log "${SYM_OK} ${COLOR_OK}PBR Textures configuration completed successfully${COLOR_RESET}" + +} + +function regionsclean() { + log "\033[33mWARNUNG: Dies wird ALLE Regionskonfigurationen in allen Simulatoren löschen!\033[0m" + echo "Sicher fortfahren? (j/N): " + read -r confirm + + if [[ "$confirm" =~ ^[jJ] ]]; then + echo "Starte Bereinigung..." + deleted_count=0 + + # Durch alle Simulatoren iterieren + for ((sim_num=1; sim_num<=999; sim_num++)); do + sim_dir="sim${sim_num}/bin/Regions" + + if [[ -d "$sim_dir" ]]; then + log "\033[33m ✓ Überprüfe $sim_dir...\033[0m" + + # Lösche nur .ini-Dateien (keine anderen Dateitypen) + while IFS= read -r -d $'\0' config_file; do + if [[ "$config_file" == *.ini ]]; then + rm -v "$config_file" + ((deleted_count++)) + fi + done < <(find "$sim_dir" -maxdepth 1 -type f -name "*.ini" -print0) + fi + done + + log "\033[32mFertig! Gelöschte Regionen: $deleted_count\033[0m" + else + echo "Abbruch: Keine Dateien wurden gelöscht." + fi + blankline +} + +function renamefiles() { + timestamp=$(date +"%Y%m%d_%H%M%S") + + if [ ! -d "$SCRIPT_DIR" ]; then + log "${RED}Fehler: Verzeichnis $SCRIPT_DIR nicht gefunden!${RESET}" + return 1 + fi + + log "${CYAN}Starte Umbenennung aller *.example Dateien in ${SCRIPT_DIR} und Unterverzeichnissen...${RESET}" + + # Verarbeite robust/bin zuerst + find "$SCRIPT_DIR" -type f -name "*.example" 2>/dev/null | while read -r example_file; do + local target_file="${example_file%.example}" # Entfernt .example + + if [ -f "$target_file" ]; then + local backup_file="${target_file}_${timestamp}.bak" + mv "$target_file" "$backup_file" + log "${YELLOW}Gesichert: ${target_file} → ${backup_file}${RESET}" + fi + + mv "$example_file" "$target_file" + log "${GREEN}Umbenannt: ${example_file} → ${target_file}${RESET}" + done + + # Jetzt alle simX/bin Verzeichnisse durchlaufen + for ((i=999; i>=1; i--)); do + sim_dir="${SCRIPT_DIR}/sim$i/bin" + if [ -d "$sim_dir" ]; then + log "${CYAN}Verarbeite: $sim_dir ${RESET}" + find "$sim_dir" -type f -name "*.example" 2>/dev/null | while read -r example_file; do + local target_file="${example_file%.example}" + + sleep 1 + + if [ -f "$target_file" ]; then + local backup_file="${target_file}_${timestamp}.bak" + mv "$target_file" "$backup_file" + log "${YELLOW}Gesichert: ${target_file} → ${backup_file}${RESET}" + fi + + mv "$example_file" "$target_file" + log "${GREEN}Umbenannt: ${example_file} → ${target_file}${RESET}" + done + fi + done + + log "${GREEN}Alle *.example Dateien wurden erfolgreich verarbeitet.${RESET}" + blankline + return 0 +} + +# Helper to clean config files (remove leading spaces/tabs) +function clean_config() { + local file=$1 + sed -i 's/^[ \t]*//' "$file" +} + +# cleanall - Removes OpenSim completely +function cleanall() { + echo "Möchtest du OpenSim komplett mit Konfigurationen entfernen? (ja/nein)" + read -r answer + + if [ "$answer" = "ja" ]; then + echo "Lösche OpenSim vollständig..." + + # robust/bin Verzeichnis leeren, falls vorhanden + if [ -d "robust/bin" ]; then + rm -rf robust/bin/* + fi + + # Überprüfen, welche simX/bin Verzeichnisse existieren und leeren + for i in {1..999}; do + dir="sim$i/bin" + if [ -d "$dir" ]; then + rm -rf "${dir:?}/"* + sleep 1 + fi + done + + echo "Alle bin-Verzeichnisse wurden geleert." + + elif [ "$answer" = "nein" ]; then + echo "OpenSim bleibt erhalten." + + else + echo "Ungültige Eingabe. Bitte 'ja' oder 'nein' eingeben." + fi + blankline +} + +#?────────────────────────────────────────────────────────────────────────────────────────── +#* ZUSAMMENFASSUNG DER FUNKTIONEN IN LOGISCHE GRUPPEN +#?────────────────────────────────────────────────────────────────────────────────────────── + +# Kompletter OpenSim-Neustart mit Logrotation +function opensimrestart() { + opensimstop + sleep $Simulator_Stop_wait # Wartezeit für Dienst-Stopp + logclean # Logbereinigung + sleep $Simulator_Start_wait # Wartezeit vor Neustart + opensimstart + log "\033[36mAktive Screen-Sessions:\033[0m" + screen -ls || echo "Keine Screen-Sessions gefunden" +} + +# Kompletter OpenSim-Neustart mit Logrotation +function opensimrestartParallel() { + opensimstopParallel + sleep $Simulator_Stop_wait # Wartezeit für Dienst-Stopp + logclean # Logbereinigung + sleep $Simulator_Start_wait # Wartezeit vor Neustart + opensimstartParallel + log "\033[36mAktive Screen-Sessions:\033[0m" + screen -ls || echo "Keine Screen-Sessions gefunden" +} + +# Server-Reboot mit Vorbereitung +function reboot() { + log "\033[1;33m🔥 Server-Neustart wird eingeleitet...\033[0m" + + opensimstop + sleep $Simulator_Stop_wait + shutdown -r now +} + +# OpenSim komplett herunterladen ⚡ Vorsicht +function downloadallgit() { + # Downloads aus dem Github. + opensimgitcopy + moneygitcopy + #ruthrothgit # Funktioniert nicht richtig. + #avatarassetsgit # Funktioniert nicht richtig. + #osslscriptsgit + #pbrtexturesgit + # Sourcecode Version vor dem kompilieren einstellen mit der Funktion versionrevision() + #versionrevision + # OpenSimulator erstellen aus dem Source Code. + opensimbuild +} + +function webinstall() { +# Nach database_setup() + log "${COLOR_ACTION}Webserver (Apache/PHP) installieren? (j/n) [n] ${COLOR_RESET}" + read -r web_choice + if [[ -n "$web_choice" && "$web_choice" =~ ^[jJ] ]]; then + setup_webserver + fi +} + +function autoinstall() { + # 06.05.2025 + + # Server vorbereiten + servercheck + + # Verzeichnisse erstellen. Standard: 1 + createdirectory + + # Doppelten Konfigurationsdateien entfernen + removeconfigfiles + + # mySQL installieren. Standard: Benutzername wird generiert, dazu wird ein 16 stelliges Passwort erzeugt. + database_setup + + # Downloads aus dem Github hier OpenSim und Money. einfach beises mit Enter bestätigen. + opensimgitcopy + moneygitcopy + + # PHP installieren + #webinstall + # Webinterface aus dem Github holen + osWebinterfacegit + + # Neue standartavatare einfügen + #ruthrothgit # Funktioniert nicht richtig. + #avatarassetsgit # Funktioniert nicht richtig. + + # OSSL Beispielskripte + osslscriptsgit + + # Texturen für Normale und PBR Texturen installieren + #pbrtexturesgit + + # Sourcecode Version vor dem kompilieren einstellen mit der Funktion versionrevision() + #versionrevision + + # OpenSimulator erstellen aus dem Source Code. Standard: ja. + opensimbuild + + # Kleine Pause + sleep $Simulator_Start_wait + + # OpenSimulator kopieren + opensimcopy + + # Alles konfigurieren + iniconfig + + # Zufallsregionen erstellen + regionsiniconfig + + #Vor dem Start die letzten vorbereitungen. + firststart + + # OpenSimulator starten + opensimrestart + + # Eure Informationen sind gespeichert in UserInfo.ini bitte sicher verwahren. + +} + +function autoupgrade() { + # 05.05.2025 + opensimgitcopy + moneygitcopy + osslscriptsgit + opensimbuild + + sleep 5 + + opensimstop + opensimcopy + opensimstart +} + +function autoupgradefast() { + # 05.05.2025 + opensimgitcopy + moneygitcopy + osslscriptsgit + opensimbuild + + sleep 5 + + opensimstopParallel + opensimcopy + opensimstartParallel +} + +#* OpenSimulator bauen aus Github. +# Dies macht ausser dem OpenSimulator erstellen nichts weiter. +function buildopensim() { + opensimgitcopy + moneygitcopy + osslscriptsgit + # Sourcecode Version vor dem kompilieren einstellen mit der Funktion versionrevision() + versionrevision + opensimbuild +} + +# Standalone +function standalonesetup() { + # 12.05.2025 + log "\e[33m[Standalone] Setup wird durchgeführt...\e[0m" + + # Prüfen ob SCRIPT_DIR gesetzt ist + if [ -z "${SCRIPT_DIR}" ]; then + log "\e[31mFehler: SCRIPT_DIR ist nicht gesetzt!\e[0m" + return 1 + fi + + local opensim_bin="${SCRIPT_DIR}/opensim/bin" + local renamed=0 + local skipped=0 + + # Sicherstellen, dass das Verzeichnis existiert + if [ ! -d "${opensim_bin}" ]; then + log "\e[31mFehler: Verzeichnis ${opensim_bin} nicht gefunden!\e[0m" + return 1 + fi + + # Verarbeite alle .example Dateien + while IFS= read -r -d '' file; do + target="${file%.example}" + + if [ -e "${target}" ]; then + log "\e[33mÜbersprungen: ${target} existiert bereits\e[0m" + ((skipped++)) + else + if mv "${file}" "${target}"; then + log "\e[32mUmbenannt: ${file} → ${target}\e[0m" + ((renamed++)) + else + log "\e[31mFehler beim Umbenennen von ${file}\e[0m" + ((skipped++)) + fi + fi + done < <(find "${opensim_bin}" -type f -name "*.example" -print0) + + genPasswort=$(tr -dc 'A-Za-z0-9!@#$%^&*()' < /dev/urandom | head -c 16) + #genUserid=$(command -v uuidgen >/dev/null 2>&1 && uuidgen 2>/dev/null || cat /proc/sys/kernel/random/uuid 2>/dev/null || echo "$RANDOM-$RANDOM-$RANDOM-$RANDOM") + user_uuid=$(command -v uuidgen >/dev/null 2>&1 && uuidgen 2>/dev/null || cat /proc/sys/kernel/random/uuid 2>/dev/null || echo "$RANDOM-$RANDOM-$RANDOM-$RANDOM") + + log "${COLOR_INFO}OpenSim User Erstellung${COLOR_RESET}" + + echo -n "Vorname: " + read -r VORNAME + + echo -n "Nachname: " + read -r NACHNAME + + echo -n "Passwort: " + read -r PASSWORT + + echo -n "E-Mail: " + read -r EMAIL + + # **Speicherung der Benutzerdaten in UserInfo.ini** NEU + ini_file="${SCRIPT_DIR}/OpenSimUserInfo.ini" + log "${COLOR_INFO}Erstelle OpenSimUserInfo.ini neu${COLOR_RESET}" + sudo rm -f "$ini_file" + sudo touch "$ini_file" + + log "\n[UserData]" | sudo tee -a "$ini_file" >/dev/null + echo "System IP = ${system_ip}" | sudo tee -a "$ini_file" >/dev/null + echo "Vorname = ${VORNAME}" | sudo tee -a "$ini_file" >/dev/null + echo "Nachname = ${NACHNAME}" | sudo tee -a "$ini_file" >/dev/null + echo "Passwort = ${PASSWORT}" | sudo tee -a "$ini_file" >/dev/null + echo "E-Mail = ${EMAIL}" | sudo tee -a "$ini_file" >/dev/null + echo "UserID = ${user_uuid}" | sudo tee -a "$ini_file" >/dev/null + + log "${SYM_LOG} ${COLOR_LABEL}Benutzerdaten hinzugefügt in: ${COLOR_FILE}${ini_file}${COLOR_RESET}" + + blankline +} + +function createstandaloneuser() { + # 12.05.2025 OpenSim Avatar erstellen + + # opensim starten + echo "opensim starten, erster start..." + if [[ -d "opensim/bin" && -f "opensim/bin/OpenSim.dll" ]]; then + log "${SYM_OK} ${COLOR_START}Starte ${COLOR_SERVER}opensim ${COLOR_RESET} ${COLOR_START}aus ${COLOR_DIR}opensim/bin...${COLOR_RESET}" + cd opensim/bin || exit 1 + + screen -fa -S opensim -d -U -m dotnet OpenSim.dll + cd - >/dev/null 2>&1 || exit 1 + else + log "${SYM_BAD} ${COLOR_SERVER}opensim: ${COLOR_BAD}OpenSim.dll nicht gefunden.${COLOR_RESET} ${COLOR_START}Überspringe Start.${COLOR_RESET}" + fi + + + # 1. INI-Datei auslesen mit angepasstem Parsing + ini_file="${SCRIPT_DIR}/OpenSimUserInfo.ini" + if [[ ! -f "$ini_file" ]]; then + log "${COLOR_BAD}UserInfo.ini nicht gefunden unter: $ini_file${COLOR_RESET}" + return 1 + fi + + # Parsing-Funktion mit Trim und Leerzeichen im Pattern + ini_get() { + local section="$1" key="$2" + awk -F ' = ' ' + /^\[/ { current_section = substr($0, 2, length($0)-2) } + current_section == "'"$section"'" && $1 == "'"$key"'" { + sub(/^[ \t]+/, "", $2) # Trim leading whitespace + sub(/[ \t\r]+$/, "", $2) # Trim trailing whitespace + print $2 + exit + } + ' "$ini_file" + } + + # Werte auslesen + VORNAME=$(ini_get "UserData" "Vorname") + NACHNAME=$(ini_get "UserData" "Nachname") + PASSWORT=$(ini_get "UserData" "Passwort") + EMAIL=$(ini_get "UserData" "E-Mail") + user_uuid=$(ini_get "UserData" "UserID") + + # Überprüfe ob opensim läuft + if ! screen -list | grep -q "opensim"; then + log "${COLOR_BAD}CREATEUSER: opensim existiert nicht oder läuft nicht${COLOR_RESET}" + return 1 + fi + + # Benutzer in Robust erstellen + screen -S opensim -p 0 -X eval "stuff 'create user'^M" + screen -S opensim -p 0 -X eval "stuff '$VORNAME'^M" + screen -S opensim -p 0 -X eval "stuff '$NACHNAME'^M" + screen -S opensim -p 0 -X eval "stuff '$PASSWORT'^M" + screen -S opensim -p 0 -X eval "stuff '$EMAIL'^M" + screen -S opensim -p 0 -X eval "stuff '$user_uuid'^M" + screen -S opensim -p 0 -X eval "stuff ''^M" + + screen -S opensim -p 0 -X eval "stuff ''^M" + screen -S opensim -p 0 -X eval "stuff ''^M" + + log "${COLOR_OK}Masteruser $VORNAME $NACHNAME wurde erstellt${COLOR_RESET}" + log "${COLOR_INFO}UserID: $user_uuid${COLOR_RESET}" + blankline +} + +function createstandalonregion() { + # 12.05.2025 Standalone Region erstellen + local config_file="opensim/bin/Regions/opensim.ini" + local region_name="OpenSim" + local location="1000,1000" + local port="9015" + system_ip=$(hostname -I | awk '{print $1}') + region_uuid=$(command -v uuidgen >/dev/null 2>&1 && uuidgen 2>/dev/null || cat /proc/sys/kernel/random/uuid 2>/dev/null || echo "$RANDOM-$RANDOM-$RANDOM-$RANDOM") + SIZES=768 + + # 1. INI-Datei auslesen mit angepasstem Parsing + ini_file="${SCRIPT_DIR}/OpenSimUserInfo.ini" + if [[ ! -f "$ini_file" ]]; then + log "${COLOR_BAD}UserInfo.ini nicht gefunden unter: $ini_file${COLOR_RESET}" + return 1 + fi + + # Parsing-Funktion mit Trim und Leerzeichen im Pattern + ini_get() { + local section="$1" key="$2" + awk -F ' = ' ' + /^\[/ { current_section = substr($0, 2, length($0)-2) } + current_section == "'"$section"'" && $1 == "'"$key"'" { + sub(/^[ \t]+/, "", $2) # Trim leading whitespace + sub(/[ \t\r]+$/, "", $2) # Trim trailing whitespace + print $2 + exit + } + ' "$ini_file" + } + + # Werte auslesen + VORNAME=$(ini_get "UserData" "Vorname") + NACHNAME=$(ini_get "UserData" "Nachname") + PASSWORT=$(ini_get "UserData" "Passwort") + + touch "$config_file" + { + echo "[${VORNAME}${NACHNAME}]" + echo "RegionUUID = ${region_uuid}" + echo "Location = ${location}" + echo "SizeX = ${SIZES}" + echo "SizeY = ${SIZES}" + echo "SizeZ = ${SIZES}" + echo "InternalPort = ${port}" + echo "ExternalHostName = ${system_ip}" + echo "MaxPrims = 45000" + echo "MaxAgents = 40" + echo "MaptileStaticUUID = 20000000-1000-1000-1000-200000000000" + echo "InternalAddress = 0.0.0.0" + echo "AllowAlternatePorts = False" + echo "NonPhysicalPrimMax = 512" + echo "PhysicalPrimMax = 128" + echo ";ClampPrimSize = false" + echo ";MaxPrimsPerUser = -1" + echo ";ScopeID = 30000000-1000-1000-1000-300000000000" + echo ";RegionType = Mainland" + echo ";RenderMinHeight = -1" + echo ";RenderMaxHeight = 100" + echo ";MapImageModule = Warp3DImageModule" + echo ";TextureOnMapTile = true" + echo ";DrawPrimOnMapTile = true" + echo ";GenerateMaptiles = true" + echo ";MaptileRefresh = 0" + echo ";MaptileStaticFile = path/to/SomeFile.png" + echo ";MasterAvatarFirstName = ${VORNAME}" + echo ";MasterAvatarLastName = ${NACHNAME}" + echo ";MasterAvatarSandboxPassword = ${PASSWORT}" + } > "$config_file" + + echo "Region '${region_name}' wurde erfolgreich erstellt in ${config_file} (Location: ${location}, Port: ${port})" + + # Datei Regions.ini löschen + rm -v "opensim/bin/Regions/Regions.ini" +} + +function createstandaloneconfig() { + system_ip=$(hostname -I | awk '{print $1}') + file="$SCRIPT_DIR/opensim/bin/OpenSim.ini" + standalonehg="ja" + + # 1. INI-Datei auslesen mit angepasstem Parsing + ini_file="${SCRIPT_DIR}/OpenSimUserInfo.ini" + if [[ ! -f "$ini_file" ]]; then + log "${COLOR_BAD}UserInfo.ini nicht gefunden unter: $ini_file${COLOR_RESET}" + return 1 + fi + + # Parsing-Funktion mit Trim und Leerzeichen im Pattern + ini_get() { + local section="$1" key="$2" + awk -F ' = ' ' + /^\[/ { current_section = substr($0, 2, length($0)-2) } + current_section == "'"$section"'" && $1 == "'"$key"'" { + sub(/^[ \t]+/, "", $2) # Trim leading whitespace + sub(/[ \t\r]+$/, "", $2) # Trim trailing whitespace + print $2 + exit + } + ' "$ini_file" + } + + # Werte auslesen + VORNAME=$(ini_get "UserData" "Vorname") + NACHNAME=$(ini_get "UserData" "Nachname") + PASSWORT=$(ini_get "UserData" "Passwort") + + if [[ -z "$standalonehg" ]]; then + log "${COLOR_BAD}FEHLER: Variable 'standalonehg' nicht definiert!${COLOR_RESET}" + return 1 + fi + + # [Const] + set_ini_key "$file" "Const" "BaseHostname" "\"${system_ip}\"" + set_ini_key "$file" "Const" "PublicPort" "\"9010\"" + # [Estates] + set_ini_key "$file" "Estates" "DefaultEstateName" "\"$VORNAME$NACHNAME Estate\"" + set_ini_key "$file" "Estates" "DefaultEstateOwnerName" "\"9010\"" + set_ini_key "$file" "Estates" "DefaultEstateOwnerUUID" "\"10000000-1000-1000-1000-100000000000\"" + set_ini_key "$file" "Estates" "DefaultEstateOwnerPassword" "\"${PASSWORT}\"" + # [Terrain] + set_ini_key "$file" "Terrain" "InitialTerrain" "\"flat\"" + + if [[ "$standalonehg" = "ja" ]]; then + # [Architecture] + set_ini_key "$file" "Architecture" "Include-Architecture" "\"config-include/StandaloneHypergrid.ini\"" + log "${COLOR_FILE}Erstelle $file${COLOR_RESET}" + else + # [Architecture] + set_ini_key "$file" "Architecture" "Include-Architecture" "\"config-include/Standalone.ini\"" + log "${COLOR_FILE}Erstelle $file${COLOR_RESET}" + fi +} + +#* Standalone Installation +function buildstandalone() { + # OpenSimulator herunterladen und bauen + opensimgitcopy + # Skripte einfügen + osslscriptsgit + # Sourcecode Version vor dem kompilieren einstellen mit der Funktion versionrevision() + versionrevision + # OpenSimulator bauen + opensimbuild + # Standalone Setup + standalonesetup + # Create Standalone Config + createstandaloneconfig + # Create Startregion + createstandalonregion + # create user + createstandaloneuser + # Standalone neu starten + standalonerestart + + log "${COLOR_OK}Standalone OpenSimulator wurde erfolgreich installiert, die Daten stehen in der Datei OpenSimUserInfo.ini${COLOR_RESET}" + +} + +#?────────────────────────────────────────────────────────────────────────────────────────── +#* Hilfefunktionen +#?────────────────────────────────────────────────────────────────────────────────────────── + +function help() { + # Allgemeine Befehle + log "${COLOR_SECTION}${SYM_SERVER} OpenSim Grundbefehle:${COLOR_RESET}" + log "${SYM_VOR} ${COLOR_START}opensimstart${COLOR_RESET} \t\t # Startet OpenSimulator komplett" + log "${SYM_VOR} ${COLOR_STOP}opensimstop${COLOR_RESET} \t\t # Stoppt OpenSimulator komplett" + log "${SYM_VOR} ${COLOR_RESTART}opensimrestart${COLOR_RESET} \t # Startet den OpenSimulator komplett neu" + echo + log "${SYM_VOR} ${COLOR_START}simstart${COLOR_RESET} \t\t # simX angeben - startet einen Regionsserver" + log "${SYM_VOR} ${COLOR_STOP}simstop${COLOR_RESET} \t\t # simX angeben - stoppt einen Regionsserver" + log "${SYM_VOR} ${COLOR_RESTART}simrestart${COLOR_RESET} \t\t # simX angeben - startet einen Regionsserver neu" + echo "" + log "${SYM_VOR} ${COLOR_START}standalonestart${COLOR_RESET} \t # Standalone starten" + log "${SYM_VOR} ${COLOR_STOP}standalonestop${COLOR_RESET} \t # Standalone stoppen" + log "${SYM_VOR} ${COLOR_RESTART}standalonerestart${COLOR_RESET} \t # Standalone neu starten" + echo "" + log "${SYM_VOR} ${COLOR_RESTART}simkill${COLOR_RESET} \t\t # Wenn der Simulator nicht beendet werden kann" + echo "" + + # System-Checks + log "${COLOR_SECTION}${SYM_TOOLS} System-Checks:${COLOR_RESET}" + log "${SYM_VOR} ${COLOR_OK}check_screens${COLOR_RESET} \t\t # Prüft laufende Prozesse und handelt entsprechend" + echo "" + + # Setup + log "${COLOR_SECTION}${SYM_TOOLS} Setup:${COLOR_RESET}" + #log "${SYM_VOR} ${COLOR_OK}servercheck${COLOR_RESET} \t\t # Installiert und Prüft den Server" + #log "${SYM_VOR} ${COLOR_OK}createdirectory${COLOR_RESET} \t\t # Erstellt alle benötigten Verzeichnisse" + log "${SYM_VOR} ${COLOR_OK}autoinstall${COLOR_RESET} \t\t # OpenSimulator Automatisiert installieren und einrichten" + log "${SYM_VOR} ${COLOR_OK}setcrontab${COLOR_RESET} \t\t # Richtet Crontab ein damit der Server wartungsfrei laeuft" + echo "" + + # Git-Operationen + #log "${COLOR_SECTION}${SYM_BOOK} Git-Operationen:${COLOR_RESET}" + #log "${SYM_VOR} ${COLOR_OK}opensimgitcopy${COLOR_RESET} \t\t # Klont den OpenSim Code" + #log "${SYM_VOR} ${COLOR_OK}moneygitcopy${COLOR_RESET} \t\t # Baut den MoneyServer in den OpenSimulator ein" + #log "${SYM_VOR} ${COLOR_OK}osslscriptsgit${COLOR_RESET} \t\t # Klont OSSL-Skripte" + #log "${SYM_VOR} ${COLOR_OK}versionrevision${COLOR_RESET} \t\t # Setzt Versionsrevision" + #echo "" + + # OpenSim Build & Deploy + #log "${COLOR_SECTION}${SYM_FOLDER} OpenSim Build & Deploy:${COLOR_RESET}" + #log "${SYM_VOR} ${COLOR_OK}opensimbuild${COLOR_RESET} \t\t # Kompiliert OpenSim zu ausführbaren Dateien" + #log "${SYM_VOR} ${COLOR_OK}opensimcopy${COLOR_RESET} \t\t # Kopiert OpenSim in alle Verzeichnisse" + #log "${SYM_VOR} ${COLOR_OK}opensimupgrade${COLOR_RESET} \t\t # Upgradet OpenSim" + #log "${SYM_VOR} ${COLOR_OK}database_setup${COLOR_RESET} \t\t # Erstellt alle Datenbanken" + #echo "" + + # Konfigurationsmanagement + #log "${COLOR_SECTION}${SYM_CONFIG} Konfigurationsmanagement:${COLOR_RESET}" + #log "${SYM_VOR} ${COLOR_WARNING}moneyserveriniconfig${COLOR_RESET}\t# Konfiguriert MoneyServer.ini (experimentell)" + #log "${SYM_VOR} ${COLOR_WARNING}opensiminiconfig${COLOR_RESET}\t# Konfiguriert OpenSim.ini (experimentell)" + #log "${SYM_VOR} ${COLOR_WARNING}regionsiniconfig${COLOR_RESET}\t# Konfiguriert Regionen (experimentell)" + #echo "" + + # Systembereinigung + log "${COLOR_SECTION}${SYM_CLEAN} Systembereinigung:${COLOR_RESET}" + log "${SYM_VOR} ${COLOR_OK}cacheclean${COLOR_RESET} \t\t # Bereinigt OpenSimulator Cache" + log "${SYM_VOR} ${COLOR_OK}logclean${COLOR_RESET} \t\t # Bereinigt OpenSimulator Logs" + log "${SYM_VOR} ${COLOR_OK}mapclean${COLOR_RESET} \t\t # Bereinigt OpenSimulator Maptiles" + #log "${SYM_VOR} ${COLOR_OK}clean_linux_logs${COLOR_RESET}\t# Bereinigt Systemlogs" + echo "" + + # Hilfe + log "${COLOR_SECTION}${SYM_INFO} Hilfe:${COLOR_RESET}" + log "${SYM_VOR} ${COLOR_OK}help${COLOR_RESET} \t\t\t # Zeigt diese Hilfe" + log "${SYM_VOR} ${COLOR_OK}prohelp${COLOR_RESET} \t\t # Zeigt die Pro Hilfe" + echo "" + log "${COLOR_SECTION}${SYM_INFO} Aufruf mit: bash osmtool.sh ${COLOR_RESET}" +} + +function prohelp() { + #* OpenSim Grundbefehle + log "${COLOR_SECTION}${SYM_SERVER} OpenSim Grundbefehle:${COLOR_RESET}" + log "${SYM_VOR} ${COLOR_START}opensimstart${COLOR_RESET} \t\t # OpenSim starten" + log "${SYM_VOR} ${COLOR_STOP}opensimstop${COLOR_RESET} \t\t # OpenSim stoppen" + log "${SYM_VOR} ${COLOR_RESTART}opensimrestart${COLOR_RESET} \t # OpenSim neu starten" + log "${SYM_VOR} ${COLOR_OK}check_screens${COLOR_RESET} \t\t # Laufende OpenSim-Prozesse prüfen und neu starten" + log "${SYM_VOR} ${COLOR_OK}autoupgrade${COLOR_RESET} \t\t # Automatisches OpenSim upgrade" + echo " " + log "${SYM_VOR} ${COLOR_START}opensimstartParallel${COLOR_RESET} \t # Startet alle Regionen parallel" + log "${SYM_VOR} ${COLOR_STOP}opensimstopParallel${COLOR_RESET} \t # Stoppt alle Regionen parallel" + log "${SYM_VOR} ${COLOR_RESTART}opensimrestartParallel${COLOR_RESET} # Startet alle Regionen neu (parallel)" + + log "${SYM_VOR} ${COLOR_START}simstart${COLOR_RESET} \t\t # Startet eine bestimmte Region" + log "${SYM_VOR} ${COLOR_STOP}simstop${COLOR_RESET} \t\t # Stoppt eine bestimmte Region" + log "${SYM_VOR} ${COLOR_RESTART}simrestart${COLOR_RESET} \t\t # Startet eine bestimmte Region neu" + echo " " + log "${SYM_VOR} ${COLOR_RESTART}simkill${COLOR_RESET} \t\t # Wenn der Simulator nicht beendet werden kann" + echo "" + + #* Standalone-Operationen + log "${COLOR_SECTION}${SYM_SCRIPT} Standalone-Operationen:${COLOR_RESET}" + log "${SYM_VOR} ${COLOR_START}standalonestart${COLOR_RESET} \t\t # Standalone starten" + log "${SYM_VOR} ${COLOR_STOP}standalonestop${COLOR_RESET} \t\t # Standalone stoppen" + log "${SYM_VOR} ${COLOR_RESTART}standalonerestart${COLOR_RESET} \t\t # Standalone neu starten" + log "${SYM_VOR} ${COLOR_OK}standalonestatus${COLOR_RESET} \t\t # Standalone Status anzeigen" + echo " " + log "${SYM_VOR} ${COLOR_OK}buildstandalone${COLOR_RESET} \t\t # Standalone erstellen" + log "${SYM_VOR} ${COLOR_OK}opensimgitcopy${COLOR_RESET} \t\t # OpenSimulator aus Git kopieren" + log "${SYM_VOR} ${COLOR_OK}osslscriptsgit${COLOR_RESET} \t\t # Skripte aus Repository einfügen" + log "${SYM_VOR} ${COLOR_OK}versionrevision${COLOR_RESET} \t\t # OpenSimulator-Version festlegen" + log "${SYM_VOR} ${COLOR_OK}opensimbuild${COLOR_RESET} \t\t\t # OpenSimulator bauen" + log "${SYM_VOR} ${COLOR_OK}standalonesetup${COLOR_RESET} \t\t # Standalone Setup durchführen" + log "${SYM_VOR} ${COLOR_OK}createmasteruser${COLOR_RESET} \t\t # Master-Benutzer erstellen" + echo " " + + #* System-Checks + log "${COLOR_SECTION}${SYM_TOOLS} System-Checks:${COLOR_RESET}" + log "${SYM_VOR} ${COLOR_OK}servercheck${COLOR_RESET} \t\t # Serverbereitschaft prüfen und Abhängigkeiten installieren" + echo " " + + #* Setup + log "${COLOR_SECTION}${SYM_TOOLS} Setup:${COLOR_RESET}" + log "${SYM_VOR} ${COLOR_OK}createdirectory${COLOR_RESET} \t # OpenSim-Verzeichnisse erstellen" + log "${SYM_VOR} ${COLOR_OK}setcrontab${COLOR_RESET} \t\t # Crontab Automatisierungen einrichten" + log "${SYM_VOR} ${COLOR_OK}setup_webserver${COLOR_RESET} \t # Webserver einrichten" + log "${SYM_VOR} ${COLOR_OK}webinstall${COLOR_RESET} \t\t # Webinterface installieren" + log "${SYM_VOR} ${COLOR_OK}osWebinterfacegit${COLOR_RESET} \t # Webinterface aus Git holen" + log "${SYM_VOR} ${COLOR_OK}autoinstall${COLOR_RESET} \t\t # OpenSimulator Automatisiert installieren und einrichten" + log "${SYM_VOR} ${COLOR_OK}firststart${COLOR_RESET} \t\t # Erststart-Konfiguration durchführen" + log "${SYM_VOR} ${COLOR_OK}setup_email_server${COLOR_RESET} \t # E-Mail-Server einrichten" + log "${SYM_VOR} ${COLOR_OK}remove_ubuntu_pro${COLOR_RESET} \t # Ubuntu Pro entfernen" + echo " " + + #* Git-Operationen + log "${COLOR_SECTION}${SYM_BOOK} Git-Operationen:${COLOR_RESET}" + + log "${SYM_VOR} ${COLOR_OK}opensimgitcopy${COLOR_RESET} \t # OpenSim aus Git herunterladen" + log "${SYM_VOR} ${COLOR_OK}moneygitcopy${COLOR_RESET} \t\t # MoneyServer aus Git holen" + log "${SYM_VOR} ${COLOR_WARNING}ruthrothgit${COLOR_RESET} \t\t # Ruth Roth IAR Dateien ${COLOR_BAD}(Vorsicht)${COLOR_RESET}" + log "${SYM_VOR} ${COLOR_WARNING}avatarassetsgit${COLOR_RESET} \t # Avatar-Assets ${COLOR_BAD}(Vorsicht)${COLOR_RESET}" + log "${SYM_VOR} ${COLOR_OK}osslscriptsgit${COLOR_RESET} \t # OSSL Beispielskripte herunterladen" + log "${SYM_VOR} ${COLOR_WARNING}pbrtexturesgit${COLOR_RESET} \t # PBR-Texturen ${COLOR_BAD}(Vorsicht)${COLOR_RESET}" + log "${SYM_VOR} ${COLOR_OK}downloadallgit${COLOR_RESET} \t # Alle Git-Repos herunterladen" + log "${SYM_VOR} ${COLOR_OK}versionrevision${COLOR_RESET} \t # Versionsverwaltung aktivieren" + echo " " + + #* OpenSim Build & Deployment + log "${COLOR_SECTION}${SYM_FOLDER} OpenSim Build & Deploy:${COLOR_RESET}" + log "${SYM_VOR} ${COLOR_OK}opensimbuild${COLOR_RESET} \t\t\t # OpenSim kompilieren" + log "${SYM_VOR} ${COLOR_OK}opensimcopy${COLOR_RESET} \t\t\t # OpenSim Dateien kopieren" + log "${SYM_VOR} ${COLOR_OK}opensimupgrade${COLOR_RESET} \t\t # OpenSim aktualisieren" + log "${SYM_VOR} ${COLOR_OK}database_setup${COLOR_RESET} \t\t # Datenbank für OpenSim einrichten" + log "${SYM_VOR} ${COLOR_OK}removeconfigfiles${COLOR_RESET} \t\t # Konfigurationsdateien entfernen" + log "${SYM_VOR} ${COLOR_OK}cleanconfig${COLOR_RESET} \t\t\t # Konfiguration bereinigen" + echo " " + log "${SYM_VOR} ${COLOR_OK}autoupgrade${COLOR_RESET} \t\t\t # Führt automatisches Update durch" + log "${SYM_VOR} ${COLOR_OK}autoupgradefast${COLOR_RESET} \t\t # Automatisches OpenSim Parallel upgraden" + log "${SYM_VOR} ${COLOR_OK}regionbackup${COLOR_RESET} \t\t\t # Sichert aktuelle Regionen-Daten" + log "${SYM_VOR} ${COLOR_OK}robustbackup${COLOR_RESET} \t\t\t # Backup der Robust-Datenbank (mit Zeitraumfilter)" + log "${SYM_VOR} ${COLOR_OK}robustrestore [teil]${COLOR_RESET} # Wiederherstellung aus robustbackup ${COLOR_BAD} experimentell${COLOR_RESET}" + log "${SYM_VOR} ${COLOR_OK}robustrepair [aktion]${COLOR_RESET} # Reparatur oder Bereinigung der Robust-DB ${COLOR_BAD} experimentell${COLOR_RESET}" + log "${SYM_VOR} ${COLOR_OK}restoreRobustDump ${COLOR_RESET} # Robust Wiederherstellung${COLOR_BAD} experimentell${COLOR_RESET}" + echo " " + + #* Konfigurationsmanagement + log "${COLOR_SECTION}${SYM_CONFIG} Konfigurationsmanagement:${COLOR_RESET}" + log "${SYM_VOR} ${COLOR_WARNING}moneyserveriniconfig${COLOR_RESET} \t\t # Konfiguriert MoneyServer.ini" + log "${SYM_VOR} ${COLOR_WARNING}opensiminiconfig${COLOR_RESET} \t\t # Konfiguriert OpenSim.ini" + log "${SYM_VOR} ${COLOR_WARNING}robusthginiconfig${COLOR_RESET} \t\t # Konfiguriert Robust.HG.ini" + log "${SYM_VOR} ${COLOR_WARNING}robustiniconfig${COLOR_RESET} \t\t # Konfiguriert Robust.local.ini" + log "${SYM_VOR} ${COLOR_WARNING}gridcommoniniconfig${COLOR_RESET} \t\t # Erstellt GridCommon.ini" + log "${SYM_VOR} ${COLOR_WARNING}standalonecommoniniconfig${COLOR_RESET} \t # Erstellt StandaloneCommon.ini" + log "${SYM_VOR} ${COLOR_WARNING}flotsaminiconfig${COLOR_RESET} \t\t # Erstellt FlotsamCache.ini" + log "${SYM_VOR} ${COLOR_WARNING}osslenableiniconfig${COLOR_RESET} \t\t # Konfiguriert osslEnable.ini" + log "${SYM_VOR} ${COLOR_WARNING}welcomeiniconfig${COLOR_RESET} \t\t # Konfiguriert Begrüßungsregion" + log "${SYM_VOR} ${COLOR_WARNING}regionsiniconfig${COLOR_RESET} \t\t # Startet neue Regionen-Konfigurationen" + log "${SYM_VOR} ${COLOR_WARNING}database_set_iniconfig${COLOR_RESET} \t # Datenbank-Konfiguration setzen" + log "${SYM_VOR} ${COLOR_WARNING}oswebinterfaceconfig${COLOR_RESET} \t\t # Webinterface konfigurieren" + log "${SYM_VOR} ${COLOR_WARNING}iniconfig${COLOR_RESET} \t\t\t # Startet ALLE Konfigurationen" + echo " " + + #* INI-Operationen + log "${COLOR_SECTION}${SYM_SCRIPT} INI-Operationen:${COLOR_RESET}" + log "${SYM_VOR} ${COLOR_OK}verify_ini_section${COLOR_RESET} \t\t # INI-Abschnitt verifizieren" + log "${SYM_VOR} ${COLOR_OK}verify_ini_key${COLOR_RESET} \t\t # INI-Schlüssel verifizieren" + log "${SYM_VOR} ${COLOR_OK}add_ini_section${COLOR_RESET} \t\t # INI-Abschnitt hinzufügen" + log "${SYM_VOR} ${COLOR_OK}add_ini_before_section${COLOR_RESET} \t # INI-Abschnitt vor anderem einfügen" + log "${SYM_VOR} ${COLOR_OK}set_ini_key${COLOR_RESET} \t\t\t # INI-Schlüssel setzen" + log "${SYM_VOR} ${COLOR_OK}add_ini_key${COLOR_RESET} \t\t\t # INI-Schlüssel hinzufügen" + log "${SYM_VOR} ${COLOR_WARNING}del_ini_section${COLOR_RESET} \t\t # INI-Abschnitt löschen" + log "${SYM_VOR} ${COLOR_OK}uncomment_ini_line${COLOR_RESET} \t\t # INI-Zeile entkommentieren" + log "${SYM_VOR} ${COLOR_OK}uncomment_ini_section_line${COLOR_RESET} \t # INI-Zeile in Abschnitt entkommentieren" + log "${SYM_VOR} ${COLOR_OK}comment_ini_line${COLOR_RESET} \t\t # INI-Zeile auskommentieren" + echo " " + + #* XML-Operationen + log "${COLOR_SECTION}${SYM_SCRIPT} XML-Operationen:${COLOR_RESET}" + log "${SYM_VOR} ${COLOR_OK}verify_xml_section${COLOR_RESET} \t\t # XML-Abschnitt verifizieren" + log "${SYM_VOR} ${COLOR_OK}add_xml_section${COLOR_RESET} \t\t # XML-Abschnitt hinzufügen" + log "${SYM_VOR} ${COLOR_WARNING}del_xml_section${COLOR_RESET} \t\t # XML-Abschnitt löschen" + echo " " + + #* Experimental + log "${COLOR_SECTION}${SYM_WARNING} Experimental:${COLOR_RESET}" + log "${SYM_VOR} ${COLOR_WARNING}configure_pbr_textures${COLOR_RESET} \t # PBR-Texturen konfigurieren" + log "${SYM_VOR} ${COLOR_OK}buildopensim${COLOR_RESET} \t\t\t # OpenSim Experimental Build" + log "${SYM_VOR} ${COLOR_OK}rootrights${COLOR_RESET} \t\t\t # Root-Rechte setzen" + echo " " + + #* System-Bereinigung + log "${COLOR_SECTION}${SYM_CLEAN} Systembereinigung:${COLOR_RESET}" + log "${SYM_VOR} ${COLOR_OK}reboot${COLOR_RESET} \t\t # Linux Server neu starten" + log "${SYM_VOR} ${COLOR_OK}dataclean${COLOR_RESET} \t\t # OpenSimulator Daten bereinigen" + log "${SYM_VOR} ${COLOR_OK}pathclean${COLOR_RESET} \t\t # Pfade bereinigen" + log "${SYM_VOR} ${COLOR_OK}cacheclean${COLOR_RESET} \t\t # OpenSimulator Cache bereinigen" + log "${SYM_VOR} ${COLOR_OK}logclean${COLOR_RESET} \t\t # OpenSimulator Logs bereinigen" + log "${SYM_VOR} ${COLOR_OK}mapclean${COLOR_RESET} \t\t # OpenSimulator Maptiles bereinigen" + log "${SYM_VOR} ${COLOR_OK}autoallclean${COLOR_RESET} \t\t # Automatische Komplettbereinigung" + echo " " + log "${SYM_VOR} ${COLOR_OK}regionsclean${COLOR_RESET} \t\t # Alle Regionsdateien löschen" + log "${SYM_VOR} ${COLOR_OK}cleanall${COLOR_RESET} \t\t # Löscht OpenSim komplett" + log "${SYM_VOR} ${COLOR_OK}renamefiles${COLOR_RESET} \t\t # OpenSimulator Beispieldateien umbenennen" + log "${SYM_VOR} ${COLOR_OK}clean_linux_logs${COLOR_RESET} \t # Linux-Logs bereinigen" + log "${SYM_VOR} ${COLOR_OK}delete_opensim${COLOR_RESET} \t # OpenSimulator mit Verzeichnisse entfernen" + echo " " + + #* Hilfe + log "${COLOR_SECTION}${SYM_INFO} Hilfe:${COLOR_RESET}" + log "${SYM_VOR} ${COLOR_OK}help${COLOR_RESET} \t\t\t # Einfache Hilfeseite anzeigen" + log "${SYM_VOR} ${COLOR_OK}prohelp${COLOR_RESET} \t\t # Diese erweiterte Hilfe anzeigen" + #log "${SYM_VOR} ${COLOR_OK}generate_all_name${COLOR_RESET} \t\t # Namen generieren" + echo " " + log "${COLOR_SECTION}${SYM_INFO} Aufruf mit: bash osmtool.sh ${COLOR_RESET}" +} + +#?────────────────────────────────────────────────────────────────────────────────────────── +#* Eingabeauswertung +#?────────────────────────────────────────────────────────────────────────────────────────── + +case $KOMMANDO in + # Allgemeine Befehle # + check_screens) check_screens ;; + start|opensimstart) opensimstart ;; + stop|opensimstop) opensimstop ;; + osrestart|autorestart|restart|opensimrestart) opensimrestart ;; + simstart) simstart "$2" ;; + simstop) simstop "$2" ;; + simrestart) simrestart "$2" ;; + + # SYSTEM-CHECKS & SETUP # + servercheck) servercheck ;; + createdirectory) createdirectory ;; + setcrontab) setcrontab ;; + + # GIT-OPERATIONEN # + opensimgitcopy|opensimgit) opensimgitcopy ;; + moneygitcopy|moneygit) moneygitcopy ;; + ruthrothgit) ruthrothgit ;; + avatarassetsgit) avatarassetsgit ;; + osslscriptsgit) osslscriptsgit ;; + pbrtexturesgit) pbrtexturesgit ;; + downloadallgit) downloadallgit ;; + versionrevision) versionrevision ;; + osWebinterfacegit) osWebinterfacegit ;; + + # OPENSIM BUILD & DEPLOY # + opensimbuild) opensimbuild ;; + opensimcopy) opensimcopy ;; + opensimupgrade) opensimupgrade ;; + database_setup) database_setup ;; + setup_webserver) setup_webserver ;; + removeconfigfiles) removeconfigfiles ;; + webinstall) webinstall ;; + autoinstall) autoinstall ;; + + # KONFIGURATIONS-MGMT AUTOKONFIGURATION # + moneyserveriniconfig) moneyserveriniconfig "$2" "$3" ;; + opensiminiconfig) opensiminiconfig "$2" "$3" ;; + robusthginiconfig) robusthginiconfig "$2" "$3" ;; + robustiniconfig) robustiniconfig "$2" "$3" ;; + gridcommoniniconfig) gridcommoniniconfig "$2" "$3" ;; + standalonecommoniniconfig) standalonecommoniniconfig "$2" "$3" ;; + flotsaminiconfig) flotsaminiconfig "$2" "$3" ;; + osslenableiniconfig) osslenableiniconfig "$2" "$3" ;; + welcomeiniconfig) welcomeiniconfig "$2" "$3" ;; + database_set_iniconfig) database_set_iniconfig ;; + regionsiniconfig) regionsiniconfig ;; # Alle neuen Konfigurationen starten. + cleanconfig) clean_config "$2" ;; + createmasteruser) createmasteruser "$2" "$3" "$4" "$5" "$6" ;; + oswebinterfaceconfig) oswebinterfaceconfig ;; + firststart) firststart ;; + + # Experimental # + configure_pbr_textures) configure_pbr_textures ;; + + # INI-OPERATIONEN # + verify_ini_section) verify_ini_section "$2" "$3" "$4" ;; + verify_ini_key) verify_ini_key "$2" "$3" "$4" ;; + add_ini_section) add_ini_section "$2" "$3" ;; + add_ini_before_section) add_ini_before_section "$2" "$3" "$4" ;; + set_ini_key) set_ini_key "$2" "$3" "$4" "$5" ;; + add_ini_key) add_ini_key "$2" "$3" "$4" "$5" ;; + del_ini_section) del_ini_section "$2" "$3" ;; + uncomment_ini_line) uncomment_ini_line "$2" "$3" ;; + uncomment_ini_section_line) uncomment_ini_section_line "$2" "$3" "$4" ;; + comment_ini_line) comment_ini_line "$2" "$3" "$4" ;; + iniconfig) iniconfig ;; + + # XML-OPERATIONEN # + verify_xml_section) verify_xml_section "$2" "$3" ;; + add_xml_section) add_xml_section "$2" "$3" "$4" ;; + del_xml_section) del_xml_section "$2" "$3" ;; + + # STANDALONE-MODUS # + standalonesetup) standalonesetup ;; + standalonestart) standalonestart ;; + standalonestop) standalonestop ;; + standalonerestart) standalonerestart ;; + standalonestatus) standalonestatus ;; + + # SYSTEM-BEREINIGUNG # + reboot) reboot ;; + dataclean) dataclean ;; + pathclean) pathclean ;; + cacheclean) cacheclean ;; + logclean) logclean ;; + mapclean) mapclean ;; + autoallclean) autoallclean ;; + regionsclean) regionsclean ;; + cleanall) cleanall ;; + renamefiles) renamefiles ;; + clean_linux_logs) clean_linux_logs ;; + delete_opensim) delete_opensim ;; + + # Tests # + opensimstartParallel) opensimstartParallel ;; # Das Parallel verarbeiten kann bei großen Regionen den Server + faststop|opensimstopParallel) opensimstopParallel ;; # so belasten das er nicht mehr reagieren kann. + opensimrestartParallel) opensimrestartParallel ;; + autoupgrade) autoupgrade ;; + autoupgradefast) autoupgradefast ;; + regionbackup) regionbackup ;; + robustbackup) robustbackup ;; + robustrestore) robustrestore "$2" "$3" "$4" ;; + robustrepair) robustrepair "$2" "$3" "$4" ;; + restoreRobustDump) restoreRobustDump "$2" "$3" "$4" "$5" ;; + rootrights) rootrights ;; + buildopensim) buildopensim ;; + buildstandalone) buildstandalone ;; + setup_email_server) setup_email_server ;; + remove_ubuntu_pro) remove_ubuntu_pro ;; + simkill) simkill "$2" ;; + newservercheck) newservercheck ;; + + # Laden und Speichern von iar und oar + loadinventar) loadinventar "$2" "$3" "$4" "$5" ;; #loadinventar MyInventory mydirectory mypassword myinventory.iar + saveinventar) saveinventar "$2" "$3" "$4" "$5" ;; #saveinventar "MeinInventar" "/pfad/zum/verzeichnis" "geheimesPasswort" "inventar.iar" + loadoar) loadoar "$2" "$3" "$4" ;; #loadoar "RestoreScreen" "MyRegion" "/pfad/zum/verzeichnis" + saveoar) saveoar "$2" "$3" "$4" ;; #saveoar "RestoreScreen" "MyRegion" "/pfad/zum/verzeichnis" + autoregionbackup) autoregionbackup ;; + + # HILFE & SONSTIGES # + generate_all_name) generate_all_name ;; + prohelp) prohelp ;; + h|help|hilfe|*) help ;; +esac + +# Programm Ende mit Zeitstempel +blankline +log "\e[36m${SCRIPTNAME}\e[0m ${VERSION} wurde beendet $(date +'%Y-%m-%d %H:%M:%S')" >&2 +exit 0 + +# Ein Metaversum sind viele kleine Räume, die nahtlos aneinander passen, sowie direkt sichtbar und begehbar sind, als wäre es aus einem Guss. +# Ein Metaversum besteht aus vielen kleinen, nahtlos verbundenen Räumen, die direkt sichtbar und begehbar sind – als wäre alles aus einem Guss erschaffen. +# Das Metaversum setzt sich aus zahlreichen virtuellen Räumen zusammen, die ohne Ladezeiten oder Übergänge miteinander verbunden sind und sich wie eine zusammenhängende Welt erleben lassen. diff --git a/osmtool_backup.log b/osmtool_backup.log new file mode 100644 index 0000000..798641c --- /dev/null +++ b/osmtool_backup.log @@ -0,0 +1,632 @@ +[2026-04-01 10:10:40] [ERROR] ERR_TARGET_NOT_ALLOWED: profile=robust target=sim1 +[2026-04-01 10:10:40] [ERROR] ERR_TARGET_NOT_ALLOWED: profile=standalone target=grid +[2026-04-01 10:10:40] [ERROR] ERR_ACTION_NOT_ALLOWED: module=backup profile=robust action=oar-backup +[2026-04-01 10:11:16] [ERROR] Target not allowed for this profile : profile=robust target=sim1 +[2026-04-01 10:12:25] [ERROR] Janus binary missing: /opt/janus/bin/janus. Run install module action compile-janus/install-janus first. +[2026-04-01 10:13:33] [INFO] Using workdir: /opt +[2026-04-01 10:13:33] [INFO] Using profile: grid-sim +[2026-04-01 10:13:33] [INFO] Janus prefix: /opt/janus +[2026-04-01 10:13:33] [INFO] Installing Janus build dependencies +[2026-04-01 10:14:42] [INFO] Cloning Janus source to /opt/janus-gateway +[2026-04-01 10:15:05] [INFO] Using workdir: /opt +[2026-04-01 10:15:05] [INFO] Using profile: grid-sim +[2026-04-01 10:15:05] [INFO] Janus prefix: /opt/janus +[2026-04-01 10:15:05] [INFO] Installing Janus build dependencies +[2026-04-01 10:15:49] [INFO] Cloning Janus source to /home/manni/janus-gateway +[2026-04-01 10:15:54] [INFO] Building Janus +[2026-04-01 10:16:20] [INFO] Janus compiled and installed to /opt/janus +[2026-04-01 10:17:02] [INFO] Using workdir: /opt +[2026-04-01 10:17:02] [INFO] Using profile: grid-sim +[2026-04-01 10:17:02] [INFO] Janus prefix: /opt/janus +[2026-04-01 10:17:41] [INFO] Using workdir: /home/manni +[2026-04-01 10:17:41] [INFO] Using profile: grid-sim +[2026-04-01 10:17:41] [INFO] Janus prefix: /opt/janus +[2026-04-01 10:18:19] [INFO] Using workdir: /opt +[2026-04-01 10:18:19] [INFO] Using profile: grid-sim +[2026-04-01 10:18:19] [INFO] Janus prefix: /opt/janus +[2026-04-01 10:18:29] [INFO] Janus configured. Marker created: /opt/janus/.osmtool_janus_ready +[2026-04-01 10:18:39] [INFO] Using profile: grid-sim +[2026-04-01 10:18:39] [ERROR] Janus control not configured. Expected system service or janus.sh helper. +[2026-04-01 10:18:40] [INFO] Using profile: grid-sim +[2026-04-01 10:18:40] [ERROR] Janus control not configured. Expected system service or janus.sh helper. +[2026-04-01 10:19:10] [INFO] Using profile: grid-sim +[2026-04-01 10:19:10] [INFO] Janus already running +[2026-04-01 10:19:11] [INFO] Using profile: grid-sim +[2026-04-01 10:19:18] [INFO] Using profile: grid-sim +[2026-04-01 10:19:43] [INFO] Using profile: grid-sim +[2026-04-01 10:19:43] [INFO] Janus already running +[2026-04-01 10:19:43] [INFO] Using profile: grid-sim +[2026-04-01 10:19:55] [INFO] Using profile: grid-sim +[2026-04-01 10:19:55] [INFO] Janus already running +[2026-04-01 10:19:55] [INFO] Using profile: grid-sim +[2026-04-01 10:20:05] [INFO] Using profile: grid-sim +[2026-04-01 10:20:41] [INFO] Using profile: grid-sim +[2026-04-01 10:24:45] [INFO] Using workdir: /opt +[2026-04-01 10:24:45] [INFO] Using profile: grid-sim +[2026-04-01 10:24:45] [INFO] Janus prefix: /opt/janus +[2026-04-01 10:24:45] [INFO] Running server readiness check +[2026-04-01 10:24:45] [INFO] Detected OS: Ubuntu 24.04.4 LTS +[2026-04-01 10:24:45] [INFO] sudo available but password will be required +[2026-04-01 10:24:45] [INFO] apt-get available +[2026-04-01 10:24:45] [INFO] Network check to github.com: OK +[2026-04-01 10:24:45] [INFO] Disk space in /opt is sufficient +[2026-04-01 10:24:45] [INFO] All required Janus build packages are installed +[2026-04-01 10:24:45] [INFO] dotnet not found (required later for OpenSim runtime) +[2026-04-01 10:24:45] [INFO] Server readiness check PASSED +[2026-04-01 10:29:20] [INFO] Using workdir: /opt +[2026-04-01 10:29:20] [INFO] Using profile: grid-sim +[2026-04-01 10:29:20] [INFO] Janus prefix: /opt/janus +[2026-04-01 10:29:20] [INFO] Running server readiness check +[2026-04-01 10:29:20] [INFO] Detected OS: Ubuntu 24.04.4 LTS +[2026-04-01 10:29:20] [INFO] sudo available but password will be required +[2026-04-01 10:29:20] [INFO] apt-get available +[2026-04-01 10:29:20] [INFO] Network check to github.com: OK +[2026-04-01 10:29:20] [INFO] Disk space in /opt is sufficient +[2026-04-01 10:29:20] [INFO] All required Janus build packages are installed +[2026-04-01 10:29:20] [ERROR] dotnet SDK 8.x missing (required for OpenSim build/runtime) +[2026-04-01 10:29:20] [ERROR] Server readiness check FAILED +[2026-04-01 10:38:11] [INFO] Using workdir: /opt +[2026-04-01 10:38:11] [INFO] Using profile: grid-sim +[2026-04-01 10:38:11] [INFO] Janus prefix: /opt/janus +[2026-04-01 10:38:11] [INFO] Running server readiness check +[2026-04-01 10:38:11] [INFO] Detected OS: Ubuntu 24.04.4 LTS +[2026-04-01 10:38:11] [INFO] sudo available but password will be required +[2026-04-01 10:38:11] [INFO] apt-get available +[2026-04-01 10:38:12] [INFO] Network check to github.com: OK +[2026-04-01 10:38:12] [INFO] Disk space in /opt is sufficient +[2026-04-01 10:38:12] [ERROR] Missing packages: zip screen libgdiplus +[2026-04-01 10:38:12] [INFO] Run: sudo apt-get update && sudo apt-get install -y zip screen libgdiplus +[2026-04-01 10:38:12] [ERROR] dotnet SDK 8.x missing (required for OpenSim build/runtime) +[2026-04-01 10:38:12] [ERROR] Server readiness check FAILED +[2026-04-01 10:39:56] [INFO] Using workdir: /opt +[2026-04-01 10:39:56] [INFO] Using profile: grid-sim +[2026-04-01 10:39:56] [INFO] Janus prefix: /opt/janus +[2026-04-01 10:39:56] [ERROR] No installable package found for token: zip +[2026-04-01 10:40:15] [INFO] Using workdir: /opt +[2026-04-01 10:40:15] [INFO] Using profile: grid-sim +[2026-04-01 10:40:15] [INFO] Janus prefix: /opt/janus +[2026-04-01 10:40:15] [INFO] Installing OpenSim dependencies: zip screen libgdiplus libssl3 liblttng-ust1 +[2026-04-01 10:40:59] [INFO] Using workdir: /opt +[2026-04-01 10:40:59] [INFO] Using profile: grid-sim +[2026-04-01 10:40:59] [INFO] Janus prefix: /opt/janus +[2026-04-01 10:40:59] [INFO] Installing .NET SDK 8.0 +[2026-04-01 10:41:47] [INFO] Adding Microsoft package repository for Ubuntu 24.04 +[2026-04-01 10:42:05] [INFO] dotnet SDK 8.x installed successfully +[2026-04-01 10:42:11] [INFO] Using workdir: /opt +[2026-04-01 10:42:11] [INFO] Using profile: grid-sim +[2026-04-01 10:42:11] [INFO] Janus prefix: /opt/janus +[2026-04-01 10:42:11] [INFO] Running server readiness check +[2026-04-01 10:42:11] [INFO] Detected OS: Ubuntu 24.04.4 LTS +[2026-04-01 10:42:11] [INFO] sudo available but password will be required +[2026-04-01 10:42:11] [INFO] apt-get available +[2026-04-01 10:42:11] [INFO] Network check to github.com: OK +[2026-04-01 10:42:11] [INFO] Disk space in /opt is sufficient +[2026-04-01 10:42:11] [INFO] All required Janus/OpenSim dependency packages are installed +[2026-04-01 10:42:11] [INFO] dotnet SDK 8.x found +[2026-04-01 10:42:11] [INFO] Server readiness check PASSED +[2026-04-01 10:50:47] [ERROR] Server preinstallation missing. Run install action bootstrap-server first. +[2026-04-01 10:50:47] [INFO] Using workdir: /opt +[2026-04-01 10:50:47] [INFO] Using profile: grid-sim +[2026-04-01 10:50:47] [INFO] Janus prefix: /opt/janus +[2026-04-01 10:50:47] [INFO] Starting central server bootstrap +[2026-04-01 10:50:47] [INFO] Preparing Ubuntu server +[2026-04-01 10:51:13] [INFO] Ubuntu base packages installed +[2026-04-01 10:51:13] [INFO] Installing OpenSim dependencies: libssl3 liblttng-ust1 +[2026-04-01 10:51:14] [INFO] dotnet SDK 8.x already installed +[2026-04-01 10:51:14] [INFO] Running server readiness check +[2026-04-01 10:51:14] [INFO] Detected OS: Ubuntu 24.04.4 LTS +[2026-04-01 10:51:14] [INFO] sudo non-interactive check: OK +[2026-04-01 10:51:14] [INFO] apt-get available +[2026-04-01 10:51:14] [INFO] Network check to github.com: OK +[2026-04-01 10:51:14] [INFO] Disk space in /opt is sufficient +[2026-04-01 10:51:15] [INFO] All required Janus/OpenSim dependency packages are installed +[2026-04-01 10:51:15] [INFO] dotnet SDK 8.x found +[2026-04-01 10:51:15] [INFO] Server readiness check PASSED +[2026-04-01 10:51:15] [INFO] Server bootstrap marker created: /opt/.osmtool_server_prepared +[2026-04-01 10:51:15] [INFO] Central server bootstrap completed +[2026-04-01 10:57:07] [INFO] Using workdir: /opt +[2026-04-01 10:57:07] [INFO] Using profile: grid-sim +[2026-04-01 10:57:07] [INFO] Repo mode: update +[2026-04-01 10:57:07] [INFO] OpenSim dir: /opt/opensim +[2026-04-01 10:57:07] [INFO] Janus prefix: /opt/janus +[2026-04-01 10:57:07] [INFO] Starting OpenSim install/build flow +[2026-04-01 10:57:07] [INFO] OpenSim repository: https://github.com/opensim/opensim.git +[2026-04-01 10:57:07] [INFO] OpenSim branch: master +[2026-04-01 10:57:07] [INFO] OpenSim directory: /opt/opensim +[2026-04-01 10:57:07] [INFO] Cloning repository https://github.com/opensim/opensim.git -> /opt/opensim +[2026-04-01 10:57:30] [INFO] Using workdir: /opt +[2026-04-01 10:57:30] [INFO] Using profile: grid-sim +[2026-04-01 10:57:30] [INFO] Repo mode: update +[2026-04-01 10:57:30] [INFO] OpenSim dir: /opt/opensim +[2026-04-01 10:57:30] [INFO] Janus prefix: /opt/janus +[2026-04-01 10:57:30] [INFO] Starting OpenSim install/build flow +[2026-04-01 10:57:30] [INFO] OpenSim repository: https://github.com/opensim/opensim.git +[2026-04-01 10:57:31] [INFO] OpenSim branch: master +[2026-04-01 10:57:31] [INFO] OpenSim directory: /opt/opensim +[2026-04-01 11:08:23] [INFO] Cloning repository https://github.com/opensim/opensim.git -> /opt/opensim +[2026-04-01 11:09:12] [INFO] Preparing support repository: opensim-tsassets +[2026-04-01 11:09:12] [INFO] Cloning repository https://github.com/ManfredAabye/opensim-tsassets.git -> /opt/opensim-tsassets +[2026-04-01 11:09:44] [INFO] Using workdir: /opt +[2026-04-01 11:09:44] [INFO] Using profile: grid-sim +[2026-04-01 11:09:44] [INFO] Repo mode: update +[2026-04-01 11:09:44] [INFO] OpenSim dir: /opt/opensim +[2026-04-01 11:09:44] [INFO] Janus prefix: /opt/janus +[2026-04-01 11:09:44] [INFO] Starting OpenSim install/build flow +[2026-04-01 11:09:44] [INFO] OpenSim repository: https://github.com/opensim/opensim.git +[2026-04-01 11:09:44] [INFO] OpenSim branch: master +[2026-04-01 11:09:44] [INFO] OpenSim directory: /opt/opensim +[2026-04-01 11:09:44] [INFO] Updating repository: /opt/opensim +[2026-04-01 11:09:46] [INFO] Preparing support repository: opensim-tsassets +[2026-04-01 11:12:10] [INFO] Cloning repository https://github.com/ManfredAabye/opensim-tsassets.git -> /opt/opensim-tsassets +[2026-04-01 11:12:10] [INFO] Preparing support repository: opensimcurrencyserver +[2026-04-01 11:12:10] [INFO] Cloning repository https://github.com/ManfredAabye/opensimcurrencyserver-dotnet.git -> /opt/opensimcurrencyserver +[2026-04-01 11:12:11] [INFO] Preparing support repository: os-data-backup +[2026-04-01 11:12:11] [INFO] Cloning repository https://github.com/ManfredAabye/os-data-backup.git -> /opt/os-data-backup +[2026-04-01 11:12:12] [INFO] Copied tsassets bin content: /opt/opensim-tsassets/bin -> /opt/opensim/bin +[2026-04-01 11:12:12] [INFO] Copied tsassets OpenSim content: /opt/opensim-tsassets/OpenSim -> /opt/opensim/OpenSim +[2026-04-01 11:12:12] [INFO] Copied currency addon modules: /opt/opensimcurrencyserver/addon-modules -> /opt/opensim/addon-modules +[2026-04-01 11:12:12] [INFO] Copied currency bin content: /opt/opensimcurrencyserver/bin -> /opt/opensim/bin +[2026-04-01 11:12:12] [INFO] Copied os-data-backup content: /opt/os-data-backup -> /opt/opensim/addon-modules/os-data-backup +[2026-04-01 11:12:12] [INFO] Running OpenSim prebuild +[2026-04-01 11:12:32] [INFO] Binary deployment skipped by configuration +[2026-04-01 11:12:32] [INFO] OpenSim install/build flow completed +[2026-04-01 11:53:03] [INFO] Using workdir: /opt +[2026-04-01 11:53:03] [INFO] Using profile: grid-sim +[2026-04-01 11:53:03] [INFO] Repo mode: update +[2026-04-01 11:53:03] [INFO] OpenSim dir: /opt/opensim +[2026-04-01 11:53:03] [INFO] Janus prefix: /opt/janus +[2026-04-01 11:53:03] [INFO] Starting OpenSim install/build flow +[2026-04-01 11:53:03] [INFO] OpenSim repository: https://github.com/opensim/opensim.git +[2026-04-01 11:53:03] [INFO] OpenSim branch: master +[2026-04-01 11:53:03] [INFO] OpenSim directory: /opt/opensim +[2026-04-01 11:53:03] [INFO] Updating repository: /opt/opensim +[2026-04-01 11:53:04] [INFO] Preparing support repository: opensim-tsassets +[2026-04-01 11:53:04] [INFO] Updating repository: /opt/opensim-tsassets +[2026-04-01 11:53:05] [INFO] Preparing support repository: opensimcurrencyserver +[2026-04-01 11:53:05] [INFO] Updating repository: /opt/opensimcurrencyserver +[2026-04-01 11:53:06] [INFO] Preparing support repository: os-data-backup +[2026-04-01 11:53:06] [INFO] Updating repository: /opt/os-data-backup +[2026-04-01 11:53:07] [INFO] Copied tsassets bin content: /opt/opensim-tsassets/bin -> /opt/opensim/bin +[2026-04-01 11:53:07] [INFO] Copied tsassets OpenSim content: /opt/opensim-tsassets/OpenSim -> /opt/opensim/OpenSim +[2026-04-01 11:53:07] [INFO] Copied currency addon modules: /opt/opensimcurrencyserver/addon-modules -> /opt/opensim/addon-modules +[2026-04-01 11:53:07] [INFO] Copied currency bin content: /opt/opensimcurrencyserver/bin -> /opt/opensim/bin +[2026-04-01 11:55:36] [INFO] Using workdir: /opt +[2026-04-01 11:55:36] [INFO] Using profile: grid-sim +[2026-04-01 11:55:36] [INFO] Repo mode: update +[2026-04-01 11:55:36] [INFO] OpenSim dir: /opt/opensim +[2026-04-01 11:55:36] [INFO] Janus prefix: /opt/janus +[2026-04-01 11:55:36] [INFO] Starting OpenSim install/build flow +[2026-04-01 11:55:36] [INFO] OpenSim repository: https://github.com/opensim/opensim.git +[2026-04-01 11:55:36] [INFO] OpenSim branch: master +[2026-04-01 11:55:36] [INFO] OpenSim directory: /opt/opensim +[2026-04-01 11:55:36] [INFO] Updating repository: /opt/opensim +[2026-04-01 11:55:37] [INFO] Preparing support repository: opensim-tsassets +[2026-04-01 11:55:37] [INFO] Updating repository: /opt/opensim-tsassets +[2026-04-01 11:55:38] [INFO] Preparing support repository: opensimcurrencyserver +[2026-04-01 11:55:38] [INFO] Updating repository: /opt/opensimcurrencyserver +[2026-04-01 11:55:38] [INFO] Preparing support repository: os-data-backup +[2026-04-01 11:55:38] [INFO] Updating repository: /opt/os-data-backup +[2026-04-01 11:55:39] [INFO] Copied tsassets bin content: /opt/opensim-tsassets/bin -> /opt/opensim/bin +[2026-04-01 11:55:39] [INFO] Copied tsassets OpenSim content: /opt/opensim-tsassets/OpenSim -> /opt/opensim/OpenSim +[2026-04-01 11:55:39] [INFO] Copied currency addon modules: /opt/opensimcurrencyserver/addon-modules -> /opt/opensim/addon-modules +[2026-04-01 11:55:39] [INFO] Copied currency bin content: /opt/opensimcurrencyserver/bin -> /opt/opensim/bin +[2026-04-01 11:55:39] [INFO] Copied os-data-backup content: /opt/os-data-backup -> /opt/opensim/addon-modules/os-data-backup +[2026-04-01 11:55:39] [INFO] Running OpenSim prebuild +[2026-04-01 11:55:59] [INFO] Copied robust binaries: /opt/opensim/bin -> /opt/robust/bin +[2026-04-01 11:55:59] [INFO] Copied region binaries: /opt/opensim/bin -> /opt/sim1/bin +[2026-04-01 11:55:59] [INFO] Copied region binaries: /opt/opensim/bin -> /opt/sim2/bin +[2026-04-01 11:55:59] [INFO] OpenSim binaries deployed to available runtime directories +[2026-04-01 11:55:59] [INFO] OpenSim install/build flow completed +[2026-04-01 12:07:30] [INFO] Using profile: grid-sim +[2026-04-01 12:07:32] [INFO] Using profile: grid-sim +[2026-04-01 12:13:24] [INFO] Using profile: grid-sim +[2026-04-01 12:28:52] [INFO] Using profile: grid-sim +[2026-04-01 12:28:52] [ERROR] RobustServer cannot start: missing /opt/robust/bin/Robust.ini — run the config action first +[2026-04-01 12:28:52] [INFO] Using profile: grid-sim +[2026-04-01 12:28:52] [ERROR] sim1 cannot start: missing /opt/sim1/bin/OpenSim.ini — run the config action first +[2026-04-01 12:29:05] [INFO] Using profile: grid-sim +[2026-04-01 12:29:05] [INFO] Starting robustserver from /opt/robust/bin +[2026-04-01 12:29:06] [INFO] Screen session 'robustserver' started successfully +[2026-04-01 12:29:08] [INFO] Using profile: grid-sim +[2026-04-01 12:30:22] [INFO] Using profile: grid-sim +[2026-04-01 12:30:22] [INFO] Starting robustserver from /opt/robust/bin +[2026-04-01 12:30:22] [INFO] Screen session 'robustserver' started successfully +[2026-04-01 12:30:23] [INFO] Using profile: grid-sim +[2026-04-01 12:30:23] [INFO] Sent shutdown to robustserver +[2026-04-01 12:30:24] [INFO] Using profile: grid-sim +[2026-04-01 12:30:24] [INFO] Sent shutdown to robustserver +[2026-04-01 12:30:24] [INFO] Using profile: grid-sim +[2026-04-01 12:30:24] [ERROR] sim1 cannot start: missing /opt/sim1/bin/OpenSim.ini — run the config action first +[2026-04-01 12:49:19] [INFO] Using profile: grid-sim +[2026-04-01 12:49:19] [ERROR] Missing required command: mysqldump +[2026-04-01 12:49:31] [INFO] Using profile: grid-sim +[2026-04-01 12:49:31] [INFO] No OSMTool cron block configured +[2026-04-01 12:49:32] [INFO] OSMTool cron jobs installed/updated +[2026-04-01 12:49:32] [INFO] OSMTool cron block removed +[2026-04-01 12:49:32] [INFO] No OSMTool cron block configured +[2026-04-01 12:49:58] [INFO] Using profile: grid-sim +[2026-04-01 12:50:34] [INFO] Using profile: grid-sim +[2026-04-01 12:50:34] [INFO] Sending OAR export command to screen session 'sim1': save oar /opt/backup/sim1_20260401_125034.oar +[2026-04-01 12:50:34] [INFO] OAR export command sent. Validate completion in the session log/output. +[2026-04-01 12:52:12] [INFO] OSMTool cron jobs installed/updated +[2026-04-01 12:52:13] [INFO] OSMTool cron block removed +[2026-04-01 12:52:14] [INFO] Using profile: grid-sim +[2026-04-01 12:52:14] [INFO] Sending OAR export command to screen session 'sim1': save oar /opt/backup/sim1_20260401_125214.oar +[2026-04-01 12:52:14] [INFO] OAR export command sent. Validate completion in the session log/output. +[2026-04-01 13:01:08] [INFO] OSMTool cron jobs installed/updated +[2026-04-01 13:01:08] [INFO] OSMTool cron block removed +[2026-04-01 13:01:09] [INFO] Using workdir: /opt +[2026-04-01 13:01:09] [INFO] Using profile: grid-sim +[2026-04-01 13:01:09] [INFO] Repo mode: update +[2026-04-01 13:01:09] [INFO] OpenSim dir: /opt/opensim +[2026-04-01 13:01:09] [INFO] Janus prefix: /opt/janus +[2026-04-01 13:01:09] [INFO] Running server readiness check +[2026-04-01 13:01:09] [INFO] Detected OS: Ubuntu 24.04.4 LTS +[2026-04-01 13:01:09] [INFO] sudo available but password will be required +[2026-04-01 13:01:09] [INFO] apt-get available +[2026-04-01 13:01:09] [INFO] Network check to github.com: OK +[2026-04-01 13:01:09] [INFO] Disk space in /opt is sufficient +[2026-04-01 13:01:09] [INFO] All required Janus/OpenSim dependency packages are installed +[2026-04-01 13:01:09] [INFO] dotnet SDK 8.x found +[2026-04-01 13:01:09] [ERROR] mysqldump missing (MariaDB client tools are required) +[2026-04-01 13:01:09] [ERROR] Server readiness check FAILED +[2026-04-01 13:01:41] [INFO] Using workdir: /opt +[2026-04-01 13:01:41] [INFO] Using profile: grid-sim +[2026-04-01 13:01:41] [INFO] Repo mode: update +[2026-04-01 13:01:41] [INFO] OpenSim dir: /opt/opensim +[2026-04-01 13:01:41] [INFO] Janus prefix: /opt/janus +[2026-04-01 13:01:41] [INFO] Running server readiness check +[2026-04-01 13:01:41] [INFO] Detected OS: Ubuntu 24.04.4 LTS +[2026-04-01 13:01:41] [INFO] sudo available but password will be required +[2026-04-01 13:01:41] [INFO] apt-get available +[2026-04-01 13:01:41] [INFO] Network check to github.com: OK +[2026-04-01 13:01:41] [INFO] Disk space in /opt is sufficient +[2026-04-01 13:01:41] [INFO] All required Janus/OpenSim dependency packages are installed +[2026-04-01 13:01:41] [INFO] dotnet SDK 8.x found +[2026-04-01 13:01:41] [INFO] mysqldump found (MariaDB backup tooling ready) +[2026-04-01 13:01:41] [INFO] Server readiness check PASSED +[2026-04-01 13:04:31] [INFO] Using workdir: /opt +[2026-04-01 13:04:31] [INFO] Using profile: grid-sim +[2026-04-01 13:04:31] [INFO] Repo mode: update +[2026-04-01 13:04:31] [INFO] OpenSim dir: /opt/opensim +[2026-04-01 13:04:31] [INFO] Janus prefix: /opt/janus +[2026-04-01 13:04:31] [INFO] Configuring OpenSim runtime ini files +[2026-04-01 13:04:31] [INFO] Keeping existing config: /opt/robust/bin/Robust.ini +[2026-04-01 13:04:31] [INFO] Keeping existing config: /opt/robust/bin/Robust.ini +[2026-04-01 13:04:31] [INFO] Created config from template: /opt/robust/bin/MoneyServer.ini +[2026-04-01 13:04:31] [INFO] Created config from template: /opt/sim1/bin/OpenSim.ini +[2026-04-01 13:04:31] [INFO] Created config from template: /opt/sim2/bin/OpenSim.ini +[2026-04-01 13:04:31] [INFO] OpenSim runtime ini configuration completed +[2026-04-01 13:04:37] [INFO] Using workdir: /opt +[2026-04-01 13:04:37] [INFO] Using profile: grid-sim +[2026-04-01 13:04:37] [INFO] Repo mode: update +[2026-04-01 13:04:37] [INFO] OpenSim dir: /opt/opensim +[2026-04-01 13:04:37] [INFO] Janus prefix: /opt/janus +[2026-04-01 13:04:37] [INFO] Starting central server bootstrap +[2026-04-01 13:04:37] [INFO] Preparing Ubuntu server +[2026-04-01 13:07:46] [INFO] Using workdir: /opt +[2026-04-01 13:07:46] [INFO] Using profile: grid-sim +[2026-04-01 13:07:46] [INFO] Repo mode: update +[2026-04-01 13:07:46] [INFO] OpenSim dir: /opt/opensim +[2026-04-01 13:07:46] [INFO] Janus prefix: /opt/janus +[2026-04-01 13:07:46] [INFO] Starting central server bootstrap +[2026-04-01 13:07:46] [INFO] Preparing Ubuntu server +[2026-04-01 13:07:50] [INFO] Ubuntu base packages installed +[2026-04-01 13:07:51] [INFO] Installing OpenSim dependencies: mariadb-server libssl3 liblttng-ust1 +[2026-04-01 13:07:59] [INFO] dotnet SDK 8.x already installed +[2026-04-01 13:07:59] [INFO] No controllable MariaDB service unit detected; skipping service activation +[2026-04-01 13:07:59] [INFO] Running server readiness check +[2026-04-01 13:07:59] [INFO] Detected OS: Ubuntu 24.04.4 LTS +[2026-04-01 13:07:59] [INFO] sudo non-interactive check: OK +[2026-04-01 13:07:59] [INFO] apt-get available +[2026-04-01 13:08:00] [INFO] Network check to github.com: OK +[2026-04-01 13:08:00] [INFO] Disk space in /opt is sufficient +[2026-04-01 13:08:00] [INFO] All required Janus/OpenSim dependency packages are installed +[2026-04-01 13:08:00] [INFO] dotnet SDK 8.x found +[2026-04-01 13:08:00] [INFO] mysqldump found (MariaDB backup tooling ready) +[2026-04-01 13:08:00] [INFO] Server readiness check PASSED +[2026-04-01 13:08:00] [INFO] Server bootstrap marker created: /opt/.osmtool_server_prepared +[2026-04-01 13:08:00] [INFO] Central server bootstrap completed +[2026-04-01 15:41:57] [INFO] Using workdir: /opt +[2026-04-01 15:41:57] [INFO] Using profile: grid-sim +[2026-04-01 15:41:57] [INFO] Repo mode: update +[2026-04-01 15:41:57] [INFO] OpenSim dir: /opt/opensim +[2026-04-01 15:41:57] [INFO] Janus prefix: /opt/janus +[2026-04-01 15:41:57] [INFO] No controllable MariaDB service unit detected; skipping service activation +[2026-04-01 15:41:57] [INFO] Configuring MariaDB databases and grants +[2026-04-01 15:41:57] [INFO] Ensured database: robust +[2026-04-01 15:41:57] [INFO] Ensured database: sim1 +[2026-04-01 15:41:57] [INFO] Ensured database: sim2 +[2026-04-01 15:41:57] [INFO] Database credentials written: /opt/UserInfo.ini +[2026-04-01 15:41:57] [INFO] MariaDB database setup completed +[2026-04-01 15:46:48] [INFO] Running health checks for profile: grid-sim +[2026-04-01 15:46:48] [INFO] Using workdir: /opt +[2026-04-01 15:46:48] [ERROR] [FAIL] Screen session missing: robustserver +[2026-04-01 15:46:48] [ERROR] [FAIL] Screen session missing: sim1 +[2026-04-01 15:46:48] [ERROR] [FAIL] Port not listening: 8002 +[2026-04-01 15:46:48] [ERROR] [FAIL] Port not listening: 9000 +[2026-04-01 15:46:48] [ERROR] [FAIL] Port not listening: 9001 +[2026-04-01 15:46:48] [ERROR] [FAIL] Port not listening: 8088 +[2026-04-01 15:46:48] [INFO] [OK] Database reachable: robust +[2026-04-01 15:46:48] [INFO] [OK] Log scan: /mnt/d/OpenSimBash/opensimMULTITOOLS-II/osmtool_backup.log -> 26 matches +[2026-04-01 15:46:48] [INFO] [WARN] Log file not found, skipped: /opt/robust/bin/OpenSim.log +[2026-04-01 15:46:48] [INFO] [OK] Log warning threshold OK: 26 <= 200 +[2026-04-01 15:46:48] [ERROR] [FAIL] Healthcheck FAILED with 2 failing check group(s) +[2026-04-01 15:47:31] [INFO] Running health checks for profile: grid-sim +[2026-04-01 15:47:31] [INFO] Using workdir: /opt +[2026-04-01 15:47:31] [INFO] [WARN] Screen check skipped (no expected screens configured) +[2026-04-01 15:47:31] [INFO] [WARN] Port check skipped (no ports configured) +[2026-04-01 15:47:31] [INFO] [OK] Database reachable: robust +[2026-04-01 15:47:31] [INFO] [OK] Log scan: /mnt/d/OpenSimBash/opensimMULTITOOLS-II/osmtool_backup.log -> 31 matches +[2026-04-01 15:47:31] [INFO] [WARN] Log file not found, skipped: /opt/robust/bin/OpenSim.log +[2026-04-01 15:47:31] [INFO] [OK] Log warning threshold OK: 31 <= 200 +[2026-04-01 15:47:31] [INFO] [OK] Healthcheck PASSED +[2026-04-01 15:47:56] [INFO] Using profile: grid-sim +[2026-04-01 15:47:56] [INFO] Dry-run mode: true +[2026-04-01 15:47:56] [INFO] Cleaning log files under /opt/opensim +[2026-04-01 15:48:14] [ERROR] Refusing cleanup on /opt root; choose a dedicated OpenSim workdir +[2026-04-01 16:31:39] [ERROR] Unknown option: --module +[2026-04-01 16:31:45] [INFO] Using profile: grid-sim +[2026-04-01 16:31:45] [INFO] SQL Backups: +[2026-04-01 16:31:45] [INFO] OAR Backups: +[2026-04-01 16:31:50] [INFO] Using profile: grid-sim +[2026-04-01 16:31:50] [INFO] SQL Backups: +[2026-04-01 16:31:50] [INFO] OAR Backups: +[2026-04-01 16:31:55] [INFO] Using profile: grid-sim +[2026-04-01 16:31:55] [ERROR] SQL backup file not found: /tmp/test.sql +[2026-04-01 16:32:02] [INFO] Using profile: grid-sim +[2026-04-01 16:32:02] [INFO] Database restore command: +[2026-04-01 16:32:02] [INFO] mysql -u'root' -p'test' 'robust' < '/tmp/test.sql' +[2026-04-01 16:32:02] [INFO] [DRY-RUN] Not executing database restore +[2026-04-01 16:32:09] [ERROR] Aktion fuer dieses Profil nicht erlaubt : module=restore profile=robust action=oar-restore +[2026-04-01 16:57:43] [INFO] Using profile: grid-sim +[2026-04-01 16:57:43] [INFO] Creating rollback archive: /tmp/osm_update_test/rollback/opensim_20260401_165743_smoke.tar.gz +[2026-04-01 16:57:43] [INFO] [DRY-RUN] Would archive paths listed in: /tmp/osm_update_test/rollback/opensim_20260401_165743_smoke.manifest +[2026-04-01 16:57:43] [INFO] Creating rollback archive: /tmp/osm_update_test/rollback/janus_20260401_165743_smoke.tar.gz +[2026-04-01 16:57:43] [INFO] [DRY-RUN] Would archive paths listed in: /tmp/osm_update_test/rollback/janus_20260401_165743_smoke.manifest +[2026-04-01 16:57:43] [INFO] Using profile: grid-sim +[2026-04-01 16:57:43] [INFO] Validating INI file: /tmp/osm_update_test/robust/bin/Robust.ini +[2026-04-01 16:57:43] [INFO] Validating INI file: /tmp/osm_update_test/sim1/bin/OpenSim.ini +[2026-04-01 16:57:43] [INFO] Configuration validation passed +[2026-04-01 16:57:53] [INFO] Using profile: grid-sim +[2026-04-01 16:57:53] [INFO] Creating rollback archive: /tmp/osm_update_test/rollback/opensim_20260401_165753_smoke.tar.gz +[2026-04-01 16:57:53] [INFO] [DRY-RUN] Would archive paths listed in: /tmp/osm_update_test/rollback/opensim_20260401_165753_smoke.manifest +[2026-04-01 16:57:53] [INFO] Delegating update to install action: install-opensim +[2026-04-01 16:57:53] [INFO] [DRY-RUN] /mnt/d/OpenSimBash/opensimMULTITOOLS-II/modular/osmtool_install.sh --profile grid-sim --workdir /tmp/osm_update_test --action install-opensim --opensim-dir /tmp/osm_update_test/opensim --repo-mode update --deploy-binaries true --janus-prefix /opt/janus --janus-src /tmp/osm_update_test/janus-gateway +[2026-04-01 16:57:54] [INFO] Using profile: grid-sim +[2026-04-01 16:57:54] [INFO] Validating INI file: /tmp/test_invalid.ini +[2026-04-01 16:57:54] [ERROR] Invalid INI syntax in /tmp/test_invalid.ini:2 -> this is broken +[2026-04-01 16:57:54] [ERROR] Configuration validation failed with 1 issue(s) +[2026-04-01 16:58:09] [INFO] Using profile: grid-sim +[2026-04-01 16:58:09] [INFO] Validating JSON file: /tmp/test_valid.json +[2026-04-01 16:58:09] [INFO] Configuration validation passed +[2026-04-01 16:58:09] [INFO] Using profile: grid-sim +[2026-04-01 16:58:46] [INFO] Using profile: grid-sim +[2026-04-01 16:58:46] [INFO] Validating INI file: /tmp/osm_config_missing/robust/bin/Robust.ini +[2026-04-01 16:58:46] [ERROR] No region runtime configs found under /tmp/osm_config_missing/sim*/bin/OpenSim.ini +[2026-04-01 16:58:46] [ERROR] Configuration validation failed with 1 issue(s) +[2026-04-01 16:58:47] [INFO] Using profile: grid-sim +[2026-04-01 16:58:47] [ERROR] Missing config file: /tmp/osm_update_test/robust/bin/Robust.ini +[2026-04-01 16:59:00] [INFO] Using profile: grid-sim +[2026-04-01 16:59:00] [INFO] Validating INI file: /tmp/osm_update_test/robust/bin/Robust.ini +[2026-04-01 16:59:00] [INFO] Validating INI file: /tmp/osm_update_test/sim1/bin/OpenSim.ini +[2026-04-01 16:59:00] [INFO] Configuration validation passed +[2026-04-01 17:17:00] [INFO] Using profile: grid-sim +[2026-04-01 17:17:18] [INFO] Using profile: grid-sim +[2026-04-01 17:17:38] [INFO] Using profile: grid-sim +[2026-04-01 17:17:38] [INFO] Validating INI file: /opt/robust/bin/Robust.ini +[2026-04-01 17:17:39] [INFO] Validating INI file: /opt/sim1/bin/OpenSim.ini +[2026-04-01 17:17:39] [INFO] Validating INI file: /opt/sim2/bin/OpenSim.ini +[2026-04-01 17:17:40] [INFO] Configuration validation passed +[2026-04-01 17:18:14] [INFO] Using profile: grid-sim +[2026-04-01 17:18:15] [INFO] Creating rollback archive: /opt/rollback/opensim_20260401_171814_realcheck.tar.gz +[2026-04-01 17:18:15] [INFO] [DRY-RUN] Would archive paths listed in: /opt/rollback/opensim_20260401_171814_realcheck.manifest +[2026-04-01 17:18:15] [INFO] Creating rollback archive: /opt/rollback/janus_20260401_171815_realcheck.tar.gz +[2026-04-01 17:18:15] [INFO] [DRY-RUN] Would archive paths listed in: /opt/rollback/janus_20260401_171815_realcheck.manifest +[2026-04-01 17:18:15] [INFO] Using profile: grid-sim +[2026-04-01 17:18:15] [INFO] Creating rollback archive: /opt/rollback/opensim_20260401_171815_realcheck.tar.gz +[2026-04-01 17:18:15] [INFO] [DRY-RUN] Would archive paths listed in: /opt/rollback/opensim_20260401_171815_realcheck.manifest +[2026-04-01 17:18:15] [INFO] Delegating update to install action: install-opensim +[2026-04-01 17:18:15] [INFO] [DRY-RUN] /mnt/d/OpenSimBash/opensimMULTITOOLS-II/modular/osmtool_install.sh --profile grid-sim --workdir /opt --action install-opensim --opensim-dir /opt/opensim --repo-mode update --deploy-binaries true --janus-prefix /opt/janus --janus-src /opt/janus-gateway +[2026-04-01 17:18:15] [INFO] Using profile: grid-sim +[2026-04-01 17:18:15] [INFO] Creating rollback archive: /opt/rollback/janus_20260401_171815_realcheck.tar.gz +[2026-04-01 17:18:15] [INFO] [DRY-RUN] Would archive paths listed in: /opt/rollback/janus_20260401_171815_realcheck.manifest +[2026-04-01 17:18:15] [INFO] Delegating update to install action: install-janus +[2026-04-01 17:18:15] [INFO] [DRY-RUN] /mnt/d/OpenSimBash/opensimMULTITOOLS-II/modular/osmtool_install.sh --profile grid-sim --workdir /opt --action install-janus --opensim-dir /opt/opensim --repo-mode update --deploy-binaries true --janus-prefix /opt/janus --janus-src /opt/janus-gateway +[2026-04-01 17:18:22] [INFO] Using profile: grid-sim +[2026-04-01 17:22:54] [INFO] Using profile: grid-sim +[2026-04-01 17:22:54] [INFO] Report generated: /opt/reports/osm_report_20260401_172254.txt +[2026-04-01 17:25:10] [INFO] OSMTool cron jobs installed/updated +[2026-04-01 17:25:10] [INFO] OSMTool cron block removed +[2026-04-01 17:25:59] [INFO] OSMTool cron jobs installed/updated +[2026-04-01 17:26:06] [INFO] OSMTool cron block removed +[2026-04-01 17:27:28] [INFO] Using profile: grid-sim +[2026-04-01 17:27:28] [INFO] Starting grid from /opt +[2026-04-01 17:27:28] [INFO] Starting robustserver from /opt/robust/bin +[2026-04-01 17:27:28] [INFO] Screen session 'robustserver' started successfully +[2026-04-01 17:27:28] [INFO] Starting moneyserver from /opt/robust/bin +[2026-04-01 17:27:29] [INFO] Screen session 'moneyserver' started successfully +[2026-04-01 17:27:29] [INFO] Starting sim1 from /opt/sim1/bin +[2026-04-01 17:27:30] [ERROR] Screen session 'sim1' did not stay alive — check logs or missing config +[2026-04-01 17:27:32] [INFO] Using profile: grid-sim +[2026-04-01 17:27:32] [INFO] Stopping grid +[2026-04-01 17:27:32] [INFO] Sent shutdown to robustserver +[2026-04-01 17:27:37] [INFO] Starting grid from /opt +[2026-04-01 17:27:37] [INFO] Starting robustserver from /opt/robust/bin +[2026-04-01 17:27:38] [INFO] Screen session 'robustserver' started successfully +[2026-04-01 17:27:38] [INFO] Starting moneyserver from /opt/robust/bin +[2026-04-01 17:27:38] [ERROR] Screen session 'moneyserver' did not stay alive — check logs or missing config +[2026-04-01 17:27:38] [INFO] Starting sim1 from /opt/sim1/bin +[2026-04-01 17:27:39] [ERROR] Screen session 'sim1' did not stay alive — check logs or missing config +[2026-04-01 17:27:41] [INFO] Using profile: grid-sim +[2026-04-01 17:27:41] [INFO] Stopping grid +[2026-04-01 17:27:41] [INFO] Sent shutdown to robustserver +[2026-04-01 17:27:55] [INFO] Using profile: robust +[2026-04-01 17:27:55] [INFO] Starting robustserver from /opt/robust/bin +[2026-04-01 17:27:56] [INFO] Screen session 'robustserver' started successfully +[2026-04-01 17:27:58] [INFO] Using profile: robust +[2026-04-01 17:27:58] [INFO] Sent shutdown to robustserver +[2026-04-01 17:28:00] [ERROR] Ziel fuer dieses Profil nicht erlaubt : profile=robust target=sim1 +[2026-04-01 17:28:32] [INFO] Using profile: standalone +[2026-04-01 17:28:32] [ERROR] standalone cannot start: missing /opt/standalone/bin/OpenSim.ini — run the config action first +[2026-04-01 17:28:32] [INFO] Using profile: standalone +[2026-04-01 17:28:32] [INFO] standalone is not running +[2026-04-01 17:28:33] [INFO] Using profile: grid-sim +[2026-04-01 17:28:33] [INFO] Using profile: grid-sim +[2026-04-01 17:29:46] [INFO] Using profile: standalone +[2026-04-01 17:29:46] [ERROR] standalone cannot start: missing /opt/standalone/bin/OpenSim.ini — run the config action first +[2026-04-01 17:29:46] [ERROR] Ziel fuer dieses Profil nicht erlaubt : profile=robust target=sim1 +[2026-04-01 17:30:35] [INFO] Using profile: grid-sim +[2026-04-01 17:30:35] [INFO] Starting grid from /opt +[2026-04-01 17:30:35] [INFO] Starting robustserver from /opt/robust/bin +[2026-04-01 17:30:35] [INFO] Screen session 'robustserver' started successfully +[2026-04-01 17:30:35] [INFO] Starting moneyserver from /opt/robust/bin +[2026-04-01 17:30:36] [INFO] Screen session 'moneyserver' started successfully +[2026-04-01 17:30:36] [INFO] Starting sim1 from /opt/sim1/bin +[2026-04-01 17:30:36] [ERROR] Screen session 'sim1' did not stay alive — check logs or missing config +[2026-04-01 17:31:16] [INFO] Using profile: grid-sim +[2026-04-01 17:31:16] [INFO] Starting grid from /opt +[2026-04-01 17:31:16] [INFO] Screen session 'robustserver' already running — skipping start +[2026-04-01 17:31:16] [INFO] Screen session 'moneyserver' already running — skipping start +[2026-04-01 17:31:16] [INFO] Starting sim1 from /opt/sim1/bin +[2026-04-01 17:31:16] [INFO] Screen session 'sim1' started successfully +[2026-04-01 17:31:20] [INFO] Using profile: grid-sim +[2026-04-01 17:31:20] [INFO] Stopping grid +[2026-04-01 17:31:20] [INFO] Sent shutdown to sim1 +[2026-04-01 17:31:20] [INFO] Sent shutdown to moneyserver +[2026-04-01 17:31:20] [INFO] Sent shutdown to robustserver +[2026-04-01 17:32:50] [INFO] Using profile: grid-sim +[2026-04-01 17:32:50] [INFO] Starting grid from /opt +[2026-04-01 17:32:50] [INFO] Starting robustserver from /opt/robust/bin +[2026-04-01 17:32:50] [INFO] Screen session 'robustserver' started successfully +[2026-04-01 17:32:50] [INFO] Starting moneyserver from /opt/robust/bin +[2026-04-01 17:32:51] [INFO] Screen session 'moneyserver' started successfully +[2026-04-01 17:32:51] [INFO] Screen session 'sim1' already running — skipping start +[2026-04-01 17:32:54] [INFO] Using profile: grid-sim +[2026-04-01 17:32:54] [INFO] Stopping grid +[2026-04-01 17:32:54] [INFO] Sent shutdown to sim1 +[2026-04-01 17:33:00] [INFO] Session 'sim1' still running after shutdown; forcing screen quit +[2026-04-01 17:33:01] [INFO] Screen session 'sim1' stopped +[2026-04-01 17:33:01] [INFO] Sent shutdown to moneyserver +[2026-04-01 17:33:07] [INFO] Screen session 'moneyserver' stopped +[2026-04-01 17:33:07] [INFO] Sent shutdown to robustserver +[2026-04-01 17:33:13] [INFO] Screen session 'robustserver' stopped +[2026-04-01 17:33:28] [INFO] Using profile: grid-sim +[2026-04-01 17:33:28] [INFO] Janus already running +[2026-04-01 17:33:30] [INFO] Using profile: grid-sim +[2026-04-01 17:33:40] [INFO] Using profile: grid-sim +[2026-04-01 17:33:40] [INFO] Using profile: grid-sim +[2026-04-01 17:34:46] [INFO] Using workdir: /opt +[2026-04-01 17:34:46] [INFO] Using profile: grid-sim +[2026-04-01 17:34:46] [INFO] Repo mode: update +[2026-04-01 17:34:46] [INFO] OpenSim dir: /opt/opensim +[2026-04-01 17:34:46] [INFO] Janus prefix: /opt/janus +[2026-04-01 17:34:46] [INFO] Configuring OpenSim runtime ini files +[2026-04-01 17:34:46] [INFO] Keeping existing config: /opt/robust/bin/Robust.ini +[2026-04-01 17:34:46] [INFO] Keeping existing config: /opt/robust/bin/Robust.ini +[2026-04-01 17:34:46] [INFO] Keeping existing config: /opt/robust/bin/MoneyServer.ini +[2026-04-01 17:34:46] [INFO] Keeping existing config: /opt/robust/bin/config-include/GridCommon.ini +[2026-04-01 17:34:46] [INFO] Keeping existing config: /opt/robust/bin/config-include/osslEnable.ini +[2026-04-01 17:34:46] [INFO] Keeping existing config: /opt/sim1/bin/OpenSim.ini +[2026-04-01 17:34:46] [INFO] Keeping existing config: /opt/sim1/bin/config-include/GridCommon.ini +[2026-04-01 17:34:46] [INFO] Keeping existing config: /opt/sim1/bin/config-include/osslEnable.ini +[2026-04-01 17:34:46] [INFO] Keeping existing config: /opt/sim2/bin/OpenSim.ini +[2026-04-01 17:34:46] [INFO] Keeping existing config: /opt/sim2/bin/config-include/GridCommon.ini +[2026-04-01 17:34:46] [INFO] Keeping existing config: /opt/sim2/bin/config-include/osslEnable.ini +[2026-04-01 17:34:47] [INFO] Updated MoneyServer.ini database credentials from configured values +[2026-04-01 17:34:47] [INFO] OpenSim runtime ini configuration completed +[2026-04-01 17:38:33] [INFO] [SMOKE] profile=grid-sim target=grid workdir=/opt +[2026-04-01 17:38:33] [INFO] [SMOKE] expected_screens=robustserver,sim1 +[2026-04-01 17:38:33] [INFO] Using profile: grid-sim +[2026-04-01 17:38:33] [INFO] Starting grid from /opt +[2026-04-01 17:38:33] [INFO] Starting robustserver from /opt/robust/bin +[2026-04-01 17:38:34] [INFO] Screen session 'robustserver' started successfully +[2026-04-01 17:38:34] [INFO] Starting moneyserver from /opt/robust/bin +[2026-04-01 17:38:34] [INFO] Screen session 'moneyserver' started successfully +[2026-04-01 17:38:34] [INFO] Starting sim1 from /opt/sim1/bin +[2026-04-01 17:38:35] [INFO] Screen session 'sim1' started successfully +[2026-04-01 17:38:38] [INFO] [SMOKE][OK] screen up: robustserver +[2026-04-01 17:38:38] [INFO] [SMOKE][OK] screen up: sim1 +[2026-04-01 17:38:38] [INFO] Using profile: grid-sim +[2026-04-01 17:38:38] [INFO] Stopping grid +[2026-04-01 17:38:38] [INFO] Sent shutdown to sim1 +[2026-04-01 17:38:44] [INFO] Session 'sim1' still running after shutdown; forcing screen quit +[2026-04-01 17:38:45] [INFO] Screen session 'sim1' stopped +[2026-04-01 17:38:45] [INFO] Sent shutdown to moneyserver +[2026-04-01 17:38:51] [INFO] Screen session 'moneyserver' stopped +[2026-04-01 17:38:51] [INFO] Sent shutdown to robustserver +[2026-04-01 17:38:57] [INFO] Screen session 'robustserver' stopped +[2026-04-01 17:39:02] [INFO] Starting grid from /opt +[2026-04-01 17:39:02] [INFO] Starting robustserver from /opt/robust/bin +[2026-04-01 17:39:03] [INFO] Screen session 'robustserver' started successfully +[2026-04-01 17:39:03] [INFO] Starting moneyserver from /opt/robust/bin +[2026-04-01 17:39:03] [INFO] Screen session 'moneyserver' started successfully +[2026-04-01 17:39:03] [INFO] Starting sim1 from /opt/sim1/bin +[2026-04-01 17:39:04] [INFO] Screen session 'sim1' started successfully +[2026-04-01 17:39:07] [INFO] [SMOKE][OK] screen up: robustserver +[2026-04-01 17:39:07] [INFO] [SMOKE][OK] screen up: sim1 +[2026-04-01 17:39:07] [INFO] Using profile: grid-sim +[2026-04-01 17:39:07] [INFO] Stopping grid +[2026-04-01 17:39:07] [INFO] Sent shutdown to sim1 +[2026-04-01 17:39:13] [INFO] Session 'sim1' still running after shutdown; forcing screen quit +[2026-04-01 17:39:14] [INFO] Screen session 'sim1' stopped +[2026-04-01 17:39:14] [INFO] Sent shutdown to moneyserver +[2026-04-01 17:39:20] [INFO] Screen session 'moneyserver' stopped +[2026-04-01 17:39:20] [INFO] Sent shutdown to robustserver +[2026-04-01 17:39:26] [INFO] Screen session 'robustserver' stopped +[2026-04-01 17:39:34] [INFO] [SMOKE][OK] screen down: robustserver +[2026-04-01 17:39:34] [INFO] [SMOKE][OK] screen down: sim1 +[2026-04-01 17:39:34] [INFO] [SMOKE][PASS] Start/Restart/Stop smoke test successful +[2026-04-01 17:39:35] [INFO] [SMOKE] profile=robust target=robust workdir=/opt +[2026-04-01 17:39:35] [INFO] [SMOKE] expected_screens=robustserver +[2026-04-01 17:39:35] [INFO] Using profile: robust +[2026-04-01 17:39:35] [INFO] Starting robustserver from /opt/robust/bin +[2026-04-01 17:39:35] [INFO] Screen session 'robustserver' started successfully +[2026-04-01 17:39:37] [INFO] [SMOKE][OK] screen up: robustserver +[2026-04-01 17:39:37] [INFO] Using profile: robust +[2026-04-01 17:39:37] [INFO] Sent shutdown to robustserver +[2026-04-01 17:39:43] [INFO] Screen session 'robustserver' stopped +[2026-04-01 17:39:46] [INFO] Starting robustserver from /opt/robust/bin +[2026-04-01 17:39:47] [INFO] Screen session 'robustserver' started successfully +[2026-04-01 17:39:49] [INFO] [SMOKE][OK] screen up: robustserver +[2026-04-01 17:39:49] [INFO] Using profile: robust +[2026-04-01 17:39:49] [INFO] Sent shutdown to robustserver +[2026-04-01 17:39:57] [INFO] Screen session 'robustserver' stopped +[2026-04-01 17:40:03] [INFO] [SMOKE][OK] screen down: robustserver +[2026-04-01 17:40:03] [INFO] [SMOKE][PASS] Start/Restart/Stop smoke test successful +[2026-04-01 17:43:37] [INFO] OSMTool cron jobs installed/updated +[2026-04-01 17:43:37] [INFO] OSMTool cron block removed +[2026-04-01 17:47:28] [INFO] Using profile: grid-sim +[2026-04-01 17:47:28] [INFO] [SMOKE] profile=robust target=robust workdir=/opt +[2026-04-01 17:47:28] [INFO] [SMOKE] expected_screens=robustserver +[2026-04-01 17:47:29] [INFO] Using profile: robust +[2026-04-01 17:47:29] [INFO] Starting robustserver from /opt/robust/bin +[2026-04-01 17:47:29] [INFO] Screen session 'robustserver' started successfully +[2026-04-01 17:47:32] [INFO] [SMOKE][OK] screen up: robustserver +[2026-04-01 17:47:32] [INFO] Using profile: robust +[2026-04-01 17:47:32] [INFO] Sent shutdown to robustserver +[2026-04-01 17:47:38] [INFO] Screen session 'robustserver' stopped +[2026-04-01 17:47:41] [INFO] Starting robustserver from /opt/robust/bin +[2026-04-01 17:47:42] [INFO] Screen session 'robustserver' started successfully +[2026-04-01 17:47:45] [INFO] [SMOKE][OK] screen up: robustserver +[2026-04-01 17:47:45] [INFO] Using profile: robust +[2026-04-01 17:47:45] [INFO] Sent shutdown to robustserver +[2026-04-01 17:47:51] [INFO] Screen session 'robustserver' stopped +[2026-04-01 17:47:59] [INFO] [SMOKE][OK] screen down: robustserver +[2026-04-01 17:47:59] [INFO] [SMOKE][PASS] Start/Restart/Stop smoke test successful +[2026-04-01 17:47:59] [INFO] Running health checks for profile: grid-sim +[2026-04-01 17:47:59] [INFO] Using workdir: /opt +[2026-04-01 17:47:59] [INFO] [WARN] Screen check skipped (no expected screens configured) +[2026-04-01 17:47:59] [INFO] [WARN] Port check skipped (no ports configured) +[2026-04-01 17:47:59] [INFO] [OK] Database reachable: robust +[2026-04-01 17:47:59] [INFO] [OK] Log scan: /mnt/d/OpenSimBash/opensimMULTITOOLS-II/osmtool_backup.log -> 52 matches +[2026-04-01 17:47:59] [INFO] [WARN] Log file not found, skipped: /opt/robust/bin/OpenSim.log +[2026-04-01 17:47:59] [ERROR] [FAIL] Log warning threshold exceeded: 52 > 50 +[2026-04-01 17:47:59] [ERROR] [FAIL] Healthcheck FAILED with 1 failing check group(s) +[2026-04-01 17:47:59] [INFO] Using profile: grid-sim +[2026-04-01 17:47:59] [INFO] Validating INI file: /opt/robust/bin/Robust.ini +[2026-04-01 17:48:00] [INFO] Validating INI file: /opt/sim1/bin/OpenSim.ini +[2026-04-01 17:48:00] [INFO] Validating INI file: /opt/sim2/bin/OpenSim.ini +[2026-04-01 17:48:01] [INFO] Configuration validation passed +[2026-04-01 17:56:29] [INFO] OSMTool cron jobs installed/updated +[2026-04-01 17:56:37] [INFO] OSMTool cron jobs installed/updated diff --git a/osmtool_backup.sh b/osmtool_backup.sh new file mode 100644 index 0000000..1f2719a --- /dev/null +++ b/osmtool_backup.sh @@ -0,0 +1,718 @@ +#!/bin/bash + +# Backup Version 1.0 + +STARTVERZEICHNIS="/opt" # Startverzeichnis +ROBUSTVERZEICHNIS="Robust" # Robust Verzeichnis +REGIONSDATEI="osmregionlist.ini" # Regionsdatei +SIMDATEI="osmsimlist.ini" # Simulationsdatei +BACKUPWARTEZEIT=60 # Wartezeit in Sekunden zwischen den Backups + +KOMMANDO=$1 + +#────────────────────────────────────────────────────────────────────────────────────────── +#* Informationen +#────────────────────────────────────────────────────────────────────────────────────────── +# + +## * makeverzeichnisliste + # Eine Funktion zum Erstellen einer Liste von Verzeichnissen aus einer Datei. + #? Verwendung: makeverzeichnisliste + # Diese Funktion liest Zeilen aus der angegebenen SIMDATEI im STARTVERZEICHNIS und erstellt eine + # Liste von Verzeichnissen. Die Liste wird in der globalen Variable VERZEICHNISSLISTE gespeichert. + # Die Anzahl der Einträge in der Liste wird in der globalen Variable ANZAHLVERZEICHNISSLISTE gespeichert. + #? Argumente: + # STARTVERZEICHNIS - Das Verzeichnis, in dem sich die SIMDATEI befindet. + # SIMDATEI - Die Datei, aus der die Verzeichnisse gelesen werden sollen. + #? Beispielaufruf + # makeverzeichnisliste +## +function makeverzeichnisliste() { + # Letzte Bearbeitung 27.09.2023 + + # Initialisieren der Verzeichnisliste + VERZEICHNISSLISTE=() + + # Schleife zum Lesen der Zeilen aus der SIMDATEI und Hinzufügen zum Array + while IFS= read -r line; do + VERZEICHNISSLISTE+=("$line") + done >"/$STARTVERZEICHNIS/$INIVERZEICHNIS/bin/Regions/$RTREGIONSNAME.ini" + else + echo error "REGIONSINITEILEN: $INI_FILE wurde nicht gefunden" + fi + return 0 +} + +## * autoregionsiniteilen + # Diese Funktion ruft zuerst die Funktion `makeverzeichnisliste` auf, um eine Liste von Verzeichnissen zu erstellen, + # und iteriert dann über jedes Verzeichnis in der Liste. Für jedes Verzeichnis wird die Regions.ini-Datei in mehrere + # separate INI-Dateien aufgeteilt, eine für jede Region, und die einzelnen Regionsdateien werden umbenannt. Falls die + # Regions.ini-Datei in einem Verzeichnis nicht vorhanden ist, wird sie nicht umbenannt. + #? Parameter: + # - Keine + #? Rückgabewert: + # - 0: Erfolgreiche Ausführung der Funktion + #? Verwendungsbeispiel: + # autoregionsiniteilen +## +function autoregionsiniteilen() { + # Letzte Bearbeitung 01.10.2023 + makeverzeichnisliste + sleep 1 + for ((i = 0; i < "$ANZAHLVERZEICHNISSLISTE"; i++)); do + echo info "Region.ini ${VERZEICHNISSLISTE[$i]} zerlegen" + echo line + VERZEICHNIS="${VERZEICHNISSLISTE[$i]}" + # Regions.ini teilen: + echo "$VERZEICHNIS" # OK geht + INI_FILE="/$STARTVERZEICHNIS/$VERZEICHNIS/bin/Regions/Regions.ini" # Auszulesende Datei + # shellcheck disable=SC2155 + declare -a TARGETS="$(get_regionsarray "${INI_FILE}")" + # shellcheck disable=SC2068 + for MeineRegion in ${TARGETS[@]}; do + regionsiniteilen "$VERZEICHNIS" "$MeineRegion" + sleep 1 + echo rohtext "regionsiniteilen $VERZEICHNIS $MeineRegion" + done + # Dann umbenennen: + # Pruefung ob Datei vorhanden ist, wenn ja umbenennen. + if [ ! -d "$INI_FILE" ]; then + mv /$STARTVERZEICHNIS/"$INIVERZEICHNIS"/bin/Regions/Regions.ini /$STARTVERZEICHNIS/"$INIVERZEICHNIS"/bin/Regions/"$DATUM"-Regions.ini.old + fi + done + return 0 +} + +## * createregionlist + # Diese Funktion erstellt eine Liste von Regionsnamen aus den INI-Dateien in den angegebenen Verzeichnissen und speichert + # sie in der Datei osmregionlist.ini. Zuvor wird die vorhandene osmregionlist.ini-Datei (falls vorhanden) gesichert und in + # osmregionlist.ini.old umbenannt. + #? Parameter: + # - Keine + #? Rückgabewert: + # - 0: Erfolgreiche Ausführung der Funktion + #? Verwendungsbeispiel: + # createregionlist +## +function createregionlist() { + # Letzte Bearbeitung 01.10.2023 + # Alte osmregionlist.ini sichern und in osmregionlist.ini.old umbenennen. + if [ -f "/$STARTVERZEICHNIS/osmregionlist.ini" ]; then + if [ -f "/$STARTVERZEICHNIS/osmregionlist.ini.old" ]; then + rm -r /$STARTVERZEICHNIS/osmregionlist.ini.old + fi + mv /$STARTVERZEICHNIS/osmregionlist.ini /$STARTVERZEICHNIS/osmregionlist.ini.old + fi + # Die mit regionsconfigdateiliste erstellte Datei osmregionlist.ini nach sim Verzeichnis und Regionsnamen in die osmregionlist.ini speichern. + declare -A Dateien # Array erstellen + makeverzeichnisliste + sleep 1 + for ((i = 0; i < "$ANZAHLVERZEICHNISSLISTE"; i++)); do + echo info "Regionnamen ${VERZEICHNISSLISTE[$i]} schreiben" + VERZEICHNIS="${VERZEICHNISSLISTE[$i]}" + # shellcheck disable=SC2178 + Dateien=$(find /$STARTVERZEICHNIS/"$VERZEICHNIS"/bin/Regions/ -name "*.ini") + for i2 in "${Dateien[@]}"; do # Array abarbeiten + echo "$i2" >>osmregionlist.ini + done + done + # Ueberfluessige Zeichen entfernen + LOESCHEN=$(sed s/'\/'$STARTVERZEICHNIS'\/'//g /$STARTVERZEICHNIS/osmregionlist.ini) + echo "$LOESCHEN" >/$STARTVERZEICHNIS/osmregionlist.ini # Aenderung /$STARTVERZEICHNIS/ speichern. + LOESCHEN=$(sed s/'\/bin\/Regions\/'/' "'/g /$STARTVERZEICHNIS/osmregionlist.ini) # /bin/Regions/ entfernen. + echo "$LOESCHEN" >/$STARTVERZEICHNIS/osmregionlist.ini # Aenderung /bin/Regions/ speichern. + LOESCHEN=$(sed s/'.ini'/'"'/g /$STARTVERZEICHNIS/osmregionlist.ini) # Endung .ini entfernen. + echo "$LOESCHEN" >/$STARTVERZEICHNIS/osmregionlist.ini # Aenderung .ini entfernen speichern. + + LOESCHEN=$(sed s/'\"'/''/g /$STARTVERZEICHNIS/osmregionlist.ini) # " entfernen. + echo "$LOESCHEN" >/$STARTVERZEICHNIS/osmregionlist.ini # Aenderung " entfernen speichern. + + # Schauen ob da noch Regions.ini bei sind also Regionen mit dem Namen Regions, diese Zeilen loeschen. + LOESCHEN=$(sed '/Regions/d' /$STARTVERZEICHNIS/osmregionlist.ini) # Alle Zeilen mit dem Eintrag Regions entfernen . + echo "$LOESCHEN" >/$STARTVERZEICHNIS/osmregionlist.ini # Aenderung .ini entfernen speichern. + return 0 +} + +## * regionbackup + # Diese Funktion erstellt ein Backup für eine OpenSimulator-Region, indem sie OAR- und Terrain-Daten sowie Konfigurationsdateien speichert. + # Die Region wird in den Offline-Modus versetzt, wenn sie nicht bereits offline ist, bevor das Backup erstellt wird. + #? Parameter: + # 1. BACKUPVERZEICHNISSCREENNAME: Der Name des Bildschirms (Screen) für das Backup. + # 2. REGIONSNAME: Der Name der Region, die gesichert werden soll. + #? Rückgabewert: + # - 0: Erfolgreiche Ausführung der Funktion + #? Verwendungsbeispiel: + # regionbackup "BackupScreen" "MyRegion" +## +function regionbackup() { + # Letzte Bearbeitung 01.10.2023 + # regionbackup "$BACKUPSCREEN" "$BACKUPREGION" + echo "Erstelle Backup von: $BACKUPSCREEN $BACKUPREGION" + + sleep 1 + BACKUPVERZEICHNISSCREENNAME=$1 + REGIONSNAME=$2 + + echo + echo "Backup $BACKUPVERZEICHNISSCREENNAME der Region $REGIONSNAME" + cd /$STARTVERZEICHNIS/"$BACKUPVERZEICHNISSCREENNAME"/bin || return 1 # Test ob Verzeichnis vorhanden. + mkdir -p /$STARTVERZEICHNIS/backup # Backup Verzeichnis anlegen falls nicht vorhanden. + echo "Ich kann nicht pruefen ob die Region im OpenSimulator vorhanden ist." + echo "Sollte sie nicht vorhanden sein wird root also alle Regionen gespeichert" + # Ist die Region Online oder Offline? + if [ -f /$STARTVERZEICHNIS/"$BACKUPVERZEICHNISSCREENNAME"/bin/Regions/"$REGIONSNAME".ini.offline ]; then + echo "$REGIONSNAME ist heruntergefahren." + else + screen -S "$BACKUPVERZEICHNISSCREENNAME" -p 0 -X eval "stuff 'change region ${REGIONSNAME//\"/}'^M" + echo "$BACKUPVERZEICHNISSCREENNAME $REGIONSNAME" + screen -S "$BACKUPVERZEICHNISSCREENNAME" -p 0 -X eval "stuff 'save oar /$STARTVERZEICHNIS/backup/'$DATUM'-$REGIONSNAME.oar'^M" + echo "$BACKUPVERZEICHNISSCREENNAME $DATUM-$REGIONSNAME.oar" + + # Neu xml backup + screen -S "$BACKUPVERZEICHNISSCREENNAME" -p 0 -X eval "stuff 'save xml2 /$STARTVERZEICHNIS/backup/'$DATUM'-$REGIONSNAME.xml2'^M" + echo "$BACKUPVERZEICHNISSCREENNAME $DATUM-$REGIONSNAME.xml2" + # Neu xml backup ende + + screen -S "$BACKUPVERZEICHNISSCREENNAME" -p 0 -X eval "stuff 'terrain save /$STARTVERZEICHNIS/backup/'$DATUM'-$REGIONSNAME.png'^M" + echo "$BACKUPVERZEICHNISSCREENNAME $DATUM-$REGIONSNAME.png" + screen -S "$BACKUPVERZEICHNISSCREENNAME" -p 0 -X eval "stuff 'terrain save /$STARTVERZEICHNIS/backup/'$DATUM'-$REGIONSNAME.raw'^M" + echo "$BACKUPVERZEICHNISSCREENNAME $DATUM-$REGIONSNAME.raw" + echo "$BACKUPVERZEICHNISSCREENNAME Region $DATUM-$REGIONSNAME RAW und PNG Terrain werden gespeichert" + fi + + sleep 10 + if [ -f /$STARTVERZEICHNIS/"$BACKUPVERZEICHNISSCREENNAME"/bin/Regions/"$REGIONSNAME".ini.offline ]; then + cp /$STARTVERZEICHNIS/"$BACKUPVERZEICHNISSCREENNAME"/bin/Regions/"$REGIONSNAME".ini.offline /$STARTVERZEICHNIS/backup/"$DATUM"-"$REGIONSNAME".ini.offline + echo "$BACKUPVERZEICHNISSCREENNAME Die Regions $DATUM-$REGIONSNAME.ini.offline wird gespeichert" + fi + if [ -f /$STARTVERZEICHNIS/"$BACKUPVERZEICHNISSCREENNAME"/bin/Regions/"$REGIONSNAME".ini ]; then + cp /$STARTVERZEICHNIS/"$BACKUPVERZEICHNISSCREENNAME"/bin/Regions/"$REGIONSNAME".ini /$STARTVERZEICHNIS/backup/"$DATUM"-"$REGIONSNAME".ini + echo "$BACKUPVERZEICHNISSCREENNAME Die Regions $DATUM-$REGIONSNAME.ini wird gespeichert" + fi + if [ -f /$STARTVERZEICHNIS/"$BACKUPVERZEICHNISSCREENNAME"/bin/Regions/"${REGIONSNAME//\"/}" ]; then + cp /$STARTVERZEICHNIS/"$BACKUPVERZEICHNISSCREENNAME"/bin/Regions/"${REGIONSNAME//\"/}" /$STARTVERZEICHNIS/backup/"$DATUM"-"$REGIONSNAME".ini + echo "$BACKUPVERZEICHNISSCREENNAME Die Regions $DATUM-$REGIONSNAME.ini wird gespeichert" + fi + + echo "Backup der Region $REGIONSNAME wird fertiggestellt." + return 0 +} + + +## * autoallclean. + # loescht Log, dll, so, exe, aot Dateien fuer einen saubere neue installation, mit Robust. + # Hierbei werden keine Datenbanken oder Konfigurationen geloescht aber opensim ist anschliessend nicht mehr startbereit. + # Um opensim wieder Funktionsbereit zu machen muss ein Upgrade oder ein oscopy vorgang ausgefuehrt werden. + #? @param keine. + #? @return nichts wird zurueckgegeben. + # todo: nichts. +## +function autoallclean() { + # Letzte Bearbeitung 01.10.2023 + makeverzeichnisliste + sleep 1 + for ((i = 0; i < "$ANZAHLVERZEICHNISSLISTE"; i++)); do + # Dateien + rm /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/*.log + rm /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/*.dll + rm /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/*.exe + rm /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/*.so + rm /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/*.xml + rm /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/*.dylib + rm /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/*.example + rm /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/*.sample + rm /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/*.txt + rm /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/*.config + rm /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/*.py + rm /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/*.old + rm /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/*.pdb + + # Verzeichnisse leeren + rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/assetcache/* + rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/maptiles/* + rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/MeshCache/* + rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/j2kDecodeCache/* + rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/ScriptEngines/* + + rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/addin-db-002/* + rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/addon-modules/* + rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/assets/* + rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/bakes/* + rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/data/* + rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/Estates/* + rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/inventory/* + rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/lib/* + rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/lib32/* + rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/lib64/* + rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/Library/* + rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/openmetaverse_data/* + rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/robust-include/* + + echo "autoallclean: ${VERZEICHNISSLISTE[$i]} geloescht" + sleep 1 + done + # nochmal das gleiche mit Robust + echo "autoallclean: $ROBUSTVERZEICHNIS geloescht" + rm /$STARTVERZEICHNIS/$ROBUSTVERZEICHNIS/bin/*.log + rm /$STARTVERZEICHNIS/$ROBUSTVERZEICHNIS/bin/*.dll + rm /$STARTVERZEICHNIS/$ROBUSTVERZEICHNIS/bin/*.exe + rm /$STARTVERZEICHNIS/$ROBUSTVERZEICHNIS/bin/*.so + rm /$STARTVERZEICHNIS/$ROBUSTVERZEICHNIS/bin/*.xml + rm /$STARTVERZEICHNIS/$ROBUSTVERZEICHNIS/bin/*.dylib + rm /$STARTVERZEICHNIS/$ROBUSTVERZEICHNIS/bin/*.example + rm /$STARTVERZEICHNIS/$ROBUSTVERZEICHNIS/bin/*.sample + rm /$STARTVERZEICHNIS/$ROBUSTVERZEICHNIS/bin/*.txt + rm /$STARTVERZEICHNIS/$ROBUSTVERZEICHNIS/bin/*.config + rm /$STARTVERZEICHNIS/$ROBUSTVERZEICHNIS/bin/*.py + rm /$STARTVERZEICHNIS/$ROBUSTVERZEICHNIS/bin/*.old + rm /$STARTVERZEICHNIS/$ROBUSTVERZEICHNIS/bin/*.pdb + + rm -r /$STARTVERZEICHNIS/$ROBUSTVERZEICHNIS/bin/assetcache/* + rm -r /$STARTVERZEICHNIS/$ROBUSTVERZEICHNIS/bin/maptiles/* + rm -r /$STARTVERZEICHNIS/$ROBUSTVERZEICHNIS/bin/MeshCache/* + rm -r /$STARTVERZEICHNIS/$ROBUSTVERZEICHNIS/bin/j2kDecodeCache/* + rm -r /$STARTVERZEICHNIS/$ROBUSTVERZEICHNIS/bin/ScriptEngines/* + + rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/addin-db-002/* + rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/addon-modules/* + rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/assets/* + rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/bakes/* + rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/data/* + rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/Estates/* + rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/inventory/* + rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/lib/* + rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/lib32/* + rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/lib64/* + rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/Library/* + rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/openmetaverse_data/* + rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/robust-include/* + + return 0 +} + +## * autoregionbackup + # Diese Funktion durchläuft die Regionen in Ihrem OpenSimulator-Grid und erstellt automatische Backups. + #? Parameter: + # Keine Parameter erforderlich. + #? Rückgabewert: + # - Kein expliziter Rückgabewert + #? Verwendungsbeispiel: + # autoregionbackup + # todo: Uebergabe Parameter Fehler. Bug Modus mit extra ausgaben. Ist keine osmregionlist.ini vorhanden muss sie erstellt werden. +## +function autoregionbackup() { + # Letzte Bearbeitung 01.10.2023 + echo "Automatisches Backup wird gestartet." + + # Ist die osmregionlist.ini vorhanden? + if [ ! -f "/$STARTVERZEICHNIS/osmregionlist.ini" ]; then + echo "Die osmregionlist.ini Datei ist noch nicht vorhanden und wird erstellt." + createregionlist + else + echo "Die osmregionlist.ini Datei ist bereits vorhanden." + fi + + makeregionsliste + sleep 1 + for ((i = 0; i < "$ANZAHLREGIONSLISTE"; i++)); do + BACKUPSCREEN=$(echo "${REGIONSLISTE[$i]}" | cut -d ' ' -f 1) + BACKUPREGION=$(echo "${REGIONSLISTE[$i]}" | cut -d ' ' -f 2) + echo "Starte Backup von: $BACKUPSCREEN $BACKUPREGION" # Testausgabe + regionbackup "$BACKUPSCREEN" "$BACKUPREGION" + if [ -f /$STARTVERZEICHNIS/"$BACKUPSCREEN"/bin/Regions/"$BACKUPREGION".ini.offline ]; then + echo "$BACKUPREGION Region ist Offline und wird uebersprungen." + else + sleep $BACKUPWARTEZEIT + echo "BACKUPWARTEZEIT $BACKUPWARTEZEIT Sekunden." # Testausgabe + fi + done + return 0 +} + +# Zähle die Tabellen in der Datenbank und die Asset-Typen in der Tabelle 'assets'. +# bash osmtool_backup.sh datenbanktabellen "mein_benutzer" "geheim123" "meine_datenbank" +function datenbanktabellen() { + username=$1 + password=$2 + databasename=$3 + logfile="/$STARTVERZEICHNIS/backup/${databasename}/status_report.txt" + + mkdir -p "/$STARTVERZEICHNIS/backup/${databasename}" || exit + + echo "📊 Statusbericht für Datenbank: $databasename" | tee "$logfile" + echo "📅 Datum: $(date '+%Y-%m-%d %H:%M:%S')" | tee -a "$logfile" + + # Tabellen zählen + tabellenanzahl=$(mysql -u"$username" -p"$password" -D"$databasename" -e "SHOW TABLES;" | tail -n +2 | wc -l) + echo "📦 Tabellen gefunden: $tabellenanzahl" | tee -a "$logfile" + + # Asset-Typen zählen (wenn Tabelle vorhanden) + if mysql -u"$username" -p"$password" -D"$databasename" -e "SHOW TABLES LIKE 'assets';" | grep -q "assets"; then + assettypen_anzahl=$(mysql -u"$username" -p"$password" -D"$databasename" -e "SELECT COUNT(DISTINCT assetType) FROM assets;" | tail -n 1) + echo "🎨 Asset-Typen in 'assets': $assettypen_anzahl" | tee -a "$logfile" + else + echo "⚠️ Tabelle 'assets' nicht gefunden." | tee -a "$logfile" + fi + + echo "✅ Bericht gespeichert unter: $logfile" +} + + +# beispiel: screen -fa -S BACKUPNOASSETS -d -U -m bash osmtool.sh backuptabelle_noassets "mein_benutzer" "geheim123" "meine_datenbank" +function backuptabelle_noassets() { + username=$1 + password=$2 + databasename=$3 + + mkdir -p /$STARTVERZEICHNIS/backup/"$databasename" || exit + logfile="/$STARTVERZEICHNIS/backup/${databasename}/backup_log.txt" + + # Tabellennamen holen und speichern + mysql -u"$username" -p"$password" -D"$databasename" -e "SHOW TABLES;" | tail -n +2 > /$STARTVERZEICHNIS/backup/"$databasename"/liste.txt + + # Anzahl der Tabellen zählen + tabellenanzahl=$(wc -l < /$STARTVERZEICHNIS/backup/"$databasename"/liste.txt) + echo "📦 Tabellen gefunden: $tabellenanzahl" | tee -a "$logfile" + + # Tabellen sichern (außer 'assets') + gesichert=0 + while IFS= read -r tabellenname; do + sleep 1 + if [[ "$tabellenname" != "assets" ]]; then + mysqldump -u"$username" -p"$password" "$databasename" "$tabellenname" | zip > /$STARTVERZEICHNIS/backup/"$databasename"/"$tabellenname".sql.zip + echo "✅ Gesichert: $tabellenname" | tee -a "$logfile" + ((gesichert++)) + fi + done < /$STARTVERZEICHNIS/backup/"$databasename"/liste.txt + + echo "🔒 Tabellen gesichert (ohne 'assets'): $gesichert" | tee -a "$logfile" + return 0 +} + +# screen -fa -S ASSETBACKUP -d -U -m bash osmtool.sh asset_backup "mein_benutzer" "geheim123" "meine_datenbank" +function asset_backup() { + username=$1 + password=$2 + databasename=$3 + fromtable="assets" + fromtypes="assetType" + + mkdir -p /$STARTVERZEICHNIS/backup/"$databasename"/assets || exit + cd /$STARTVERZEICHNIS/backup/"$databasename"/assets || exit + logfile="./asset_backup_log.txt" + + # Assettypen holen + mysql -u"$username" -p"$password" -D"$databasename" -e "SELECT DISTINCT $fromtypes FROM $fromtable;" | tail -n +2 > typen.txt + assettypen_anzahl=$(wc -l < typen.txt) + echo "📦 Assettypen gefunden: $assettypen_anzahl" | tee -a "$logfile" + + gesichert=0 + while IFS= read -r assettype; do + filename="assetType_${assettype}" + echo "✅ Sichere Asset-Typ: $assettype → $filename.sql.zip" | tee -a "$logfile" + mysqldump -u"$username" -p"$password" "$databasename" --tables "$fromtable" --where="$fromtypes = '$assettype'" | zip > "$filename.sql.zip" + ((gesichert++)) + done < typen.txt + + echo "🔒 Assettypen gesichert: $gesichert" | tee -a "$logfile" +} + +Unterschiedliche assettypen der Tabelle assets zählen + +# screen -fa -S DBRESTORE -d -U -m bash osmtool.sh db_restoretabelle_noassets "mein_benutzer" "geheim123" "meine_datenbank" +function db_restoretabelle_noassets() { + username=$1 + password=$2 + databasename=$3 + restorepath="/$STARTVERZEICHNIS/backup/${databasename}" + logfile="${restorepath}/restore_log.txt" + + cd "$restorepath" || exit + + if [[ ! -f liste.txt ]]; then + echo "❌ Keine 'liste.txt' gefunden. Backup-Datei fehlt oder ungültig." | tee -a "$logfile" + return 1 + fi + + echo "📦 Starte Wiederherstellung aus: $restorepath" | tee -a "$logfile" + + restored=0 + total=$(grep -v "^assets$" liste.txt | wc -l) + echo "🔍 Tabellen zur Wiederherstellung (ohne 'assets'): $total" | tee -a "$logfile" + + while IFS= read -r tabellenname; do + if [[ "$tabellenname" != "assets" ]]; then + zipfile="${tabellenname}.sql.zip" + if [[ -f "$zipfile" ]]; then + echo "🔄 Wiederherstellung: $tabellenname" | tee -a "$logfile" + unzip -p "$zipfile" | mysql -u"$username" -p"$password" "$databasename" + ((restored++)) + else + echo "⚠️ Datei fehlt: $zipfile" | tee -a "$logfile" + fi + fi + done < liste.txt + + echo "✅ Tabellen erfolgreich wiederhergestellt: $restored von $total" | tee -a "$logfile" + return 0 +} + +# Wiederherstellung der Asset-Typen +function asset_restore() { + username=$1 + password=$2 + databasename=$3 + restorepath="/$STARTVERZEICHNIS/backup/${databasename}/assets" + logfile="${restorepath}/asset_restore_log.txt" + + cd "$restorepath" || exit + + zipfiles=(assetType_*.sql.zip) + anzahl=${#zipfiles[@]} + echo "📦 Asset-Backups gefunden: $anzahl" | tee -a "$logfile" + + restored=0 + for zipfile in "${zipfiles[@]}"; do + echo "🔄 Wiederherstellung: $zipfile" | tee -a "$logfile" + unzip -p "$zipfile" | mysql -u"$username" -p"$password" "$databasename" + ((restored++)) + done + + echo "✅ Assettypen wiederhergestellt: $restored" | tee -a "$logfile" +} + +# Sichere die Datenbanken 'wordweb' und 'money' in ZIP-Dateien. +# bash osmtool_backup.sh sichere_wordweb_und_money "mein_benutzer" "geheim123" + +function sichere_wordweb_und_money() { + username=$1 + password=$2 + logfile="/$STARTVERZEICHNIS/backup/db_sicherung_log.txt" + datum=$(date '+%Y-%m-%d_%H-%M-%S') + + mkdir -p /$STARTVERZEICHNIS/backup/wordweb + mkdir -p /$STARTVERZEICHNIS/backup/money + + echo "📅 Sicherung gestartet: $datum" | tee -a "$logfile" + + # WORDWEB sichern + echo "💾 Sichere Datenbank: wordweb" | tee -a "$logfile" + mysqldump -u"$username" -p"$password" wordweb | zip > /$STARTVERZEICHNIS/backup/wordweb/wordweb_${datum}.sql.zip + size_wordweb=$(du -sh /$STARTVERZEICHNIS/backup/wordweb/wordweb_${datum}.sql.zip | cut -f1) + echo "✅ wordweb gesichert → Größe: $size_wordweb" | tee -a "$logfile" + + # MONEY sichern + echo "💾 Sichere Datenbank: money" | tee -a "$logfile" + mysqldump -u"$username" -p"$password" money | zip > /$STARTVERZEICHNIS/backup/money/money_${datum}.sql.zip + size_money=$(du -sh /$STARTVERZEICHNIS/backup/money/money_${datum}.sql.zip | cut -f1) + echo "✅ money gesichert → Größe: $size_money" | tee -a "$logfile" + + echo "📦 Sicherung abgeschlossen: $(date '+%Y-%m-%d %H:%M:%S')" | tee -a "$logfile" +} + +function restore_wordweb_und_money() { + username=$1 + password=$2 + for dbname in wordweb money; do + restorepath="/$STARTVERZEICHNIS/backup/${dbname}" + latestfile=$(ls -t "$restorepath"/*.sql.zip | head -n 1) + if [[ -f "$latestfile" ]]; then + echo "🔄 Wiederherstellung: $dbname aus $latestfile" + unzip -p "$latestfile" | mysql -u"$username" -p"$password" "$dbname" + else + echo "⚠️ Keine Backup-Datei gefunden für $dbname" + fi + done +} + +# Backup-Konfigurationsdateien aus robust/bin und sim-Verzeichnissen +# bash osmtool_backup.sh backup_config +function backup_config() { + echo "${SYM_WAIT} ${COLOR_START}Sichere Konfigurationsdateien...${COLOR_RESET}" + + # Zielverzeichnis für Backups + backup_root="/opt/backup/config" + + # Backup aus robust/bin + if [[ -d "robust/bin" ]]; then + src_dir="robust/bin" + dest_dir="${backup_root}/${src_dir}" + echo "${SYM_OK} ${COLOR_START}Sichere ${COLOR_DIR}${src_dir}${COLOR_RESET} nach ${COLOR_DIR}${dest_dir}${COLOR_RESET}" + find "$src_dir" -type f -name "*.ini" | while read -r file; do + rel_path="${file#$src_dir/}" # relativer Pfad + mkdir -p "${dest_dir}/$(dirname "$rel_path")" + cp "$file" "${dest_dir}/$rel_path" + done + else + echo "${SYM_BAD} ${COLOR_DIR}robust/bin${COLOR_RESET} nicht gefunden. Überspringe." + fi + + # Backup aus sim-Verzeichnissen + for ((i=1; i<=999; i++)); do + sim_dir="sim$i/bin" + if [[ -d "$sim_dir" ]]; then + dest_dir="${backup_root}/${sim_dir}" + echo "${SYM_OK} ${COLOR_START}Sichere ${COLOR_DIR}${sim_dir}${COLOR_RESET} nach ${COLOR_DIR}${dest_dir}${COLOR_RESET}" + find "$sim_dir" -type f -name "*.ini" | while read -r file; do + rel_path="${file#$sim_dir/}" + mkdir -p "${dest_dir}/$(dirname "$rel_path")" + cp "$file" "${dest_dir}/$rel_path" + done + fi + done + + echo "${SYM_OK} ${COLOR_START}Backup abgeschlossen.${COLOR_RESET}" + echo . +} + +# Restore-Konfigurationsdateien aus dem backup Verzeichnis nach robust/bin und sim-Verzeichnissen +# bash osmtool_backup.sh restore_config +function restore_config() { + echo "${SYM_WAIT} ${COLOR_START}Stelle Konfigurationsdateien aus Backup wieder her...${COLOR_RESET}" + + # Quellverzeichnis für Backups + backup_root="/opt/backup/config" + + # Restore für robust/bin + src_dir="${backup_root}/robust/bin" + dest_dir="robust/bin" + if [[ -d "$src_dir" ]]; then + echo "${SYM_OK} ${COLOR_START}Stelle ${COLOR_DIR}${dest_dir}${COLOR_RESET} aus ${COLOR_DIR}${src_dir}${COLOR_RESET} wieder her" + find "$src_dir" -type f -name "*.ini" | while read -r file; do + rel_path="${file#$src_dir/}" + mkdir -p "${dest_dir}/$(dirname "$rel_path")" + cp "$file" "${dest_dir}/$rel_path" + done + else + echo "${SYM_BAD} ${COLOR_DIR}${src_dir}${COLOR_RESET} nicht gefunden. Überspringe." + fi + + # Restore für sim-Verzeichnisse + for ((i=1; i<=999; i++)); do + src_dir="${backup_root}/sim$i/bin" + dest_dir="sim$i/bin" + if [[ -d "$src_dir" ]]; then + echo "${SYM_OK} ${COLOR_START}Stelle ${COLOR_DIR}${dest_dir}${COLOR_RESET} aus ${COLOR_DIR}${src_dir}${COLOR_RESET} wieder her" + find "$src_dir" -type f -name "*.ini" | while read -r file; do + rel_path="${file#$src_dir/}" + mkdir -p "${dest_dir}/$(dirname "$rel_path")" + cp "$file" "${dest_dir}/$rel_path" + done + fi + done + + echo "${SYM_OK} ${COLOR_START}Wiederherstellung abgeschlossen.${COLOR_RESET}" + echo . +} + + + +case $KOMMANDO in + "regionsiniteilen") regionsiniteilen "$2" "$3" "$4" ;; + "autoregionsiniteilen") autoregionsiniteilen ;; + "createregionlist") createregionlist ;; + "regionbackup") regionbackup "$2" "$3" ;; + "backup_config") backup_config ;; + "restore_config") restore_config ;; + "autoallclean") autoallclean ;; + "autoregionbackup") autoregionbackup ;; + "datenbanktabellen") datenbanktabellen "$2" "$3" "$4" ;; + "backuptabelle_noassets") backuptabelle_noassets "$2" "$3" "$4" ;; + "asset_backup") asset_backup "$2" "$3" "$4" ;; + "db_restoretabelle_noassets") db_restoretabelle_noassets "$2" "$3" "$4" ;; + "asset_restore") asset_restore "$2" "$3" "$4" ;; + "sichere_wordweb_und_money") sichere_wordweb_und_money "$2" "$3" ;; + "restore_wordweb_und_money") restore_wordweb_und_money "$2" "$3" ;; + *) echo "Unbekanntes Kommando: $KOMMANDO" ;; +esac +# Ende des Skripts +exit 0 \ No newline at end of file diff --git a/osmtool_main.sh b/osmtool_main.sh new file mode 100644 index 0000000..136a781 --- /dev/null +++ b/osmtool_main.sh @@ -0,0 +1,258 @@ +#!/usr/bin/env bash +set -euo pipefail + +BASE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +MOD_DIR="$BASE_DIR/modular" + +# shellcheck source=./modular/osmtool_core.sh +source "$MOD_DIR/osmtool_core.sh" +init_i18n + +usage() { + cat <<'EOF' +Usage: + osmtool_main.sh [--mode ] [--lang ] [--profile ] --module [module-options] + +Examples: + osmtool_main.sh --module install --action prepare-ubuntu + osmtool_main.sh --module startstop --target grid --action restart --workdir /opt + osmtool_main.sh --module cleanup --action log-clean --workdir /opt + osmtool_main.sh --module health --action run --workdir /opt + osmtool_main.sh --module backup --action db-backup --db-user root --db-pass secret --db-name opensim + osmtool_main.sh --module update --action create-rollback --workdir /opt + osmtool_main.sh --module config --action validate-runtime --workdir /opt + osmtool_main.sh --module report --action generate --workdir /opt + osmtool_main.sh --module smoke --action run --profile grid-sim --workdir /opt + osmtool_main.sh --module cron --action list + osmtool_main.sh --mode ui +EOF + print_usage_common +} + +dispatch_module() { + local module="$1" + shift + + case "$module" in + install) + "$MOD_DIR/osmtool_install.sh" --profile "$PROFILE" "$@" + ;; + startstop) + "$MOD_DIR/osmtool_startstop.sh" --profile "$PROFILE" "$@" + ;; + cleanup) + "$MOD_DIR/osmtool_cleanup.sh" --profile "$PROFILE" "$@" + ;; + health) + "$MOD_DIR/osmtool_health.sh" --profile "$PROFILE" "$@" + ;; + backup) + "$MOD_DIR/osmtool_data.sh" --profile "$PROFILE" "$@" + ;; + update) + "$MOD_DIR/osmtool_update.sh" --profile "$PROFILE" "$@" + ;; + config) + "$MOD_DIR/osmtool_config.sh" --profile "$PROFILE" "$@" + ;; + report) + "$MOD_DIR/osmtool_report.sh" --profile "$PROFILE" "$@" + ;; + smoke) + "$MOD_DIR/osmtool_smoke.sh" --profile "$PROFILE" "$@" + ;; + cron) + bash "$MOD_DIR/osmtool_cron.sh" --profile "$PROFILE" "$@" + ;; + restore) + "$MOD_DIR/osmtool_restore.sh" --profile "$PROFILE" "$@" + ;; + *) + die "$(msg ERR_UNKNOWN_MODULE): $module" + ;; + esac +} + +default_target_for_profile() { + case "$1" in + grid-sim) echo "grid" ;; + robust) echo "robust" ;; + standalone) echo "standalone" ;; + esac +} + +args_have_target() { + local prev="" + local token + for token in "$@"; do + if [[ "$prev" == "--target" ]]; then + return 0 + fi + prev="$token" + done + return 1 +} + +ui_flow() { + local module action target + + PROFILE="$(show_menu "$(msg APP_TITLE)" "$(msg MENU_CHOOSE_PROFILE)" \ + "grid-sim" "$(msg PROFILE_GRID_SIM)" \ + "robust" "$(msg PROFILE_ROBUST)" \ + "standalone" "$(msg PROFILE_STANDALONE)")" + validate_profile "$PROFILE" + + module="$(show_menu "$(msg APP_TITLE)" "$(msg MENU_CHOOSE_MODULE)" \ + "install" "$(msg MENU_INSTALL)" \ + "startstop" "$(msg MENU_STARTSTOP)" \ + "cleanup" "$(msg MENU_CLEANUP)" \ + "health" "$(msg MENU_HEALTH)" \ + "backup" "$(msg MENU_BACKUP)" \ + "restore" "$(msg MENU_RESTORE)" \ + "update" "$(msg MENU_UPDATE)" \ + "config" "$(msg MENU_CONFIG)" \ + "report" "$(msg MENU_REPORT)" \ + "smoke" "$(msg MENU_SMOKE)" \ + "cron" "$(msg MENU_CRON)")" + + case "$module" in + install) + action="$(show_menu "$(msg MENU_INSTALL)" "$(msg MENU_CHOOSE_ACTION)" \ + "bootstrap-server" "bootstrap-server" \ + "server-check" "server-check" \ + "prepare-ubuntu" "prepare-ubuntu" \ + "install-opensim-deps" "install-opensim-deps" \ + "install-dotnet8" "install-dotnet8" \ + "install-opensim" "install-opensim" \ + "configure-opensim" "configure-opensim" \ + "configure-database" "configure-database" \ + "compile-janus" "compile-janus" \ + "configure-janus" "configure-janus" \ + "install-janus" "install-janus")" + dispatch_module install --action "$action" + ;; + startstop) + if [[ "$PROFILE" == "grid-sim" ]]; then + target="$(show_menu "$(msg MENU_STARTSTOP)" "$(msg MENU_CHOOSE_TARGET)" \ + "grid" "$(msg TARGET_GRID)" \ + "robust" "$(msg TARGET_ROBUST)" \ + "sim1" "$(msg TARGET_SIM1)" \ + "janus" "$(msg TARGET_JANUS)")" + elif [[ "$PROFILE" == "robust" ]]; then + target="$(show_menu "$(msg MENU_STARTSTOP)" "$(msg MENU_CHOOSE_TARGET)" \ + "robust" "$(msg TARGET_ROBUST)" \ + "janus" "$(msg TARGET_JANUS)")" + else + target="$(show_menu "$(msg MENU_STARTSTOP)" "$(msg MENU_CHOOSE_TARGET)" \ + "standalone" "$(msg TARGET_STANDALONE)" \ + "janus" "$(msg TARGET_JANUS)")" + fi + + action="$(show_menu "$(msg MENU_STARTSTOP)" "$(msg MENU_CHOOSE_ACTION)" \ + "start" "$(msg ACTION_START)" \ + "stop" "$(msg ACTION_STOP)" \ + "restart" "$(msg ACTION_RESTART)")" + dispatch_module startstop --target "$target" --action "$action" + ;; + cleanup) + action="$(show_menu "$(msg MENU_CLEANUP)" "$(msg MENU_CHOOSE_ACTION)" \ + "cache-clean" "$(msg ACTION_CACHE_CLEAN)" \ + "log-clean" "$(msg ACTION_LOG_CLEAN)" \ + "tmp-clean" "$(msg ACTION_TMP_CLEAN)" \ + "reboot" "$(msg ACTION_REBOOT)")" + dispatch_module cleanup --action "$action" + ;; + health) + action="$(show_menu "$(msg MENU_HEALTH)" "$(msg MENU_CHOOSE_ACTION)" \ + "run" "$(msg ACTION_HEALTH_RUN)")" + dispatch_module health --action "$action" + ;; + backup) + action="$(show_menu "$(msg MENU_BACKUP)" "$(msg MENU_CHOOSE_ACTION)" \ + "db-backup" "$(msg ACTION_DB_BACKUP)" \ + "oar-backup" "$(msg ACTION_OAR_BACKUP)" \ + "full-backup" "$(msg ACTION_FULL_BACKUP)")" + dispatch_module backup --action "$action" + ;; + restore) + action="$(show_menu "$(msg MENU_RESTORE)" "$(msg MENU_CHOOSE_ACTION)" \ + "list-backups" "$(msg ACTION_RESTORE_LIST)" \ + "db-restore" "$(msg ACTION_RESTORE_DB)" \ + "oar-restore" "$(msg ACTION_RESTORE_OAR)" \ + "full-restore" "$(msg ACTION_RESTORE_FULL)")" + dispatch_module restore --action "$action" + ;; + update) + action="$(show_menu "$(msg MENU_UPDATE)" "$(msg MENU_CHOOSE_ACTION)" \ + "create-rollback" "$(msg ACTION_UPDATE_ROLLBACK)" \ + "list-rollbacks" "$(msg ACTION_UPDATE_LIST)" \ + "update-opensim" "$(msg ACTION_UPDATE_OPENSIM)" \ + "update-janus" "$(msg ACTION_UPDATE_JANUS)" \ + "full-update" "$(msg ACTION_UPDATE_FULL)")" + dispatch_module update --action "$action" + ;; + config) + action="$(show_menu "$(msg MENU_CONFIG)" "$(msg MENU_CHOOSE_ACTION)" \ + "validate-file" "$(msg ACTION_CONFIG_FILE)" \ + "validate-runtime" "$(msg ACTION_CONFIG_RUNTIME)")" + dispatch_module config --action "$action" + ;; + report) + action="$(show_menu "$(msg MENU_REPORT)" "$(msg MENU_CHOOSE_ACTION)" \ + "generate" "$(msg ACTION_REPORT_GENERATE)")" + dispatch_module report --action "$action" + ;; + smoke) + action="$(show_menu "$(msg MENU_SMOKE)" "$(msg MENU_CHOOSE_ACTION)" \ + "run" "$(msg ACTION_SMOKE_RUN)")" + dispatch_module smoke --action "$action" + ;; + cron) + action="$(show_menu "$(msg MENU_CRON)" "$(msg MENU_CHOOSE_ACTION)" \ + "install" "$(msg ACTION_CRON_INSTALL)" \ + "list" "$(msg ACTION_CRON_LIST)" \ + "remove" "$(msg ACTION_CRON_REMOVE)")" + dispatch_module cron --action "$action" + ;; + *) + die "$(msg ERR_UNKNOWN_UI_MODULE): $module" + ;; + esac +} + +MODE="cli" +MODULE="" +LANG_OVERRIDE="${OSM_LANG:-de}" +PROFILE="grid-sim" + +if [[ $# -eq 0 ]]; then + usage + exit 1 +fi + +while [[ $# -gt 0 ]]; do + case "$1" in + --mode) MODE="$2"; shift 2 ;; + --lang) LANG_OVERRIDE="$2"; shift 2 ;; + --profile) PROFILE="$2"; shift 2 ;; + --module) MODULE="$2"; shift 2 ;; + --help|-h) usage; exit 0 ;; + *) break ;; + esac +done + +set_language "$LANG_OVERRIDE" +validate_profile "$PROFILE" + +if [[ "$MODE" == "ui" ]]; then + ui_flow + exit 0 +fi + +[[ -n "$MODULE" ]] || die "$(msg ERR_MISSING_MODULE)" + +if [[ "$MODULE" == "startstop" ]] && ! args_have_target "$@"; then + dispatch_module "$MODULE" --target "$(default_target_for_profile "$PROFILE")" "$@" +else + dispatch_module "$MODULE" "$@" +fi diff --git a/osmtool_restart.sh b/osmtool_restart.sh new file mode 100644 index 0000000..c26808d --- /dev/null +++ b/osmtool_restart.sh @@ -0,0 +1,270 @@ +#!/bin/bash + +#* WARTEZEITEN muessen leider sein damit der Server nicht überfordert wird. +Simulator_Start_wait=15 # Sekunden +MoneyServer_Start_wait=30 # Sekunden +RobustServer_Start_wait=30 # Sekunden +Simulator_Stop_wait=15 # Sekunden +MoneyServer_Stop_wait=30 # Sekunden +RobustServer_Stop_wait=30 # Sekunden + +#?────────────────────────────────────────────────────────────────────────────────────────── +#* Start Stop Grid +#?────────────────────────────────────────────────────────────────────────────────────────── + +function opensimstart() { + echo "${SYM_WAIT} ${COLOR_START}Starte das Grid!${COLOR_RESET}" + + # 1. RobustServer (muss zuerst laufen) + if [[ -d "robust/bin" && -f "robust/bin/Robust.dll" ]]; then + echo "${SYM_OK} ${COLOR_START}Starte ${COLOR_SERVER}RobustServer ${COLOR_RESET} ${COLOR_START}aus ${COLOR_DIR}robust/bin...${COLOR_RESET}" + (cd robust/bin && screen -fa -S robustserver -d -U -m dotnet Robust.dll) + sleep "$RobustServer_Start_wait" + else + echo "${SYM_BAD} ${COLOR_SERVER}RobustServer: ${COLOR_BAD}Robust.dll nicht gefunden.${COLOR_RESET} ${COLOR_START}Überspringe Start.${COLOR_RESET}" + fi + + # 2. MoneyServer (optional) + if [[ -f "robust/bin/MoneyServer.dll" ]]; then + echo "${SYM_OK} ${COLOR_START}Starte ${COLOR_SERVER}MoneyServer${COLOR_RESET} ${COLOR_START}aus ${COLOR_DIR}robust/bin...${COLOR_RESET}" + (cd robust/bin && screen -fa -S moneyserver -d -U -m dotnet MoneyServer.dll) + sleep "$MoneyServer_Start_wait" + fi + + # 3. SIM1 ZUERST (sequentiell) + if [[ -d "sim1/bin" && -f "sim1/bin/OpenSim.dll" ]]; then + echo "${SYM_OK} ${COLOR_START}Starte ${COLOR_SERVER}sim1${COLOR_RESET} ${COLOR_START}(primäre Region)...${COLOR_RESET}" + (cd sim1/bin && screen -fa -S sim1 -d -U -m dotnet OpenSim.dll) + sleep "$Simulator_Start_wait" + else + echo "${SYM_BAD} ${COLOR_SERVER}sim1: ${COLOR_BAD}OpenSim.dll nicht gefunden.${COLOR_RESET}" + fi + + # 4. SIM2-SIM99 PARALLEL + echo "${SYM_INFO} ${COLOR_START}Starte sekundäre Simulatoren parallel...${COLOR_RESET}" + + # Maximal 10 parallele Jobs + MAX_JOBS=10 + for ((i=2; i<=99; i++)); do + sim_dir="sim$i/bin" + if [[ -d "$sim_dir" && -f "$sim_dir/OpenSim.dll" ]]; then + + # Warten falls zu viele Jobs laufen + while (( $(jobs -p | wc -l) >= MAX_JOBS )); do + sleep 1 + done + + echo "${SYM_OK} ${COLOR_START}Starte ${COLOR_SERVER}sim$i${COLOR_RESET} ${COLOR_START}aus ${COLOR_DIR}$sim_dir...${COLOR_RESET}" + (cd "$sim_dir" && screen -fa -S "sim$i" -d -U -m dotnet OpenSim.dll) & + fi + done + + wait # Warte auf alle Hintergrundjobs + echo "${SYM_OK} ${COLOR_START}Alle Simulatoren gestartet.${COLOR_RESET}" + echo . +} + +function opensimstop() { + echo "${SYM_WAIT} ${COLOR_STOP}Stoppe das Grid!${COLOR_RESET}" + local MAX_PARALLEL=10 # Maximale parallele Stopp-Vorgänge + + # 1. Paralleles Stoppen von sim999 bis sim2 + echo "${SYM_INFO} ${COLOR_STOP}Stoppe sekundäre Simulatoren parallel...${COLOR_RESET}" + for ((i=999; i>=2; i--)); do + sim_name="sim$i" + + # Warten falls zu viele parallele Jobs laufen + #while [ $(jobs -p | wc -l) -ge $MAX_PARALLEL ]; do + while [ "$(jobs -p | wc -l)" -ge $MAX_PARALLEL ]; do + sleep 0.5 + done + + if screen -list | grep -q "$sim_name"; then + echo "${SYM_OK} ${COLOR_STOP}Stoppe ${COLOR_SERVER}$sim_name${COLOR_RESET}" + ( + screen -S "$sim_name" -p 0 -X stuff "shutdown^M" + sleep $Simulator_Stop_wait + ) & + fi + done + wait # Warte auf alle parallelen Stopp-Vorgänge + + # 2. Sim1 sequentiell stoppen + if screen -list | grep -q "sim1"; then + echo "${SYM_OK} ${COLOR_STOP}Stoppe ${COLOR_SERVER}sim1${COLOR_RESET} ${COLOR_STOP}(primäre Region)...${COLOR_RESET}" + screen -S "sim1" -p 0 -X stuff "shutdown^M" + sleep $Simulator_Stop_wait + fi + + # 3. MoneyServer stoppen + if screen -list | grep -q "moneyserver"; then + echo "${SYM_OK} ${COLOR_STOP}Stoppe ${COLOR_SERVER}MoneyServer${COLOR_RESET} ${COLOR_STOP}...${COLOR_RESET}" + screen -S moneyserver -p 0 -X stuff "shutdown^M" + sleep $MoneyServer_Stop_wait + else + echo "${SYM_BAD} ${COLOR_SERVER}MoneyServer: ${COLOR_BAD}Läuft nicht.${COLOR_RESET}" + fi + + # 4. RobustServer stoppen (zuletzt) + if screen -list | grep -q "robustserver"; then + echo "${SYM_OK} ${COLOR_STOP}Stoppe ${COLOR_SERVER}RobustServer${COLOR_RESET} ${COLOR_STOP}...${COLOR_RESET}" + screen -S robustserver -p 0 -X stuff "shutdown^M" + sleep $RobustServer_Stop_wait + else + echo "${SYM_BAD} ${COLOR_SERVER}RobustServer: ${COLOR_BAD}Läuft nicht.${COLOR_RESET}" + fi + + echo "${SYM_OK} ${COLOR_STOP}Alle Dienste gestoppt.${COLOR_RESET}" + echo . +} + +# check_screens ist eine Grid Funktion und funktioniert nicht im Standalone. +# bash osmtool_backup.sh check_screens +function check_screens() { + # echo "Überprüfung der laufenden OpenSim-Prozesse..." + + restart_all=false + + # Überprüfen, ob RobustServer läuft + if [[ -d "robust/bin" && -f "robust/bin/Robust.dll" ]]; then + screenRO=$(screen -ls | grep -w "robustserver") + if [[ -z "$screenRO" ]]; then + echo "$(date '+%Y-%m-%d %H:%M:%S') - RobustServer läuft nicht und muss neu gestartet werden." >> ProblemRestarts.log + restart_all=true + fi + fi + + # Überprüfen, ob MoneyServer läuft + if [[ -d "robust/bin" && -f "robust/bin/MoneyServer.dll" ]];then + screenMoney=$(screen -ls | grep -w "moneyserver") + if [[ -z "$screenMoney" ]]; then + echo "$(date '+%Y-%m-%d %H:%M:%S') - MoneyServer läuft nicht und muss neu gestartet werden." >> ProblemRestarts.log + restart_all=true + fi + fi + + # Überprüfen, ob sim1 läuft + if [[ -d "sim1/bin" && -f "sim1/bin/OpenSim.dll" ]]; then + screenSim1=$(screen -ls | grep -w "sim1") + if [[ -z "$screenSim1" ]]; then + echo "$(date '+%Y-%m-%d %H:%M:%S') - Sim1 läuft nicht und muss neu gestartet werden." >> ProblemRestarts.log + restart_all=true + fi + fi + + # Falls eines der kritischen Systeme ausgefallen ist, alles neu starten + if [[ "$restart_all" == true ]]; then + echo "$(date '+%Y-%m-%d %H:%M:%S') - Kritische Prozesse sind ausgefallen! OpenSim wird komplett neu gestartet." >> ProblemRestarts.log + # opensimstart + opensimrestart + return 0 + fi + + # Überprüfen, ob andere Regionen (sim2 bis sim999) einzeln neu gestartet werden müssen + for ((i=2; i<=999; i++)); do + sim_dir="sim$i/bin" + if [[ -d "$sim_dir" && -f "$sim_dir/OpenSim.dll" ]]; then + screenSim=$(screen -ls | grep -w "sim$i") + if [[ -z "$screenSim" ]]; then + echo "$(date '+%Y-%m-%d %H:%M:%S') - Sim$i läuft nicht und wird einzeln neu gestartet." >> ProblemRestarts.log + cd "$sim_dir" || continue + screen -fa -S "sim$i" -d -U -m dotnet OpenSim.dll + cd - >/dev/null 2>&1 || continue + sleep $Simulator_Start_wait + fi + fi + done + echo . +} + +# Neu Test +function check_screens_neu() { + echo "${SYM_WAIT} ${COLOR_START}Überprüfe laufende OpenSim-Prozesse...${COLOR_RESET}" + restart_all=false + log_file="ProblemRestarts.log" + + timestamp="$(date '+%Y-%m-%d %H:%M:%S')" + + # Funktion zum Screen-Check + function screen_running() { + screen -ls | grep -qw "$1" + } + + # RobustServer prüfen + if [[ -d "robust/bin" && -f "robust/bin/Robust.dll" ]]; then + if ! screen_running "robustserver"; then + echo "$timestamp - RobustServer läuft nicht." >> "$log_file" + restart_all=true + fi + fi + + # MoneyServer prüfen + if [[ -f "robust/bin/MoneyServer.dll" ]]; then + if ! screen_running "moneyserver"; then + echo "$timestamp - MoneyServer läuft nicht." >> "$log_file" + restart_all=true + fi + fi + + # sim1 prüfen + if [[ -d "sim1/bin" && -f "sim1/bin/OpenSim.dll" ]]; then + if ! screen_running "sim1"; then + echo "$timestamp - sim1 läuft nicht." >> "$log_file" + restart_all=true + fi + fi + + # Falls kritische Komponenten fehlen → kompletten Neustart + if [[ "$restart_all" == true ]]; then + echo "$timestamp - Kritische Komponenten fehlen. Starte das gesamte Grid neu." >> "$log_file" + opensimrestart + return 0 + fi + + # sim2 bis sim999 einzeln prüfen und ggf. neu starten + for ((i=2; i<=999; i++)); do + sim_dir="sim$i/bin" + if [[ -d "$sim_dir" && -f "$sim_dir/OpenSim.dll" ]]; then + if ! screen_running "sim$i"; then + echo "$timestamp - sim$i läuft nicht. Starte Region neu." >> "$log_file" + cd "$sim_dir" || continue + screen -fa -S "sim$i" -d -U -m dotnet OpenSim.dll + cd - >/dev/null 2>&1 || continue + sleep $Simulator_Start_wait + fi + fi + done + + echo "${SYM_OK} ${COLOR_START}Überprüfung abgeschlossen.${COLOR_RESET}" + screen -ls || echo "Keine Screen-Sessions gefunden" +} + + +function opensimrestart() { + opensimstop + sleep $Simulator_Stop_wait # Wartezeit für Dienst-Stopp + opensimstart + screen -ls || echo "Keine Screen-Sessions gefunden" +} + + +KOMMANDO="$1" + +case $KOMMANDO in + "start") + opensimstart + ;; + "stop") + opensimstop + ;; + "restart") + opensimrestart + ;; + "check_screens") + check_screens_neu + ;; + *) + opensimrestart + ;; +esac +# Ende des Skripts \ No newline at end of file diff --git a/webphp/README.md b/webphp/README.md new file mode 100644 index 0000000..7becdd2 --- /dev/null +++ b/webphp/README.md @@ -0,0 +1,77 @@ +# OSMTool Web PHP (DE/EN/FR/ES) + +This folder contains a minimal and secure PHP web controller for `osmtool_main.sh`. + +## Features + +- Login-protected web UI +- Language switch: `de`, `en`, `fr`, `es` +- CSRF protection +- Whitelisted actions only +- Runner script wrapper for safer command execution + +## File Layout + +- `public/index.php` - web UI and request handling +- `src/bootstrap.php` - session, auth helpers, CSRF +- `src/i18n.php` - translations +- `src/runner.php` - command whitelist and execution +- `src/config.sample.php` - configuration template +- `bin/osmtool_web_runner.sh` - controlled bridge to `osmtool_main.sh` + +## Setup + +1. Typical Linux deploy path: + +```bash +sudo mkdir -p /var/www/html/osmtool-web +sudo cp -a webphp/. /var/www/html/osmtool-web/ +sudo chown -R www-data:www-data /var/www/html/osmtool-web +``` + +1. Edit config values: + +```bash +nano /var/www/html/osmtool-web/src/config.php +``` + +1. Set execute bit for runner and main script: + +```bash +chmod +x /var/www/html/osmtool-web/bin/osmtool_web_runner.sh +chmod +x /opt/opensimMULTITOOLS-II/osmtool_main.sh +``` + +1. Configure web server document root to: + +```text +/var/www/html/osmtool-web/public +``` + +1. Open in browser and log in with `web_password` from config. + +## Sudoers Example (recommended) + +Use `visudo` and allow only the web runner: + +```text +www-data ALL=(manni) NOPASSWD: /var/www/html/osmtool-web/bin/osmtool_web_runner.sh +``` + +Then keep `use_sudo=true` and `sudo_user=manni` in config. + +## Supported Actions + +- `start` +- `stop` +- `restart` +- `smoke` +- `report` +- `health` +- `cron-list` + +## Notes + +- Do not expose this UI publicly without HTTPS and IP restrictions. +- Keep the password strong and unique. +- Extend actions only in `src/runner.php` and `bin/osmtool_web_runner.sh` together. diff --git a/webphp/bin/osmtool_web_runner.sh b/webphp/bin/osmtool_web_runner.sh new file mode 100644 index 0000000..a3e75e6 --- /dev/null +++ b/webphp/bin/osmtool_web_runner.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +MAIN_SCRIPT="$PROJECT_ROOT/osmtool_main.sh" + +if [[ ! -x "$MAIN_SCRIPT" ]]; then + echo "ERROR: osmtool_main.sh not executable: $MAIN_SCRIPT" >&2 + exit 1 +fi + +ACTION="${1:-}" +PROFILE="${2:-grid-sim}" +LANG="${3:-de}" +WORKDIR="${4:-/opt}" + +case "$PROFILE" in + grid-sim|robust|standalone) ;; + *) echo "ERROR: invalid profile" >&2; exit 2 ;; +esac + +case "$LANG" in + de|en|fr|es) ;; + *) echo "ERROR: invalid language" >&2; exit 2 ;; +esac + +run_main() { + bash "$MAIN_SCRIPT" --mode cli --profile "$PROFILE" --lang "$LANG" --workdir "$WORKDIR" "$@" +} + +case "$ACTION" in + start) + run_main --module startstop --action start + ;; + stop) + run_main --module startstop --action stop + ;; + restart) + run_main --module startstop --action restart + ;; + smoke) + run_main --module smoke --action run + ;; + report) + run_main --module report --action generate + ;; + health) + run_main --module health --action run + ;; + cron-list) + run_main --module cron --action list + ;; + *) + echo "ERROR: unsupported action" >&2 + exit 2 + ;; +esac diff --git a/webphp/public/index.php b/webphp/public/index.php new file mode 100644 index 0000000..2dc29e6 --- /dev/null +++ b/webphp/public/index.php @@ -0,0 +1,264 @@ + t($lang, 'action_start'), + 'stop' => t($lang, 'action_stop'), + 'restart' => t($lang, 'action_restart'), + 'smoke' => t($lang, 'action_smoke'), + 'report' => t($lang, 'action_report'), + 'health' => t($lang, 'action_health'), + 'cron-list' => t($lang, 'action_cron_list'), +]; + +$profiles = [ + 'grid-sim' => 'grid-sim', + 'robust' => 'robust', + 'standalone' => 'standalone', +]; +?> + + + + + + <?= h(t($lang, 'title')) ?> + + + +
+
+
+
+

+

+
+ +
+ + +
+ + + +
+ + + +
+
+ + +
+
+ +
+
+
+ +

+
+ + + + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+
+ + +
+ +
+
+ + +
+
+ + diff --git a/webphp/src/bootstrap.php b/webphp/src/bootstrap.php new file mode 100644 index 0000000..6e461fa --- /dev/null +++ b/webphp/src/bootstrap.php @@ -0,0 +1,64 @@ + 0, + 'path' => '/', + 'secure' => false, + 'httponly' => true, + 'samesite' => 'Strict', +]); + +session_start(); + +if (!isset($_SESSION['session_seed'])) { + $_SESSION['session_seed'] = hash('sha256', $config['session_key'] . random_int(1000, 999999)); +} + +require_once __DIR__ . '/i18n.php'; + +function is_logged_in(): bool +{ + return isset($_SESSION['auth']) && $_SESSION['auth'] === true; +} + +function require_login(): void +{ + if (!is_logged_in()) { + header('Location: index.php'); + exit; + } +} + +function csrf_token(): string +{ + if (!isset($_SESSION['csrf_token'])) { + $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); + } + return $_SESSION['csrf_token']; +} + +function verify_csrf(?string $token): bool +{ + if (!isset($_SESSION['csrf_token']) || !is_string($token)) { + return false; + } + return hash_equals($_SESSION['csrf_token'], $token); +} + +function h(string $value): string +{ + return htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); +} diff --git a/webphp/src/config.php b/webphp/src/config.php new file mode 100644 index 0000000..9106c84 --- /dev/null +++ b/webphp/src/config.php @@ -0,0 +1,13 @@ + 'change-me-super-random-session-key', + 'web_password' => 'ChangeMeNow-2026', + // Default assumes deployment under /var/www/html/osmtool-web + 'runner_path' => realpath(__DIR__ . '/../bin/osmtool_web_runner.sh') ?: (__DIR__ . '/../bin/osmtool_web_runner.sh'), + 'command_timeout_seconds' => 120, + 'default_workdir' => '/opt', + 'use_sudo' => true, + 'sudo_user' => 'manni', +]; diff --git a/webphp/src/config.sample.php b/webphp/src/config.sample.php new file mode 100644 index 0000000..27cf511 --- /dev/null +++ b/webphp/src/config.sample.php @@ -0,0 +1,27 @@ + 'change-me-super-random-session-key', + + // Default login password. Replace immediately in production. + 'web_password' => 'ChangeMeNow-2026', + + // Default assumes deployment under /var/www/html/osmtool-web. + // You can keep this dynamic path or set an absolute path manually. + 'runner_path' => realpath(__DIR__ . '/../bin/osmtool_web_runner.sh') ?: (__DIR__ . '/../bin/osmtool_web_runner.sh'), + + // Web timeout in seconds. + 'command_timeout_seconds' => 120, + + // Default workdir passed to osmtool_main.sh. + 'default_workdir' => '/opt', + + // Allow running through sudo if needed. + 'use_sudo' => true, + + // If use_sudo=true, command becomes: sudo -n + // Example: www-data (Linux) or your service account. + 'sudo_user' => 'manni', +]; diff --git a/webphp/src/i18n.php b/webphp/src/i18n.php new file mode 100644 index 0000000..8e641ac --- /dev/null +++ b/webphp/src/i18n.php @@ -0,0 +1,140 @@ + [ + 'title' => 'OSMTool Web Steuerung', + 'subtitle' => 'Sichere Web-Oberflaeche fuer OpenSim Aktionen', + 'login_title' => 'Anmeldung', + 'password' => 'Passwort', + 'login' => 'Einloggen', + 'logout' => 'Ausloggen', + 'language' => 'Sprache', + 'profile' => 'Profil', + 'action' => 'Aktion', + 'execute' => 'Ausfuehren', + 'workdir' => 'Workdir', + 'output' => 'Ausgabe', + 'status_ok' => 'Erfolgreich', + 'status_error' => 'Fehler', + 'invalid_login' => 'Anmeldung fehlgeschlagen.', + 'csrf_error' => 'Ungueltiges Formular-Token.', + 'not_allowed' => 'Aktion nicht erlaubt.', + 'run_failed' => 'Ausfuehrung fehlgeschlagen.', + 'hint' => 'Es werden nur freigegebene Aktionen ausgefuehrt.', + 'action_start' => 'Start', + 'action_stop' => 'Stop', + 'action_restart' => 'Neustart', + 'action_smoke' => 'Smoke-Test', + 'action_report' => 'Report erzeugen', + 'action_health' => 'Healthcheck', + 'action_cron_list' => 'Cronjobs anzeigen', + ], + 'en' => [ + 'title' => 'OSMTool Web Control', + 'subtitle' => 'Secure web interface for OpenSim actions', + 'login_title' => 'Login', + 'password' => 'Password', + 'login' => 'Sign in', + 'logout' => 'Sign out', + 'language' => 'Language', + 'profile' => 'Profile', + 'action' => 'Action', + 'execute' => 'Execute', + 'workdir' => 'Workdir', + 'output' => 'Output', + 'status_ok' => 'Success', + 'status_error' => 'Error', + 'invalid_login' => 'Login failed.', + 'csrf_error' => 'Invalid form token.', + 'not_allowed' => 'Action not allowed.', + 'run_failed' => 'Execution failed.', + 'hint' => 'Only whitelisted actions are executed.', + 'action_start' => 'Start', + 'action_stop' => 'Stop', + 'action_restart' => 'Restart', + 'action_smoke' => 'Smoke test', + 'action_report' => 'Generate report', + 'action_health' => 'Health check', + 'action_cron_list' => 'List cron jobs', + ], + 'fr' => [ + 'title' => 'Controle Web OSMTool', + 'subtitle' => 'Interface web securisee pour les actions OpenSim', + 'login_title' => 'Connexion', + 'password' => 'Mot de passe', + 'login' => 'Se connecter', + 'logout' => 'Se deconnecter', + 'language' => 'Langue', + 'profile' => 'Profil', + 'action' => 'Action', + 'execute' => 'Executer', + 'workdir' => 'Repertoire de travail', + 'output' => 'Sortie', + 'status_ok' => 'Succes', + 'status_error' => 'Erreur', + 'invalid_login' => 'Echec de connexion.', + 'csrf_error' => 'Jeton de formulaire invalide.', + 'not_allowed' => 'Action non autorisee.', + 'run_failed' => 'Execution echouee.', + 'hint' => 'Seules les actions en liste blanche sont executees.', + 'action_start' => 'Demarrer', + 'action_stop' => 'Arreter', + 'action_restart' => 'Redemarrer', + 'action_smoke' => 'Test smoke', + 'action_report' => 'Generer un rapport', + 'action_health' => 'Controle de sante', + 'action_cron_list' => 'Lister les cron jobs', + ], + 'es' => [ + 'title' => 'Control Web OSMTool', + 'subtitle' => 'Interfaz web segura para acciones de OpenSim', + 'login_title' => 'Inicio de sesion', + 'password' => 'Contrasena', + 'login' => 'Entrar', + 'logout' => 'Salir', + 'language' => 'Idioma', + 'profile' => 'Perfil', + 'action' => 'Accion', + 'execute' => 'Ejecutar', + 'workdir' => 'Directorio de trabajo', + 'output' => 'Salida', + 'status_ok' => 'Correcto', + 'status_error' => 'Error', + 'invalid_login' => 'Error de inicio de sesion.', + 'csrf_error' => 'Token de formulario invalido.', + 'not_allowed' => 'Accion no permitida.', + 'run_failed' => 'La ejecucion fallo.', + 'hint' => 'Solo se ejecutan acciones permitidas.', + 'action_start' => 'Iniciar', + 'action_stop' => 'Detener', + 'action_restart' => 'Reiniciar', + 'action_smoke' => 'Prueba smoke', + 'action_report' => 'Generar informe', + 'action_health' => 'Chequeo de salud', + 'action_cron_list' => 'Listar cron jobs', + ], + ]; +} + +function detect_lang(array $supported): string +{ + $lang = $_GET['lang'] ?? $_POST['lang'] ?? $_SESSION['lang'] ?? 'de'; + if (!is_string($lang) || !in_array($lang, $supported, true)) { + return 'de'; + } + return $lang; +} + +function t(string $lang, string $key): string +{ + $all = translations(); + return $all[$lang][$key] ?? $all['en'][$key] ?? $key; +} diff --git a/webphp/src/runner.php b/webphp/src/runner.php new file mode 100644 index 0000000..9ff87d7 --- /dev/null +++ b/webphp/src/runner.php @@ -0,0 +1,77 @@ + false, 'exitCode' => 2, 'output' => "ERROR: action not allowed\n"]; + } + + if (!in_array($profile, allowed_profiles(), true)) { + return ['ok' => false, 'exitCode' => 2, 'output' => "ERROR: invalid profile\n"]; + } + + if (!in_array($lang, supported_languages(), true)) { + return ['ok' => false, 'exitCode' => 2, 'output' => "ERROR: invalid language\n"]; + } + + $workdirClean = trim($workdir); + if ($workdirClean === '') { + $workdirClean = (string)($config['default_workdir'] ?? '/opt'); + } + + $cmd = build_command($config, $action, $profile, $lang, $workdirClean); + $descriptorSpec = [ + 0 => ['pipe', 'r'], + 1 => ['pipe', 'w'], + 2 => ['pipe', 'w'], + ]; + + $process = proc_open($cmd, $descriptorSpec, $pipes); + if (!is_resource($process)) { + return ['ok' => false, 'exitCode' => 1, 'output' => "ERROR: unable to start process\n"]; + } + + fclose($pipes[0]); + + $stdout = stream_get_contents($pipes[1]); + $stderr = stream_get_contents($pipes[2]); + + fclose($pipes[1]); + fclose($pipes[2]); + + $exitCode = proc_close($process); + $output = (string)$stdout . (string)$stderr; + + return ['ok' => $exitCode === 0, 'exitCode' => $exitCode, 'output' => $output]; +}