Sim Start | Stop script - Gau Hax - 10.02.2026
Fast drei Monate habe ich jetzt wieder Opensimulator und die Server am Wickel.
Heute möchte ich euch mein sim_start_stop.sh script – Projektstatus & Dokumentationsanker vorstellen.
Nach dem die neu aufgebauten Debian GNU/Linux 13 (trixie) und Ubuntu Ubuntu 22.04 (jammy) Server Laufen,
ist jetzt OpenSim 0.9.3 in Brid-Berlin am laufen.
Es hat mich einiges an zeit gekostet meine Alten scripte zu sortieren, aufzuräumen, auszumülllen, anzupassen und kann sie hier heute stabil euch zeigen.
Ach und was ganz WICHTIGES, das script nimt keine änderungen an eurern Opensim instalationen vor!!
Ziel des Projekts:
- Robustes, nachvollziehbares Start/Stop/Status-Script für OpenSim
- Fokus auf Stabilität, klare Prüfungen und saubere Fehlermeldungen
Getestete / Zielumgebung:
- Ubuntu (aktuelle LTS)
- Debian 13
- Bash-Scripting
- OpenSim mit Robust (local / remote / auto)
Bewusste Designentscheidungen:
- Keine Abhängigkeit von OpenSim-Konfigurationsstrings
- Keine Modifikation bestehender OpenSim-Konfigurationen
- Prüfungen erfolgen ausschließlich scriptseitig
- PID-Prüfungen nur als Rückverifizierung, niemals allein entscheidend
- Dienste müssen erreichbar sein, nicht nur „lokal laufen“
Robust-Prüfung:
- Modus: local / remote / auto
- Prüfung der tatsächlichen Erreichbarkeit (Ports)
- OpenSim-Instanzen starten nur, wenn ein passender Robust erreichbar ist
- Mehrere Robust-Instanzen (z. B. Grid, Assets, Login, Testgrid) sind berücksichtigt
MySQL-Prüfung:
- Analog zur Robust-Logik umgesetzt
- Local / remote / auto möglich
- Keine Abhängigkeit von EstateConnectionString
- Klare, explizite MySQL-Parameter im Script
Was ich mir von der Community wünsche:
- Feedback zu Designentscheidungen
- Erfahrungen aus anderen Systemumgebungen
- Hinweise auf reale Edge-Cases
- Sachliche Kritik, keine Grundsatzdebatten
Code: #!/usr/bin/env bash
# ==========================================================
# sim_start_stop.sh
# ----------------------------------------------------------
# OpenSimulator Start | Stop | Status Script
#
# Version: 0.5.3
# OpenSim: 0.9.3
# Getestet unter: Ubuntu / Debian (mit MySQL)
#
# Lizenz: BSD (Open Source)
# Autor: Gau Hax
# Mail: gauhax@vamp-berlin.de
#
# Zweck:
# - Starten, Stoppen und Statusabfrage einzelner
# OpenSimulator-Instanzen in einem Multi-Screen-Setup
#
# Philosophie:
# - Klar, robust, admin-tauglich
# - Keine Magie, keine Endlosschleifen
# - Fehler sichtbar machen, nicht verstecken
# ==========================================================
# -----------------------------
# Parameter ( Start )
# -----------------------------
AKTION="$1"
INSTANZ="$2"
if [[ ! "${AKTION}" =~ ^(start|stop|status)$ ]]; then
echo "Nutzung: $0 start|stop|status <1-20>"
exit 1
fi
if [[ ! "${INSTANZ}" =~ ^[0-9]+$ ]] || (( INSTANZ < 1 || INSTANZ > 20 )); then
echo "Instanz muss eine Zahl von 1 bis 20 sein"
exit 1
fi
INSTNUM="$(printf "%02d" "${INSTANZ}")"
INSTTAG="Inst_${INSTNUM}"
STATUS_MODUS=false
[[ "${AKTION}" == "status" ]] && STATUS_MODUS=true
abbruch_oder_status() {
[[ "${STATUS_MODUS}" == false ]] && exit 1
}
# ----------------------------------------------------------
# Statische Konfiguration ( Robust / MySql / Verzeichnisse )
# ----------------------------------------------------------
# --- Robust Grid Service ---
ROBUST_MODE="auto" # local | remote | auto
ROBUST_HOST="127.0.0.1" # Host oder IP
ROBUST_PORT_1="8002"
ROBUST_PORT_2="8003"
ROBUST_TAG="Robust" # -smtag=Robust z.b.
# --- MySQL ---
MYSQL_MODE="auto" # local | remote | auto
MYSQL_HOST="127.0.0.1" # Host oder IP
MYSQL_PORT="3306"
MYSQL_DB="opensim-datenbank" # Anpassen DB
# --- Basis & Pfade ---
#
# Start Verzeichniss von OpenSimulator / Anpassen !
#
BASIS_VERZEICHNIS="/opt/opensim"
ARBEITS_VERZEICHNIS="${BASIS_VERZEICHNIS}/bin_${INSTNUM}"
# PID Verzeichniss = OpenSim.ini / Anpassen !
PID_DATEI="/opt/opensim/run/pid/${INSTTAG}.pid"
# Log Datein Verzeichniss
LOG_VERZEICHNIS="/opt/opensim/log"
LOG_DATEI="${LOG_VERZEICHNIS}/sim_start_stop${INSTNUM}.log"
SCREEN_HAUPT="HAUPSCREEN-NAME" # z.b. Simss / Simss_01 !!
ERWARTETER_USER="OPENSIMUNSER" # euer User vom System für Opensim!
mkdir -p "${LOG_VERZEICHNIS}"
# -----------------------------
# Pflichtdateien – Definition
# -----------------------------
# Dateien direkt im Arbeitsverzeichnis (bin_XX)
PFLICHT_DATEIEN_ROOT=(
"OpenSim.ini"
"OpenSimDefaults.ini"
)
# Dateien unter config-include/
# Sind hier als vorlage für ... !
PFLICHT_DATEIEN_CONFIG=(
"GridCommon.ini"
"FlotsamCache.ini"
)
# Regions-Datei (Sonderpfad)
# Ich gehe hier davon aus, das script, es es mindestens
# eine "Regions.ini" Datei für eine Inst hat
REGION_DATEI="${ARBEITS_VERZEICHNIS}/Regions/Regions.ini"
# -----------------------------
# Pflicht Datein Prüfung (erweiterung)
# ERWEITERUNG !!! in vorbereitung !
# -----------------------------
## PFLICHT_DATEIEN_CONFIG+=( "NeueDatei.ini" )
# -----------------------------
# Farben (Konsole)
# muss ja schön aussehen
# Danke an Manfred Aabye für die Inspiration
# -----------------------------
## C_RESET="\033[0m"
## C_INFO="\033[1;34m"
## C_WARN="\033[1;33m"
## C_ERR="\033[1;31m"
## C_OK="\033[1;32m"
C_RESET="\033[0m"
C_INFO="\033[1;33m"
C_WARN="\033[33;1m"
C_ERR="\033[31m"
C_OK="\033[32m"
# -----------------------------
# Logging
# Console mit Farbe, Datei mit Datum und Uhrzeit
# -----------------------------
log_console() {
echo -e "$2[$1]${C_RESET} $3"
}
log_file() {
echo "[ $(date '+%Y-%m-%d %H:%M:%S') ] [$1] $2" >> "${LOG_DATEI}"
}
log_info() { log_console INFO "${C_INFO}" "$1"; log_file INFO "$1"; }
log_warn() { log_console WARN "${C_WARN}" "$1"; log_file WARN "$1"; }
log_err() { log_console FEHLER "${C_ERR}" "$1"; log_file FEHLER "$1"; }
log_ok() { log_console OK "${C_OK}" "$1"; log_file OK "$1"; }
# -----------------------------
# Robust Prüfung
# -----------------------------
check_robust() {
log_info "Robust-Prüfung gestartet (Modus: ${ROBUST_MODE})"
# -------------------------
# Port-Check Funktion
# -------------------------
check_ports() {
local ok=true
for port in "${ROBUST_PORT_1}" "${ROBUST_PORT_2}"; do
if timeout 2 bash -c "</dev/tcp/${ROBUST_HOST}/${port}" 2>/dev/null; then
log_ok "Robust-Port erreichbar: ${ROBUST_HOST}:${port}"
else
log_err "Robust-Port NICHT erreichbar: ${ROBUST_HOST}:${port}"
ok=false
fi
done
[[ "${ok}" == true ]]
}
# -------------------------
# Prozess-Check Funktion
# -------------------------
check_process() {
local found=false
while read -r pid; do
CMD="$(tr '\0' ' ' < /proc/${pid}/cmdline 2>/dev/null)"
if echo "${CMD}" | grep -qi "Robust" && echo "${CMD}" | grep -q -- "-smtag=${ROBUST_TAG}"; then
log_ok "Robust-Prozess gefunden (PID=${pid})"
found=true
break
fi
done < <(pgrep -f dotnet || true)
[[ "${found}" == true ]]
}
# -------------------------
# Modus-Auswertung
# -------------------------
case "${ROBUST_MODE}" in
local)
log_info "Robust LOCAL: Prozessprüfung"
check_process || {
log_err "Lokaler Robust läuft nicht"
return 1
}
;;
remote)
log_info "Robust REMOTE: Portprüfung"
check_ports || {
log_err "Remote Robust nicht erreichbar"
return 1
}
;;
auto)
log_info "Robust AUTO: Erkennung local / remote"
if check_process; then
log_ok "Robust lokal erkannt"
return 0
fi
log_warn "Kein lokaler Robust – prüfe Remote"
check_ports || {
log_err "Robust weder lokal noch remote erreichbar"
return 1
}
;;
*)
log_err "Unbekannter ROBUST_MODE: ${ROBUST_MODE}"
return 1
;;
esac
log_ok "Robust-Prüfung erfolgreich"
return 0
}
# ----------------------------------------------------------
# MySQL-Prüfung (analog Robust)
# ----------------------------------------------------------
check_mysql() {
log_info "MySQL-Prüfung gestartet (Modus: ${MYSQL_MODE})"
# -------------------------
# Port-Check Funktion
# -------------------------
check_port() {
if timeout 2 bash -c "</dev/tcp/${MYSQL_HOST}/${MYSQL_PORT}" 2>/dev/null; then
log_ok "MySQL-Port erreichbar: ${MYSQL_HOST}:${MYSQL_PORT}"
return 0
else
log_err "MySQL-Port NICHT erreichbar: ${MYSQL_HOST}:${MYSQL_PORT}"
return 1
fi
}
# -------------------------
# Lokaler Dienst-Check
# -------------------------
check_local_service() {
if mysqladmin ping --silent >/dev/null 2>&1; then
log_ok "Lokaler MySQL-Dienst erreichbar"
return 0
else
log_err "Lokaler MySQL-Dienst nicht erreichbar"
return 1
fi
}
# -------------------------
# Datenbank-Check
# -------------------------
check_database() {
if mysql -e "USE ${MYSQL_DB};" >/dev/null 2>&1; then
log_ok "MySQL-Datenbank '${MYSQL_DB}' erreichbar"
return 0
else
log_err "MySQL-Datenbank '${MYSQL_DB}' nicht erreichbar"
return 1
fi
}
# -------------------------
# Modus-Auswertung
# -------------------------
case "${MYSQL_MODE}" in
local)
log_info "MySQL LOCAL: Dienstprüfung"
check_local_service || return 1
;;
remote)
log_info "MySQL REMOTE: Portprüfung"
check_port || return 1
;;
auto)
log_info "MySQL AUTO: Erkennung local / remote"
if check_local_service; then
log_ok "MySQL lokal erkannt"
else
log_warn "Kein lokaler MySQL – prüfe Remote"
check_port || {
log_err "MySQL weder lokal noch remote erreichbar"
return 1
}
fi
;;
*)
log_err "Unbekannter MYSQL_MODE: ${MYSQL_MODE}"
return 1
;;
esac
# -------------------------
# Datenbank immer prüfen
# -------------------------
check_database || return 1
log_ok "MySQL-Datenbank-Prüfung erfolgreich"
return 0
}
# -----------------------------
# Benutzerprüfung
# Läuft OpenSim unter dem richtigen SystemBenutzer
# -----------------------------
AKTUELLER_USER="$(whoami)"
if [[ "${AKTUELLER_USER}" != "${ERWARTETER_USER}" ]]; then
log_warn "Script läuft als '${AKTUELLER_USER}', erwartet '${ERWARTETER_USER}'"
else
log_ok "Script läuft unter korrektem Benutzer '${ERWARTETER_USER}'"
fi
# -----------------------------
# Host & IP
# Hier prüfen / ermitteln IP / Host
# -----------------------------
HOST="$(hostname)"
HOSTIP="$(
ip route get 1.1.1.1 2>/dev/null \
| awk '{for(i=1;i<=NF;i++) if ($i=="src") {print $(i+1); exit}}'
)"
[[ -n "${HOSTIP}" ]] \
&& log_ok "System: HOST=${HOST}, IP=${HOSTIP}" \
|| log_err "System-IP konnte nicht ermittelt werden"
# -----------------------------
# Arbeitsverzeichnis
# -----------------------------
if [[ ! -d "${ARBEITS_VERZEICHNIS}" ]]; then
log_err "Arbeitsverzeichnis fehlt: ${ARBEITS_VERZEICHNIS}"
abbruch_oder_status
else
log_ok "Arbeitsverzeichnis vorhanden"
fi
# -----------------------------
# Pflichtdateien prüfen
# -----------------------------
# Root-Dateien
for datei in "${PFLICHT_DATEIEN_ROOT[@]}"; do
pfad="${ARBEITS_VERZEICHNIS}/${datei}"
if [[ ! -f "${pfad}" ]]; then
log_err "Pflichtdatei fehlt: ${pfad}"
abbruch_oder_status
elif [[ ! -s "${pfad}" ]]; then
log_err "Pflichtdatei ist leer: ${pfad}"
abbruch_oder_status
else
log_ok "Pflichtdatei geprüft: ${datei}"
fi
done
# config-include-Dateien
for datei in "${PFLICHT_DATEIEN_CONFIG[@]}"; do
pfad="${ARBEITS_VERZEICHNIS}/config-include/${datei}"
if [[ ! -f "${pfad}" ]]; then
log_err "Pflichtdatei fehlt: ${pfad}"
abbruch_oder_status
elif [[ ! -s "${pfad}" ]]; then
log_err "Pflichtdatei ist leer: ${pfad}"
abbruch_oder_status
else
log_ok "Pflichtdatei geprüft: config-include/${datei}"
fi
done
# Regions.ini
if [[ ! -f "${REGION_DATEI}" ]]; then
log_err "Regions-Datei fehlt: ${REGION_DATEI}"
abbruch_oder_status
elif [[ ! -s "${REGION_DATEI}" ]]; then
log_err "Regions-Datei ist leer: ${REGION_DATEI}"
abbruch_oder_status
else
log_ok "Regions-Datei geprüft: Regions.ini"
fi
# -----------------------------
# Regions.ini auswerten
# -----------------------------
REGIONS_NAME="$(
awk -F'[][]' '/^\[/{print $2; exit}' "${REGION_DATEI}" \
| sed 's/^[[:space:]]*//; s/[[:space:]]*$//'
)"
REGIONS_UUID="$(
grep -E '^RegionUUID[[:space:]]*=' "${REGION_DATEI}" \
| awk -F '=' '{gsub(/[[:space:]]/, "", $2); print $2}' \
| head -n1
)"
[[ -n "${REGIONS_NAME}" ]] \
&& log_ok "Regionsname: ${REGIONS_NAME}" \
|| log_warn "Kein Regionsname gefunden"
[[ -n "${REGIONS_UUID}" ]] \
&& log_ok "RegionUUID: ${REGIONS_UUID}" \
|| log_warn "Keine RegionUUID gefunden"
# -----------------------------
# Port-Prüfung
# -----------------------------
OS_PORT="$(
grep -E '^[[:space:]]*http_listener_port[[:space:]]*=' \
"${ARBEITS_VERZEICHNIS}/OpenSim.ini" \
| awk -F '=' '{gsub(/[[:space:]]/, "", $2); print $2}' \
| head -n1
)"
REGION_PORTS="$(
grep -E '^[[:space:]]*InternalPort[[:space:]]*=' "${REGION_DATEI}" \
| awk -F '=' '{gsub(/[[:space:]]/, "", $2); print $2}'
)"
[[ -n "${OS_PORT}" ]] \
&& log_ok "OpenSim Listener-Port: ${OS_PORT}" \
|| { log_err "Kein http_listener_port gefunden"; abbruch_oder_status; }
for rport in ${REGION_PORTS}; do
[[ "${rport}" == "${OS_PORT}" ]] \
&& log_ok "Region-Port ${rport} passt" \
|| log_warn "Region-Port ${rport} weicht ab"
done
# ==========================================================
# PIDFile Prüfung (OpenSim.ini) – robust (mit/ohne Anführungszeichen)
#
# ==========================================================
PIDFILE_INI_RAW="$(
grep -E '^[[:space:]]*PIDFile[[:space:]]*=' \
"${ARBEITS_VERZEICHNIS}/OpenSim.ini" \
| head -n1 \
| awk -F '=' '{print $2}'
)"
PIDFILE_INI="$(
echo "${PIDFILE_INI_RAW}" \
| sed 's/^[[:space:]]*//; s/[[:space:]]*$//' \
| sed 's/^"//; s/"$//'
)"
if [[ -z "${PIDFILE_INI}" ]]; then
log_err "PIDFile ist nicht in OpenSim.ini gesetzt"
abbruch_oder_status
elif [[ "${PIDFILE_INI}" != "${PID_DATEI}" ]]; then
log_err "PIDFile stimmt nicht"
log_err "Gefunden : ${PIDFILE_INI}"
log_err "Erwartet: ${PID_DATEI}"
abbruch_oder_status
else
log_ok "PIDFile korrekt gesetzt (${PIDFILE_INI})"
fi
# ==========================================================
# AKTIONEN ( start | stop | status )
# ==========================================================
case "${AKTION}" in
# -----------------------------
# STATUS
# Läuft komplett durch, auch bei Fehlern !
# -----------------------------
status)
if [[ -f "${PID_DATEI}" ]]; then
PID="$(cat "${PID_DATEI}")"
if [[ -n "${PID}" && -d "/proc/${PID}" ]]; then
CMD="$(tr '\0' ' ' < /proc/${PID}/cmdline)"
if echo "${CMD}" | grep -q -- "-smtag=${INSTTAG}"; then
log_ok "Instanz läuft (PID=${PID})"
else
log_warn "PID aktiv, aber falscher Prozess"
fi
else
log_warn "Verwaiste PID-Datei gefunden"
fi
else
log_info "Instanz läuft nicht"
fi
if [[ "${AKTION}" == "status" ]]; then
check_robust \
&& log_ok "Robust-Status: OK" \
|| log_warn "Robust-Status: NICHT erreichbar"
check_mysql \
&& log_ok "MySQL-Status: OK" \
|| log_warn "MySQL-Status: FEHLER"
fi
;;
# -----------------------------
# START
# Hier muss alles passen
# ansonsten abbruch
# -----------------------------
start)
log_ok "Startbefehl gesendet - Anfang"
# check_robust || { log_err "Start abgebrochen: Robust nicht verfügbar"; exit 1; }
check_mysql || { log_err "Start abgebrochen: MySQL nicht verfügbar"; exit 1; }
if [[ "${AKTION}" == "start" ]]; then
check_robust || {
log_err "Start abgebrochen: Robust nicht verfügbar"
exit 1
}
fi
if [[ -f "${PID_DATEI}" ]]; then
PID="$(cat "${PID_DATEI}")"
if [[ -n "${PID}" && -d "/proc/${PID}" ]]; then
log_warn "Instanz läuft bereits (PID=${PID})"
exit 0
else
log_warn "Verwaiste PID-Datei entfernt"
rm -f "${PID_DATEI}"
fi
fi
if ! screen -list | grep -q "\.${SCREEN_HAUPT}"; then
log_err "Hauptscreen '${SCREEN_HAUPT}' existiert nicht"
exit 1
fi
log_info "Starte Instanz ${INSTTAG} (${REGIONS_NAME})"
screen -S "${SCREEN_HAUPT}" -p 0 -X screen -t "${REGIONS_NAME}" "${INSTNUM}"
sleep 0.5
screen -S "${SCREEN_HAUPT}" -p "${INSTNUM}" -X stuff \
"cd ${ARBEITS_VERZEICHNIS} && dotnet OpenSim.dll -smtag=${INSTTAG}^M"
log_ok "Startbefehl gesendet - Ende"
;;
# -----------------------------
# STOP
# mit kontrolle auf sauberes ENDE
# -----------------------------
stop)
if [[ ! -f "${PID_DATEI}" ]]; then
log_warn "Keine PID-Datei – Instanz läuft nicht"
exit 0
fi
PID="$(cat "${PID_DATEI}")"
log_info "Stoppe Instanz ${INSTTAG} (${REGIONS_NAME})"
screen -S "${SCREEN_HAUPT}" -p "${INSTNUM}" -X stuff \
"alert Die Region ${REGIONS_NAME} wird in 2 Minuten gestoppt.^M"
sleep 0.3
screen -S "${SCREEN_HAUPT}" -p "${INSTNUM}" -X stuff "login disable^M"
sleep 120
screen -S "${SCREEN_HAUPT}" -p "${INSTNUM}" -X stuff \
"alert Die Region ${REGIONS_NAME} wird in 1 Minute gestoppt.^M"
sleep 45
screen -S "${SCREEN_HAUPT}" -p "${INSTNUM}" -X stuff \
"alert Die Region ${REGIONS_NAME} wird in 15 Sekunden gestoppt.^M"
sleep 15
screen -S "${SCREEN_HAUPT}" -p "${INSTNUM}" -X stuff "quit^M"
sleep 5
screen -S "${SCREEN_HAUPT}" -p "${INSTNUM}" -X stuff "exit^M"
# -------------------------
# Warte Zyklus auf sauber Stop
# -------------------------
for ((i=0;i<20;i++)); do
sleep 15
[[ ! -d "/proc/${PID}" ]] && break
done
if [[ -d "/proc/${PID}" ]]; then
log_warn "Prozess hängt – hartes KILL"
kill -9 "${PID}"
fi
rm -f "${PID_DATEI}"
log_ok "Instanz sauber gestoppt"
;;
esac
exit 0
Kurze erklärung zu dem Script.
Eine genaue Erklärung, Anleitung unter Opensim Start Stop Script
Im Kopf sind alle wichtigen angaben zu machen für Robus und MySql den Verzeichnissen und SystemUser unter dem Opemsim Läuft.
Vorraussetzungen
- Laufender HAUPTSCREEN
" Ein HauptScreen wird bei mir mit dem System automatsch gestartet "
" Wer kein script für ein "Hauptscreen hat, kann sich gerne melden"
- Opensim muss auf dem System Lauffähig vorliegen
"keine nachinstalationen durch das script"
- Robust muss erreichbar sein local | remote | auto
- MySql ebenso local | remote | auto
- eine vorhandene ( .my.cnf ) im stamverzeichniss des Opensim Users
[client]
user=opensimuser
password=suppergeheim
muss identisch zu dem User sein der in Opensimulator ist, und zugriff auf die entsprechende Datenbank haben
Hier jetzt mal ein Beispiel zu der Gesamt Konfiguration
Stamm Verzeichniss /opt/opensim
Instanzen bin_01
bin_02 ... usw
eine Instanz wird immer von 1 -20 aufgerufen, daraus wird an nötigen stellen 1 = 01 / 2 = 02 ... usw ist für die bin_xx für den INSTTAG (Inst_01) und den screen (1, 2 ,3 ... ) verwendet
Instanzlogik:
- INSTNUM = zweistellig (01–20)
- INSTTAG = Inst_XX
- smtag = -smtag=INSTTAG (zwingend)
Opensim aufruf / start ( dotnet OpenSim.dll -smtag=Inst_XX ) xx ersetzt das script durch entsprechdende Nummer!
(diesen -smtag=Robust könnt ihr, solltet ihr auch machen)
Log Verzeichniss, ist das einziegstes was bei nicht vorhanden sein, im Arbeitsverzeichniss angelegt wird
/log
weiter muss
/run/pid
PID_DATEI="/opt/opensim/run/pid/${INSTTAG}.pid"
Dies muss in der " OpenSim.ini angegeben werden " /opt/opensim/run/pid/Inst_01.pid " 02/03 je nach bin_xx
vorhanden sein
Noch ein Wort zu der MySql und Robust prüfung. Bei " auto " wird geprüft ob unter der angegeben IP / HOST der Dienst verfügbar ist, in zweiter line, ob er local läuft.
Ein Abruch kommt, wenn
Dienst nicht erreichbar
Config falsch, leer oder nicht vorhanden, ebenso Arbeitsverzeichniss fehlt oder ports falsch sind !
Unter Ubuntu, kann das script eins zu eins in "Monit" eingebunden werden
(Debian ist da leider zickig )
Beispiel:
Code: # Monitor mono opensim Service for Instanze 01
check process SIMNAME (/ INST oder wie man möchte)
with pidfile /opt/opensim/run/pid/Inst_01.pid
GROUP opensim
ONREBOOT NOSTART
start program = "/home/OPENSIMUSER/.local/bin/sim_start_sop.sh start 1"
as uid OPENSIMUSER and gid OPENSIMUSER
stop program = "/home/OPENSIMUSER/.local/bin/sim_start_stop.sh stop 1"
as uid OPENSIMUSER and gid OPENSIMUSER
if failed
host 127.0.0.1
port 9010
protocol http
request "/simstatus" within
15 cycles then restart
if memory usage > 768 MB then alert
if memory usage > 972 MB then restart
if cpu usage > 50% then alert
if cpu usage > 75% for 3 cycles then restart
if does not exist then restart
if 3 restarts within 5 cycles then timeout
depends on mysqld , Robust
Ach ja, das script kann also wie im Beispiel oben
" sim_start_stop.sh start 1 "
" sim_start_stop.sh stop 1 "
" sim_start_stop.sh status 1 "
aufgefrufen werden ...
Als dann, ich hoffe, das ich alles Gut Klar beschrieben habe, und bin auf eure reaktionen gespannt.
Und zu guter letzt, möchte ich mich bei "Manfred Aabye" für die Inspiration für die Color ausgabe in der Console bedanken.
WinBlöd wird ja auch so viel verkauft, weil es hüpsch aussieht
RE: Sim Start | Stop script - Dorena Verne - 10.02.2026
Hui, ganz schön aufwändig Gau, auf den ersten Blick etwas verwirrend für mich. Aber toll, wenn es läuft. :-)
Bei mir ist alles etwas einfacher gestalten:
Das screenscript für eine Instanz:
Hier alle mal untereinander:
Und hier als laufende Prozesse:
Überwachen tue ich das Ganze im Moment durchs tägliche hereinschauen und es läuft stabil. Aber ich werde es noch ergänzen, der Faulheit der manuellen Überwachung geschuldet.
Sim Start | Stop script - Akira - 10.02.2026
Code Review: sim_start_stop.sh
Was sehr gut ist
Struktur & Philosophie – Die klare Trennung von Prüfung, Logging und Aktionen ist vorbildlich. Der Kommentar "Keine Magie, keine Endlosschleifen" wird auch eingehalten.
Logging – Die doppelte Ausgabe (Konsole mit Farbe, Datei mit Timestamp) ist sauber gelöst. Die Trennung in log_info/warn/err/ok macht den Output übersichtlich.
abbruch_oder_status – Eine elegante Lösung, damit status immer durchläuft, während start bei Fehlern abbricht.
Robust/MySQL-Modi – Das auto/local/remote-System ist flexibel und praxisnah.
Probleme & Verbesserungsvorschläge
1. Doppelter Robust-Aufruf im start-Block
Der auskommentierte check_robust-Aufruf und der if-Block darunter sind ein Überbleibsel – hier sollte aufgeräumt werden.
Code: # check_robust wird zweimal definiert / aufgerufen:
check_mysql || ...
if [[ "${AKTION}" == "start" ]]; then # ← Dieser Block ist überflüssig
check_robust || ...
fi
# Zeile davor ist auskommentiert, aber die Logik ist redundant
2. Stop-Timeout potenziell sehr lang
Nach dem quit wartet das Script im schlechtesten Fall 5 Minuten (20 × 15 Sekunden). Eine Statusausgabe in der Schleife wäre hilfreich:
Code: for ((i=0;i<20;i++)); do
sleep 15 # = bis zu 300 Sekunden (5 Minuten!)
log_info "Warte auf Prozessende... (${i}/20)" # ← ergänzen
[[ ! -d "/proc/${PID}" ]] && break
done
3. screen -p "${INSTNUM}" – Fenster-Tab-Logik fragil
Beim Start wird ein neues Screen-Fenster mit -t "${REGIONS_NAME}" "${INSTNUM}" erstellt – die Fensternummer ist dabei die letzte Zahl, die screen vergeben würde, nicht zwingend INSTNUM. Beim Stop wird dann blind auf -p "${INSTNUM}" zugegriffen. Das kann ins Leere laufen, wenn Fenster zwischenzeitlich umbenannt oder geschlossen wurden.
4. Kein Schutz gegen Race Conditions beim Start
Zwischen der PID-Prüfung und dem tatsächlichen Start gibt es ein kleines Zeitfenster. Nicht kritisch, aber bei einem Script das regelmäßig automatisiert läuft (z.B. per systemd/cron) sollte man das im Hinterkopf behalten.
5. mysql-Befehl ohne Credentials
Code: mysql -e "USE ${MYSQL_DB};" >/dev/null 2>&1
Das funktioniert nur wenn ~/.my.cnf korrekt konfiguriert ist oder der Unix-Socket-Auth greift. Ansonsten schlägt der Check stillschweigend fehl und die Fehlermeldung ist irreführend. Ein Kommentar dazu wäre nützlich.
6. Kleinigkeit: Pflicht-Konfigurationsvariablen ohne Validierung
Die Variablen SCREEN_HAUPT und ERWARTETER_USER haben Platzhalterwerte (HAUPSCREEN-NAME, OPENSIMUNSER). Das Script sollte beim Start prüfen ob diese noch auf dem Default stehen:
Code: if [[ "${SCREEN_HAUPT}" == "HAUPSCREEN-NAME" ]]; then
log_err "SCREEN_HAUPT ist nicht konfiguriert!"
exit 1
fi
Fazit
Ein solides, gut lesbares Admin-Script. Die größten praktischen Stolpersteine sind der doppelte Robust-Aufruf und die Screen-Fenster-Nummerierung. Alles andere sind eher Verfeinerungen als echte Fehler.
RE: Sim Start | Stop script - Gau Hax - 11.02.2026
Wow … danke für das Lob – und vor allem danke für eure Reaktionen!
Ich habe mir alles noch mal in Ruhe angeschaut und das Script direkt an den zwei wichtigen Stellen nachgebessert. Danke auch für den Hinweis mit der Warteschleife – da kam bei mir im „Bauscript“ tatsächlich bei jedem Zyklus eine Ausgabe. Das habe ich jetzt sauber gelöst.
Man sieht jetzt klar, dass das Script noch arbeitet und nicht hängt.
Hier nur kurz zur Info die neue Warteschleife:
Code: # ----------------------------
# Warte Zyklus auf sauber Stop
# ----------------------------
log_info "Warte auf sauberen Shutdown (max. ${STOP_MAX_WAIT_SEC}s)"
elapsed=0
loops=$(( STOP_MAX_WAIT_SEC / STOP_CHECK_INTERVAL ))
progress_counter=0
for ((i=1; i<=loops; i++)); do
sleep "${STOP_CHECK_INTERVAL}"
elapsed=$(( elapsed + STOP_CHECK_INTERVAL ))
progress_counter=$(( progress_counter + 1 ))
# Prozess beendet?
if [[ ! -d "/proc/${PID}" ]]; then
log_ok "Prozess nach ${elapsed}s sauber beendet"
break
fi
# Fortschrittsmeldung
if (( progress_counter % STOP_PROGRESS_EVERY == 0 )); then
remaining=$(( STOP_MAX_WAIT_SEC - elapsed ))
log_info "Shutdown läuft … ${elapsed}s vergangen, ${remaining}s verbleiben"
fi
done
if [[ -d "/proc/${PID}" ]]; then
log_warn "Prozess reagiert nicht nach ${STOP_MAX_WAIT_SEC}s – hartes KILL"
kill -9 "${PID}"
else
log_ok "Instanz sauber gestoppt"
fi
rm -f "${PID_DATEI}"
Wie man sieht, sind das jetzt alles Variablen, die oben im Kopf definiert werden:
# -----------------------------
# Stop-Wartezeiten
# nur hier die Wartezeiten
# einstellen / ändern
# -----------------------------
STOP_MAX_WAIT_SEC=300 # max. Wartezeit auf sauberen Stop (300 = 5 Minuten)
STOP_CHECK_INTERVAL=10 # Prüfintervall in Sekunden
STOP_PROGRESS_EVERY=2 # alle X Durchläufe Fortschritt in der Konsole anzeigen
So kann man die Zeiten zentral anpassen, ohne im Script suchen zu müssen.
Das mit dem doppelten „Robust“ – ja, unschön !!!
Ist ein Überbleibsel vom Umbau, habe ich aber direkt geradegezogen. War mehr optisch störend als funktional kritisch.
Zum Thema screen: Danke für den Hinweis, da gehe ich noch mal sauber ran. Bei mir ist an der Stelle seit Jahren nichts passiert, aber ihr habt recht – sicher ist sicher. Ich baue da noch eine zusätzliche Rückversicherung ein, damit wirklich das richtige Screen-Fenster getroffen wird.
Ganz wichtig noch zu meiner Philosophie:
Passwörter gehören nicht ins Script. Gerade bei MySQL ist es ja sauber lösbar über eine .my.cnf im Home-Verzeichnis des Benutzers – so wie ich es im Beispiel beschrieben habe. Das ist für mich der deutlich bessere Weg.
Unterm Strich ist es ein bewusst schlankes Script, das vor dem Start alle wichtigen Punkte prüft und dann den Simulator sauber startet.
Für die nächste Version werden Start und Stop als separate, gekapselte Funktionen umgesetzt. Mit eigener Rückversicherung, ob der Vorgang wirklich erfolgreich war – und natürlich weiterhin mit zentral definierten Zeit-Variablen im Kopfbereich.
Danke euch auf jeden Fall für das Feedback – und ich freue mich schon, euch die nächsten Verbesserungen zu zeigen !
RE: Sim Start | Stop script - Gau Hax - 12.02.2026
Gerne möchte ich euch zeigen, wie es dann, von zuhause aus, mit ssh aufgerufen werden kann, und aussehen kann.
Die Bilder jetzt, da habe ich die Anmerkungen von Akira schon umgesetzt.
... und die zwei "Variablen SCREEN_HAUPT und ERWARTETER_USER" sind im Kopf definiert.
Schaue bitte nochmal genau nach.
So, nun dann, hier jetzt mal der Start Aufruf, von mir zuhause, mit ssh ( setzt ssh per key vorraus, ohne passwort )
ssh -t -p PORT SUPERUSER@domain/ip "sudo -i -u OPENSIMUSER bash sim_start_stop stop 9"
sim start
![[Bild: sim_start.jpg]](https://www.grid-berlin.de/anleitungen/sim_start.jpg)
sim stop 1
![[Bild: sim_stop1.jpg]](https://www.grid-berlin.de/anleitungen/sim_stop1.jpg)
sim stop 2
![[Bild: sim_stop2.jpg]](https://www.grid-berlin.de/anleitungen/sim_stop2.jpg)
Bis hier hin Läuft alles problemlos per Remote Aufruf. Und die Konsolen ausgaben können klar zeigen ob es geklappt hat.
damit ich das script so Starten kann, liegt es bei mir im HOME verzeichniss meines Opensim Users unter
~/.local/bin/sim_start_stop
und ich der .profile eures Benutzers, sollte sicherlich der PATH schon angegeben sein.
Code: # set PATH so it includes user's private bin if it exists
if [ -d "$HOME/.local/bin" ] ; then
PATH="$HOME/.local/bin:$PATH"
fi
in der art etwa
Ich muss dazusagen, nach der Anregung von Akira, habe ich schon etwas nachgearbeitet, was die ausgaben beim Start und Stop angehen. ebenso, der Umzug in separate Funktionen. Also wenn ich das Sauber habe, dann werde ich das gesamte script Komplett mit einer Anleitung Posten.
Ansonsten, möchte ich nochmal klar hervorheben, das StartStopScript nimmt keinerlei Veränderungen vor, es prüft nur die Gegebenheiten, und kann somit gefahrlos getestet werden, ohne das irgendwelche Änderung gemacht werden. weder am System, noch bei Opensimulator.
Für die Hübsche Anleitung, da muss ich noch ein paar Tage warten, bis ne gute Freundin wieder zu Besuch kommt, und mir den Test für alle Sauer schreibt 
P.S. Ihr könnt mich bitte auch auf Rechtschreibfehler, oder bessere Ausdrucksform im script hinweisen 
Bis dahin, viel Spass, Erfolg beim Ausprobieren.
|