Files
opensimMULTITOOLS-II/modular/osmtool_install.sh
T
2026-04-01 20:55:45 +02:00

1023 lines
30 KiB
Bash

#!/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 <path>] --action <bootstrap-server|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:
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/urandom | head -c "$length"
}
server_prep_marker() {
printf '%s' "$WORKDIR/.osmtool_server_prepared"
}
mark_server_prepared() {
local marker
marker="$(server_prep_marker)"
mkdir -p "$WORKDIR" 2>/dev/null || sudo mkdir -p "$WORKDIR"
cat <<EOF | sudo tee "$marker" >/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 <<EOF | sudo tee "$snippet_file" >/dev/null
[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
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 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