Files
2026-05-01 09:19:32 +02:00

1198 lines
37 KiB
Bash
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=/dev/null
source "$SCRIPT_DIR/osmtool_core.sh"
usage() {
cat <<'EOF'
Usage:
osmtool_install.sh [--workdir <path>] --action <init-config|server-check|prepare-ubuntu|install-opensim-deps|install-dotnet8|install-opensim|configure-opensim|configure-database|compile-janus|configure-janus|install-janus>
[--opensim-dir <path>] [--opensim-repo <url>] [--opensim-branch <name>] [--repo-mode <update|fresh>]
[--tsassets-dir <path>] [--currency-dir <path>] [--data-backup-dir <path>]
[--deploy-binaries <true|false>] [--legacy-patch-dir <path>]
[--db-root-user <name>] [--db-root-pass <value>] [--db-user <name>] [--db-pass <value>]
[--janus-prefix <path>] [--janus-src <path>] [--public-host <host>] [--http-port <port>]
[--admin-port <port>] [--rtp-range <from-to>] [--enable-admin <true|false>]
[--api-secret <value>] [--admin-secret <value>]
Actions:
init-config Create/update shared osmtool configuration interactively
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 server-check --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="$(awk -F= '/^VERSION_ID=/{gsub(/\"/,"",$2); print $2; exit}' /etc/os-release 2>/dev/null || true)"
[[ -n "$version_id" ]] || die "Could not detect VERSION_ID from /etc/os-release"
pkg_url="https://packages.microsoft.com/config/ubuntu/${version_id}/packages-microsoft-prod.deb"
pkg_file="/tmp/packages-microsoft-prod.deb"
# Ubuntu 26.04: Microsoft bietet noch kein eigenes Repo an; 24.04 (noble) ist kompatibel
if [[ "$version_id" == "26.04" ]]; then
log INFO "Ubuntu 26.04 erkannt verwende Microsoft-Repo fuer Ubuntu 24.04 (noble)"
pkg_url="https://packages.microsoft.com/config/ubuntu/24.04/packages-microsoft-prod.deb"
fi
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"
}
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
local db_conn_robust db_conn_sim sim_name_inner
# MoneyServer Credentials
set_ini_key_value "$WORKDIR/robust/bin/MoneyServer.ini" "username" "$cfg_user"
set_ini_key_value "$WORKDIR/robust/bin/MoneyServer.ini" "password" "$cfg_pass"
# Robust.ini ConnectionString (zwingend erforderlich für Datenbankverbindung)
db_conn_robust="Data Source=localhost;Database=robust;User ID=${cfg_user};Password=${cfg_pass};Old Guids=true;"
if [[ -f "$WORKDIR/robust/bin/Robust.ini" ]]; then
upsert_key "$WORKDIR/robust/bin/Robust.ini" "ConnectionString" "\"${db_conn_robust}\""
fi
# Jede simX/bin/OpenSim.ini ConnectionString (zwingend, sonst startet sim nicht)
shopt -s nullglob
for sim_dir in "$WORKDIR"/sim[0-9]*/bin; do
sim_name_inner="$(basename "$(dirname "$sim_dir")")"
[[ "$sim_name_inner" =~ ^sim[0-9]+$ ]] || continue
[[ -f "$sim_dir/OpenSim.ini" ]] || continue
db_conn_sim="Data Source=localhost;Database=${sim_name_inner};User ID=${cfg_user};Password=${cfg_pass};Old Guids=true;"
upsert_key "$sim_dir/OpenSim.ini" "ConnectionString" "\"${db_conn_sim}\""
done
shopt -u nullglob
log INFO "Updated ConnectionStrings in Robust.ini, MoneyServer.ini und simX/OpenSim.ini"
else
log INFO "Keine DB-Credentials verfügbar — ConnectionString nicht gesetzt. Erst configure-database ausführen."
fi
# Hostname/IP in alle Konfigurationsdateien einsetzen (YOURHOST ersetzen)
if [[ -n "$PUBLIC_HOST" && "$PUBLIC_HOST" != "127.0.0.1" ]]; then
local h_escaped
h_escaped="$(printf '%s' "$PUBLIC_HOST" | sed 's/[\/&]/\\&/g')"
local cfg_f
for cfg_f in \
"$WORKDIR/robust/bin/Robust.ini" \
"$WORKDIR/robust/bin/config-include/GridCommon.ini" \
"$WORKDIR/standalone/bin/config-include/StandaloneCommon.ini"; do
[[ -f "$cfg_f" ]] || continue
sed -i "s|YOURHOST|${h_escaped}|g" "$cfg_f"
done
shopt -s nullglob
for sim_dir in "$WORKDIR"/sim[0-9]*/bin; do
for cfg_f in "$sim_dir/OpenSim.ini" "$sim_dir/config-include/GridCommon.ini"; do
[[ -f "$cfg_f" ]] || continue
sed -i "s|YOURHOST|${h_escaped}|g" "$cfg_f"
done
done
shopt -u nullglob
log INFO "Hostname ($PUBLIC_HOST) in Konfigurationsdateien eingesetzt"
fi
# Gridname in Konfigurationsdateien setzen
if [[ -n "$GRIDNAME" ]]; then
local cfg_f
for cfg_f in \
"$WORKDIR/robust/bin/Robust.ini" \
"$WORKDIR/robust/bin/config-include/GridCommon.ini" \
"$WORKDIR/standalone/bin/config-include/StandaloneCommon.ini"; do
[[ -f "$cfg_f" ]] || continue
upsert_key "$cfg_f" "GridName" "\"${GRIDNAME}\""
upsert_key "$cfg_f" "WelcomeMessage" "\"Willkommen bei ${GRIDNAME}\""
done
log INFO "Gridname ($GRIDNAME) in Konfigurationsdateien gesetzt"
fi
log INFO "OpenSim runtime ini Konfiguration abgeschlossen"
}
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-Datenbanken: bei --sim-count N explizit, sonst Verzeichnisse ermitteln
local sim_db_list=""
if [[ "$SIM_COUNT" -gt 0 ]]; then
sim_db_list="$(for ((i=1; i<=SIM_COUNT; i++)); do echo "sim${i}"; done)"
log INFO "--sim-count $SIM_COUNT: erstelle Datenbanken sim1..sim${SIM_COUNT}"
else
sim_db_list="$(discover_sim_databases)"
if [[ -z "$sim_db_list" ]]; then
log INFO "Keine sim*-Verzeichnisse unter $WORKDIR gefunden — sim-Datenbanken übersprungen. Mit --sim-count N erzwingen."
fi
fi
if [[ -n "$sim_db_list" ]]; then
while IFS= read -r db_name; do
[[ -n "$db_name" ]] || continue
local db_ident="\`$db_name\`"
run_mariadb_sql "CREATE DATABASE IF NOT EXISTS $db_ident CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
log INFO "Datenbank sichergestellt: $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
local 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 Datenbank-Setup abgeschlossen"
}
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
os_id="$(awk -F= '/^ID=/{gsub(/\"/,"",$2); print $2; exit}' /etc/os-release 2>/dev/null || true)"
[[ -n "$os_id" ]] || os_id="unknown"
local os_pretty
os_pretty="$(awk -F= '/^PRETTY_NAME=/{gsub(/\"/,"",$2); print $2; exit}' /etc/os-release 2>/dev/null || true)"
[[ -n "$os_pretty" ]] || os_pretty="unknown"
log INFO "Detected OS: $os_pretty"
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/urandom | head -c "$length"
}
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 secrets_file
snippet_file="$WORKDIR/opensim-janus-config.generated.ini"
secrets_file="$HOME/janus_opensim_secrets.env"
mkdir -p "$WORKDIR" 2>/dev/null || sudo mkdir -p "$WORKDIR"
# Vollstaendiges OpenSim/Robust-Snippet (entspricht config-janus-linux.sh)
cat <<EOF | sudo tee "$snippet_file" >/dev/null
; ==========================================================
; OpenSim WebRTC/Janus Config (generiert von osmtool)
; ==========================================================
; ----------------------------
; OpenSim.ini [WebRtcVoice]
; ----------------------------
[WebRtcVoice]
Enabled = true
SpatialVoiceService = WebRtcVoice.dll:WebRtcVoiceServiceConnector
NonSpatialVoiceService = WebRtcVoice.dll:WebRtcVoiceServiceConnector
WebRtcVoiceServerURI = http://$PUBLIC_HOST:8003
; ----------------------------
; Robust.ini [ServiceList] + [WebRtcVoice] + [JanusWebRtcVoice]
; ----------------------------
[ServiceList]
VoiceServiceConnector = "8000/OpenSim.Server.Handlers.dll:OpenSim.Server.Handlers.WebRtcVoiceServerConnector"
[WebRtcVoice]
Enabled = true
NonSpatialVoiceService = WebRtcJanusService.dll:WebRtcJanusService
[JanusWebRtcVoice]
JanusGatewayURI = http://$PUBLIC_HOST:$HTTP_PORT/janus
APIToken = $api_secret
EOF
if [[ "$ENABLE_ADMIN" == "true" ]]; then
cat <<EOF | sudo tee -a "$snippet_file" >/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
# Secrets sichern (analog janus_instal.sh)
{
echo "JANUS_API_SECRET=$api_secret"
[[ -n "$admin_secret" ]] && echo "JANUS_ADMIN_SECRET=$admin_secret"
echo "JANUS_HTTP_PORT=$HTTP_PORT"
echo "JANUS_PUBLIC_HOST=$PUBLIC_HOST"
} > "$secrets_file"
chmod 600 "$secrets_file"
log INFO "Janus Secrets gespeichert: $secrets_file (chmod 600)"
cat <<EOF | sudo tee "$JANUS_PREFIX/.osmtool_janus_ready" >/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 konfiguriert. Snippet: $snippet_file — Marker: $JANUS_PREFIX/.osmtool_janus_ready"
log INFO "Firewall oeffnen: TCP $HTTP_PORT, UDP ${RTP_RANGE%%-*}-${RTP_RANGE##*-}"
log INFO "Janus neu starten: sudo systemctl restart janus"
log INFO "Snippet-Inhalt in OpenSim.ini und Robust.ini uebernehmen."
}
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"
# Patch-Dateien nur mit Bestaetigung einspielen
if [[ -n "$LEGACY_PATCH_DIR" ]]; then
if confirm_addon "Patch-Dateien" "Verzeichnis: $LEGACY_PATCH_DIR"; then
apply_legacy_patches "$LEGACY_PATCH_DIR"
fi
fi
# opensim-tsassets (Texturen/Assets)
if confirm_addon "opensim-tsassets" "$TSASSETS_REPO"; then
sync_support_repo "opensim-tsassets" "$TSASSETS_REPO" "$TSASSETS_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"
fi
# opensimcurrencyserver-dotnet (Geldsystem)
if confirm_addon "opensimcurrencyserver-dotnet" "$CURRENCY_REPO"; then
sync_support_repo "opensimcurrencyserver" "$CURRENCY_REPO" "$CURRENCY_DIR"
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"
fi
# os-data-backup
if confirm_addon "os-data-backup" "$DATA_BACKUP_REPO"; then
sync_support_repo "os-data-backup" "$DATA_BACKUP_REPO" "$DATA_BACKUP_DIR"
copy_tree_contents "$DATA_BACKUP_DIR" "$OPENSIM_DIR/addon-modules/os-data-backup" "os-data-backup content"
fi
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"
}
# Allgemeine Bestaetigungsfunktion fuer externe Addons.
# Umgebungsvariable ADDON_CONFIRMED=true ueberspringt die Abfrage (CI/Automation).
confirm_addon() {
local addon_name="${1:-Addon}"
local addon_desc="${2:-}"
local answer
if [[ "${ADDON_CONFIRMED:-}" == "true" ]]; then
return 0
fi
echo
echo "======================================================="
echo " $(msg PROMPT_EXTERNAL_ADDON): $addon_name"
[[ -n "$addon_desc" ]] && echo " $addon_desc"
echo " $(msg PROMPT_ADDON_INFO_1)"
echo " $(msg PROMPT_ADDON_INFO_2)"
echo "======================================================="
read -r -p " $(msg PROMPT_INSTALL_YESNO) " answer
case "${answer,,}" in
j|ja|y|yes|o|oui|s|si|) return 0 ;;
*)
log INFO "$(msg INFO_ADDON_SKIPPED): $addon_name"
return 1
;;
esac
}
confirm_janus() {
local action_label="${1:-Janus-Aktion}"
local answer
# Im nicht-interaktiven Betrieb (z. B. CI) Var JANUS_CONFIRMED=true setzen
if [[ "${JANUS_CONFIRMED:-}" == "true" ]]; then
return 0
fi
echo
echo "======================================================="
echo " $(msg PROMPT_JANUS_TITLE): $action_label"
echo "======================================================="
echo " $(msg PROMPT_JANUS_INFO_1)"
echo " $(msg PROMPT_JANUS_INFO_2)"
echo " $(msg PROMPT_JANUS_INFO_3) $JANUS_PREFIX."
echo "======================================================="
read -r -p " $(msg PROMPT_CONTINUE_YESNO) " answer
case "${answer,,}" in
j|ja|y|yes|o|oui|s|si|) return 0 ;;
*)
log INFO "$(msg INFO_JANUS_CANCELLED)"
exit 0
;;
esac
}
install_janus() {
confirm_janus "Installation + Konfiguration"
compile_janus
configure_janus
}
ensure_shared_config_for_install() {
if [[ -f "$OSMTOOL_CONFIG_FILE" ]]; then
return 0
fi
if [[ -t 0 ]]; then
log INFO "Shared configuration not found. Starting interactive setup."
init_shared_config_interactive
return 0
fi
log INFO "Shared configuration not found at $OSMTOOL_CONFIG_FILE (non-interactive mode)"
return 0
}
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:-${CONFIG_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:-${CONFIG_DB_ROOT_USER:-root}}"
DB_ROOT_PASS="${DB_ROOT_PASS:-${CONFIG_DB_ROOT_PASS:-}}"
DB_USER="${DB_USER:-${CONFIG_DB_USER:-opensimuser}}"
DB_PASS="${DB_PASS:-${CONFIG_DB_PASS:-}}"
JANUS_PREFIX="${JANUS_PREFIX:-${CONFIG_JANUS_PREFIX:-/opt/janus}}"
JANUS_SRC="${JANUS_SRC:-$WORKDIR/janus-gateway}"
PUBLIC_HOST="${PUBLIC_HOST:-${CONFIG_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:-}"
GRIDNAME="${GRIDNAME:-${CONFIG_GRIDNAME:-}}"
SIM_COUNT="${SIM_COUNT:-0}"
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 ;; --gridname) GRIDNAME="$2"; shift 2 ;;
--sim-count) SIM_COUNT="$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"
if [[ "$ACTION" != "init-config" ]]; then
ensure_shared_config_for_install
fi
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
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
init-config)
init_shared_config_interactive
;;
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)
confirm_janus "Kompilierung"
compile_janus
;;
configure-janus)
confirm_janus "Konfiguration"
configure_janus
;;
install-janus) install_janus ;;
*) die "Unsupported action: $ACTION" ;;
esac