q81801/agent
0
1#!/usr/bin/env bash2set -euo pipefail3 4OPENCLAW_USER="${OPENCLAW_USER:-root}"5OPENCLAW_GROUP="${OPENCLAW_GROUP:-root}"6OPENCLAW_HOME="${OPENCLAW_HOME:-/root}"7OPENCLAW_STATE_DIR="${OPENCLAW_STATE_DIR:-/root/.openclaw}"8OPENCLAW_CONFIG_PATH="${OPENCLAW_CONFIG_PATH:-${OPENCLAW_STATE_DIR}/openclaw.json}"9OPENCLAW_WORKSPACE_DIR="${OPENCLAW_WORKSPACE_DIR:-${OPENCLAW_STATE_DIR}/workspace}"10OPENCLAW_GATEWAY_BIND="${OPENCLAW_GATEWAY_BIND:-lan}"11OPENCLAW_GATEWAY_PORT="${OPENCLAW_GATEWAY_PORT:-7860}"12OPENCLAW_INIT_GATEWAY_MODE="${OPENCLAW_INIT_GATEWAY_MODE:-local}"13OPENCLAW_GATEWAY_AUTH_MODE="${OPENCLAW_GATEWAY_AUTH_MODE:-token}"14OPENCLAW_LLM_PROVIDER="${OPENCLAW_LLM_PROVIDER:-thirdparty}"15OPENCLAW_LLM_MODEL="${OPENCLAW_LLM_MODEL:-}"16OPENCLAW_LLM_API="${OPENCLAW_LLM_API:-openai-completions}"17OPENCLAW_LLM_BASE_URL_ENV="${OPENCLAW_LLM_BASE_URL_ENV:-OPENCLAW_LLM_BASE_URL}"18OPENCLAW_LLM_API_KEY_ENV="${OPENCLAW_LLM_API_KEY_ENV:-OPENCLAW_LLM_API_KEY}"19OPENCLAW_BACKUP_ENABLED="${OPENCLAW_BACKUP_ENABLED:-true}"20OPENCLAW_BACKUP_CRON="${OPENCLAW_BACKUP_CRON:-*/30 * * * *}"21OPENCLAW_BACKUP_SOURCE_DIR="${OPENCLAW_BACKUP_SOURCE_DIR:-${OPENCLAW_STATE_DIR}}"22OPENCLAW_BACKUP_ROOT_CONFIG_DIR="${OPENCLAW_BACKUP_ROOT_CONFIG_DIR:-/root/.config}"23OPENCLAW_BACKUP_ROOT_CODEX_DIR="${OPENCLAW_BACKUP_ROOT_CODEX_DIR:-/root/.codex}"24OPENCLAW_BACKUP_ROOT_CLAUDE_DIR="${OPENCLAW_BACKUP_ROOT_CLAUDE_DIR:-/root/.claude}"25OPENCLAW_BACKUP_ROOT_AGENTS_DIR="${OPENCLAW_BACKUP_ROOT_AGENTS_DIR:-/root/.agents}"26OPENCLAW_BACKUP_ROOT_SSH_DIR="${OPENCLAW_BACKUP_ROOT_SSH_DIR:-/root/.ssh}"27OPENCLAW_BACKUP_ROOT_NPM_DIR="${OPENCLAW_BACKUP_ROOT_NPM_DIR:-/root/.npm}"28OPENCLAW_BACKUP_ROOT_LARK_CLI_DIR="${OPENCLAW_BACKUP_ROOT_LARK_CLI_DIR:-/root/.lark-cli}"29OPENCLAW_BACKUP_ENV_FILE_PATH="${OPENCLAW_BACKUP_ENV_FILE_PATH:-/etc/openclaw-backup.env}"30OPENCLAW_STDOUT_LOG_PATH="${OPENCLAW_STDOUT_LOG_PATH:-/var/log/openclaw/gateway.stdout.log}"31OPENCLAW_STDERR_LOG_PATH="${OPENCLAW_STDERR_LOG_PATH:-/var/log/openclaw/gateway.stderr.log}"32OPENCLAW_SSHX_AUTO_START="${OPENCLAW_SSHX_AUTO_START:-false}"33OPENCLAW_GATEWAY_CONTROLUI_ALLOW_INSECURE_AUTH="${OPENCLAW_GATEWAY_CONTROLUI_ALLOW_INSECURE_AUTH:-false}"34OPENCLAW_GATEWAY_CONTROLUI_DANGEROUSLY_DISABLE_DEVICE_AUTH="${OPENCLAW_GATEWAY_CONTROLUI_DANGEROUSLY_DISABLE_DEVICE_AUTH:-false}"35OPENCLAW_CHILD_PID=""36OPENCLAW_SSHX_PID=""37OPENCLAW_SHUTDOWN_BACKUP_DONE=038 39export HOME="$OPENCLAW_HOME"40export OPENCLAW_HOME41export OPENCLAW_STATE_DIR42export OPENCLAW_CONFIG_PATH43export OPENCLAW_BACKUP_ENV_FILE_PATH44OPENCLAW_STEP_INDEX=045OPENCLAW_ENTRYPOINT_TAG="openclaw-entrypoint"46 47timestamp_utc() {48 date -u +"%Y-%m-%dT%H:%M:%SZ"49}50 51log_info() {52 printf '[%s] %s: %s\n' "$(timestamp_utc)" "$OPENCLAW_ENTRYPOINT_TAG" "$*"53}54 55log_warn() {56 printf '[%s] %s: %s\n' "$(timestamp_utc)" "$OPENCLAW_ENTRYPOINT_TAG" "$*" >&257}58 59run_step() {60 local description="$1"61 shift62 OPENCLAW_STEP_INDEX=$((OPENCLAW_STEP_INDEX + 1))63 log_info "step ${OPENCLAW_STEP_INDEX} start: ${description}"64 "$@"65 log_info "step ${OPENCLAW_STEP_INDEX} done: ${description}"66}67 68is_true() {69 local value70 value="$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]')"71 [[ "$value" == "1" || "$value" == "true" || "$value" == "yes" || "$value" == "on" ]]72}73 74generate_gateway_token() {75 if command -v openssl >/dev/null 2>&1; then76 openssl rand -hex 3277 else78 python3 - <<'PY'79import secrets80print(secrets.token_hex(32))81PY82 fi83}84 85mkdir_state_dirs() {86 mkdir -p "$OPENCLAW_STATE_DIR" "$OPENCLAW_WORKSPACE_DIR"87 mkdir -p "$OPENCLAW_STATE_DIR/identity"88 mkdir -p "$OPENCLAW_STATE_DIR/agents/main/agent"89 mkdir -p "$OPENCLAW_STATE_DIR/agents/main/sessions"90 mkdir -p "$OPENCLAW_STATE_DIR/logs"91 mkdir -p "$(dirname "$OPENCLAW_STDOUT_LOG_PATH")" "$(dirname "$OPENCLAW_STDERR_LOG_PATH")"92 touch "$OPENCLAW_STDOUT_LOG_PATH" "$OPENCLAW_STDERR_LOG_PATH"93}94 95fix_permissions() {96 local path97 98 if [[ "$(id -u)" -ne 0 ]]; then99 return 0100 fi101 102 if [[ -e "$OPENCLAW_HOME" ]]; then103 chown "$OPENCLAW_USER:$OPENCLAW_GROUP" "$OPENCLAW_HOME"104 fi105 106 for path in \107 "$OPENCLAW_STATE_DIR" \108 "$OPENCLAW_WORKSPACE_DIR" \109 "$OPENCLAW_STDOUT_LOG_PATH" \110 "$OPENCLAW_STDERR_LOG_PATH" \111 "$(dirname "$OPENCLAW_STDOUT_LOG_PATH")" \112 "$(dirname "$OPENCLAW_STDERR_LOG_PATH")"; do113 if [[ -e "$path" ]]; then114 chown -R "$OPENCLAW_USER:$OPENCLAW_GROUP" "$path"115 fi116 done117}118 119write_or_update_config() {120 local existing_token121 existing_token=""122 123 if [[ -f "$OPENCLAW_CONFIG_PATH" ]]; then124 existing_token="$(python3 - <<'PY'125import json126import os127 128config_path = os.environ["OPENCLAW_CONFIG_PATH"]129try:130 with open(config_path, "r", encoding="utf-8") as fh:131 cfg = json.load(fh)132 token = cfg.get("gateway", {}).get("auth", {}).get("token", "")133 if isinstance(token, str):134 print(token.strip())135except Exception:136 pass137PY138)"139 fi140 141 if [[ -z "${OPENCLAW_GATEWAY_TOKEN:-}" ]]; then142 if [[ -n "$existing_token" ]]; then143 OPENCLAW_GATEWAY_TOKEN="$existing_token"144 else145 OPENCLAW_GATEWAY_TOKEN="$(generate_gateway_token)"146 fi147 fi148 export OPENCLAW_GATEWAY_TOKEN149 150 OPENCLAW_GATEWAY_PORT="$OPENCLAW_GATEWAY_PORT" \151 OPENCLAW_GATEWAY_BIND="$OPENCLAW_GATEWAY_BIND" \152 OPENCLAW_INIT_GATEWAY_MODE="$OPENCLAW_INIT_GATEWAY_MODE" \153 OPENCLAW_CONFIG_PATH="$OPENCLAW_CONFIG_PATH" \154 OPENCLAW_GATEWAY_TOKEN="$OPENCLAW_GATEWAY_TOKEN" \155 OPENCLAW_GATEWAY_PASSWORD="${OPENCLAW_GATEWAY_PASSWORD:-}" \156 OPENCLAW_GATEWAY_AUTH_MODE="$OPENCLAW_GATEWAY_AUTH_MODE" \157 OPENCLAW_LLM_PROVIDER="$OPENCLAW_LLM_PROVIDER" \158 OPENCLAW_LLM_MODEL="$OPENCLAW_LLM_MODEL" \159 OPENCLAW_LLM_API="$OPENCLAW_LLM_API" \160 OPENCLAW_LLM_BASE_URL_ENV="$OPENCLAW_LLM_BASE_URL_ENV" \161 OPENCLAW_LLM_API_KEY_ENV="$OPENCLAW_LLM_API_KEY_ENV" \162 OPENCLAW_GATEWAY_CONTROLUI_DANGEROUSLY_ALLOW_HOST_HEADER_ORIGIN_FALLBACK="${OPENCLAW_GATEWAY_CONTROLUI_DANGEROUSLY_ALLOW_HOST_HEADER_ORIGIN_FALLBACK:-true}" \163 OPENCLAW_GATEWAY_CONTROLUI_ALLOW_INSECURE_AUTH="$OPENCLAW_GATEWAY_CONTROLUI_ALLOW_INSECURE_AUTH" \164 OPENCLAW_GATEWAY_CONTROLUI_DANGEROUSLY_DISABLE_DEVICE_AUTH="$OPENCLAW_GATEWAY_CONTROLUI_DANGEROUSLY_DISABLE_DEVICE_AUTH" \165 OPENCLAW_INIT_CONTROL_UI_ALLOWED_ORIGINS="${OPENCLAW_INIT_CONTROL_UI_ALLOWED_ORIGINS:-}" \166 python3 - <<'PY'167import json168import os169import sys170from pathlib import Path171 172config_path = Path(os.environ["OPENCLAW_CONFIG_PATH"])173config = {}174if config_path.exists():175 try:176 config = json.loads(config_path.read_text(encoding="utf-8"))177 except Exception:178 config = {}179 180provider = os.environ.get("OPENCLAW_LLM_PROVIDER", "thirdparty").strip() or "thirdparty"181model = (os.environ.get("OPENCLAW_LLM_MODEL") or "").strip()182api = os.environ.get("OPENCLAW_LLM_API", "openai-completions").strip() or "openai-completions"183base_env_name = os.environ.get("OPENCLAW_LLM_BASE_URL_ENV", "OPENCLAW_LLM_BASE_URL").strip() or "OPENCLAW_LLM_BASE_URL"184key_env_name = os.environ.get("OPENCLAW_LLM_API_KEY_ENV", "OPENCLAW_LLM_API_KEY").strip() or "OPENCLAW_LLM_API_KEY"185base_url_value = (os.environ.get(base_env_name) or "").strip()186api_key_value = (os.environ.get(key_env_name) or "").strip()187 188missing_llm_env = []189if not model:190 missing_llm_env.append("OPENCLAW_LLM_MODEL")191if not base_url_value:192 missing_llm_env.append(base_env_name)193if not api_key_value:194 missing_llm_env.append(key_env_name)195custom_llm_ready = not missing_llm_env196model_ref = f"{provider}/{model}" if custom_llm_ready else ""197 198if missing_llm_env:199 print(200 "openclaw: skip custom LLM model configuration because missing: "201 + ", ".join(missing_llm_env),202 file=sys.stderr,203 )204 sshx_auto_start = (os.environ.get("OPENCLAW_SSHX_AUTO_START") or "false").strip().lower() in {205 "1",206 "true",207 "yes",208 "on",209 }210 if not sshx_auto_start:211 print(212 "openclaw: if you want to configure LLM via sshx, consider setting OPENCLAW_SSHX_AUTO_START=true",213 file=sys.stderr,214 )215 216fallback_allowed_origins = [f"http://127.0.0.1:{os.environ.get('OPENCLAW_GATEWAY_PORT', '7860')}"]217allowed_origins = fallback_allowed_origins218raw_allowed_origins = (os.environ.get("OPENCLAW_INIT_CONTROL_UI_ALLOWED_ORIGINS") or "").strip()219if raw_allowed_origins:220 try:221 parsed = json.loads(raw_allowed_origins)222 if isinstance(parsed, list):223 normalized = []224 for value in parsed:225 if isinstance(value, str):226 item = value.strip()227 if item:228 normalized.append(item)229 if normalized:230 allowed_origins = normalized231 except Exception:232 pass233 234fallback_origin = (os.environ.get("OPENCLAW_GATEWAY_CONTROLUI_DANGEROUSLY_ALLOW_HOST_HEADER_ORIGIN_FALLBACK") or "true").lower() in {235 "1",236 "true",237 "yes",238 "on",239}240allow_insecure_auth = (os.environ.get("OPENCLAW_GATEWAY_CONTROLUI_ALLOW_INSECURE_AUTH") or "false").lower() in {241 "1",242 "true",243 "yes",244 "on",245}246disable_device_auth = (os.environ.get("OPENCLAW_GATEWAY_CONTROLUI_DANGEROUSLY_DISABLE_DEVICE_AUTH") or "false").lower() in {247 "1",248 "true",249 "yes",250 "on",251}252 253gateway = config.get("gateway")254if not isinstance(gateway, dict):255 gateway = {}256gateway["mode"] = os.environ.get("OPENCLAW_INIT_GATEWAY_MODE", "local")257gateway["bind"] = os.environ.get("OPENCLAW_GATEWAY_BIND", "lan")258auth = gateway.get("auth")259if not isinstance(auth, dict):260 auth = {}261 262token_value = (os.environ.get("OPENCLAW_GATEWAY_TOKEN") or "").strip()263password_value = (os.environ.get("OPENCLAW_GATEWAY_PASSWORD") or "").strip()264if token_value:265 auth["token"] = token_value266if password_value:267 auth["password"] = password_value268 269valid_auth_modes = {"none", "token", "password", "trusted-proxy"}270mode_env = (os.environ.get("OPENCLAW_GATEWAY_AUTH_MODE") or "").strip().lower()271existing_mode = auth.get("mode")272if mode_env in valid_auth_modes:273 auth["mode"] = mode_env274elif password_value and not token_value:275 auth["mode"] = "password"276elif token_value and not password_value:277 auth["mode"] = "token"278elif token_value and password_value:279 auth["mode"] = "token"280elif isinstance(existing_mode, str) and existing_mode.strip().lower() in valid_auth_modes:281 auth["mode"] = existing_mode.strip().lower()282else:283 auth["mode"] = "token"284 285gateway["auth"] = auth286gateway.setdefault("controlUi", {})287gateway["controlUi"]["allowedOrigins"] = allowed_origins288gateway["controlUi"]["dangerouslyAllowHostHeaderOriginFallback"] = fallback_origin289gateway["controlUi"]["allowInsecureAuth"] = allow_insecure_auth290gateway["controlUi"]["dangerouslyDisableDeviceAuth"] = disable_device_auth291config["gateway"] = gateway292 293agents = config.get("agents")294if not isinstance(agents, dict):295 agents = {}296defaults = agents.get("defaults")297if not isinstance(defaults, dict):298 defaults = {}299if custom_llm_ready:300 defaults["model"] = {"primary": model_ref}301 allow_models = defaults.get("models")302 if not isinstance(allow_models, dict):303 allow_models = {}304 allow_models.setdefault(model_ref, {"alias": "Default"})305 defaults["models"] = allow_models306agents["defaults"] = defaults307config["agents"] = agents308 309if custom_llm_ready:310 models_cfg = config.get("models")311 if not isinstance(models_cfg, dict):312 models_cfg = {}313 models_cfg["mode"] = "merge"314 providers = models_cfg.get("providers")315 if not isinstance(providers, dict):316 providers = {}317 provider_cfg = providers.get(provider)318 if not isinstance(provider_cfg, dict):319 provider_cfg = {}320 provider_cfg["baseUrl"] = f"${{{base_env_name}}}"321 provider_cfg["apiKey"] = f"${{{key_env_name}}}"322 provider_cfg["api"] = api323 provider_cfg["models"] = [{"id": model, "name": model}]324 providers[provider] = provider_cfg325 models_cfg["providers"] = providers326 config["models"] = models_cfg327 328config_path.parent.mkdir(parents=True, exist_ok=True)329config_path.write_text(json.dumps(config, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")330os.chmod(config_path, 0o600)331PY332}333 334write_backup_env_file() {335 local backup_env_file336 local keys=(337 HF_TOKEN338 HUGGINGFACE_HUB_TOKEN339 OPENCLAW_BACKUP_DATASET_REPO340 OPENCLAW_BACKUP_REPO_TYPE341 OPENCLAW_BACKUP_PATH_PREFIX342 OPENCLAW_BACKUP_LATEST_NAME343 OPENCLAW_BACKUP_KEEP_COUNT344 OPENCLAW_BACKUP_PRIVATE345 OPENCLAW_BACKUP_WORK_DIR346 OPENCLAW_BACKUP_SOURCE_DIR347 OPENCLAW_BACKUP_ROOT_CONFIG_DIR348 OPENCLAW_BACKUP_ROOT_CODEX_DIR349 OPENCLAW_BACKUP_ROOT_CLAUDE_DIR350 OPENCLAW_BACKUP_ROOT_AGENTS_DIR351 OPENCLAW_BACKUP_ROOT_SSH_DIR352 OPENCLAW_BACKUP_ROOT_NPM_DIR353 OPENCLAW_BACKUP_ROOT_LARK_CLI_DIR354 OPENCLAW_STATE_DIR355 )356 357 backup_env_file="${OPENCLAW_BACKUP_ENV_FILE_PATH:-/etc/openclaw-backup.env}"358 if [[ "$(id -u)" -ne 0 && "$backup_env_file" == "/etc/openclaw-backup.env" ]]; then359 backup_env_file="${OPENCLAW_STATE_DIR}/openclaw-backup.env"360 fi361 362 mkdir -p "$(dirname "$backup_env_file")"363 if ! : > "$backup_env_file"; then364 echo "openclaw: cannot write backup env file at $backup_env_file" >&2365 return 1366 fi367 368 for key in "${keys[@]}"; do369 printf '%s=%q\n' "$key" "${!key:-}" >> "$backup_env_file"370 done371 OPENCLAW_BACKUP_ENV_FILE_PATH="$backup_env_file"372 export OPENCLAW_BACKUP_ENV_FILE_PATH373 374 if [[ "$(id -u)" -eq 0 ]]; then375 chown "$OPENCLAW_USER:$OPENCLAW_GROUP" "$backup_env_file"376 chmod 640 "$backup_env_file"377 else378 chmod 600 "$backup_env_file"379 fi380}381 382setup_backup_cron() {383 if [[ "$(id -u)" -ne 0 ]]; then384 echo "openclaw: backup cron requires root; skip cron setup" >&2385 return 0386 fi387 388 if ! is_true "$OPENCLAW_BACKUP_ENABLED"; then389 return 0390 fi391 392 if [[ -z "${OPENCLAW_BACKUP_DATASET_REPO:-}" ]]; then393 echo "openclaw: backup enabled but OPENCLAW_BACKUP_DATASET_REPO is empty; skip cron" >&2394 return 0395 fi396 397 if ! write_backup_env_file; then398 echo "openclaw: backup env file unavailable; skip cron setup" >&2399 return 0400 fi401 402 cat > /etc/cron.d/openclaw-backup <<EOFCRON403SHELL=/bin/bash404PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin405OPENCLAW_BACKUP_ENV_FILE_PATH=${OPENCLAW_BACKUP_ENV_FILE_PATH}406${OPENCLAW_BACKUP_CRON} ${OPENCLAW_USER} /usr/local/bin/openclaw-backup-cron.sh >> /var/log/openclaw/backup.log 2>&1407EOFCRON408 chmod 0644 /etc/cron.d/openclaw-backup409 touch /var/log/openclaw/backup.log410 chown "$OPENCLAW_USER:$OPENCLAW_GROUP" /var/log/openclaw/backup.log411 412 if pgrep -x cron >/dev/null 2>&1; then413 return 0414 fi415 416 cron417}418 419restore_from_backup_on_startup() {420 if [[ -z "${OPENCLAW_BACKUP_DATASET_REPO:-}" ]]; then421 echo "openclaw: startup restore skipped (OPENCLAW_BACKUP_DATASET_REPO is empty)" >&2422 return 0423 fi424 425 if ! write_backup_env_file; then426 echo "openclaw: backup env file unavailable; skip startup restore" >&2427 return 0428 fi429 if /usr/local/bin/openclaw-restore.sh; then430 echo "openclaw: restore finished"431 else432 echo "openclaw: restore skipped or failed, continue startup" >&2433 fi434}435 436run_as_node() {437 local cmd=("$@")438 if [[ "$(id -u)" -eq 0 ]]; then439 exec gosu "$OPENCLAW_USER:$OPENCLAW_GROUP" "${cmd[@]}" >>"$OPENCLAW_STDOUT_LOG_PATH" 2>>"$OPENCLAW_STDERR_LOG_PATH"440 fi441 exec "${cmd[@]}" >>"$OPENCLAW_STDOUT_LOG_PATH" 2>>"$OPENCLAW_STDERR_LOG_PATH"442}443 444run_as_node_background() {445 local cmd=("$@")446 if [[ "$(id -u)" -eq 0 ]]; then447 gosu "$OPENCLAW_USER:$OPENCLAW_GROUP" "${cmd[@]}" >>"$OPENCLAW_STDOUT_LOG_PATH" 2>>"$OPENCLAW_STDERR_LOG_PATH" &448 else449 "${cmd[@]}" >>"$OPENCLAW_STDOUT_LOG_PATH" 2>>"$OPENCLAW_STDERR_LOG_PATH" &450 fi451 OPENCLAW_CHILD_PID="$!"452}453 454start_sshx_background() {455 if ! is_true "$OPENCLAW_SSHX_AUTO_START"; then456 return 0457 fi458 459 if ! command -v sshx >/dev/null 2>&1; then460 echo "openclaw: OPENCLAW_SSHX_AUTO_START=true but sshx not found; skip auto start" >&2461 return 0462 fi463 464 if [[ "$(id -u)" -eq 0 ]]; then465 gosu "$OPENCLAW_USER:$OPENCLAW_GROUP" sshx >/proc/1/fd/1 2>/proc/1/fd/2 &466 else467 sshx >/proc/1/fd/1 2>/proc/1/fd/2 &468 fi469 OPENCLAW_SSHX_PID="$!"470 echo "openclaw: sshx started in background (pid=$OPENCLAW_SSHX_PID)"471}472 473stop_sshx_background() {474 if [[ -n "$OPENCLAW_SSHX_PID" ]] && kill -0 "$OPENCLAW_SSHX_PID" >/dev/null 2>&1; then475 echo "openclaw: stopping sshx background process (pid=$OPENCLAW_SSHX_PID)"476 kill "$OPENCLAW_SSHX_PID" >/dev/null 2>&1 || true477 wait "$OPENCLAW_SSHX_PID" >/dev/null 2>&1 || true478 fi479}480 481backup_on_shutdown() {482 if [[ "$OPENCLAW_SHUTDOWN_BACKUP_DONE" -eq 1 ]]; then483 return 0484 fi485 OPENCLAW_SHUTDOWN_BACKUP_DONE=1486 487 if [[ -z "${OPENCLAW_BACKUP_DATASET_REPO:-}" ]]; then488 echo "openclaw: shutdown backup skipped (OPENCLAW_BACKUP_DATASET_REPO is empty)" >&2489 return 0490 fi491 492 if ! write_backup_env_file; then493 echo "openclaw: backup env file unavailable; skip shutdown backup" >&2494 return 0495 fi496 echo "openclaw: running shutdown backup"497 if /usr/local/bin/openclaw-backup-cron.sh; then498 echo "openclaw: shutdown backup completed"499 else500 echo "openclaw: shutdown backup failed, container will still exit" >&2501 fi502}503 504run_gateway_with_shutdown_backup() {505 local gateway_exit_code=0506 local shutting_down=0507 508 on_gateway_signal() {509 local signal="$1"510 if [[ "$shutting_down" -eq 1 ]]; then511 return 0512 fi513 shutting_down=1514 log_warn "received ${signal}, preparing graceful shutdown"515 516 if [[ -n "$OPENCLAW_CHILD_PID" ]] && kill -0 "$OPENCLAW_CHILD_PID" >/dev/null 2>&1; then517 kill -s "$signal" "$OPENCLAW_CHILD_PID" >/dev/null 2>&1 || kill "$OPENCLAW_CHILD_PID" >/dev/null 2>&1 || true518 if ! wait "$OPENCLAW_CHILD_PID"; then519 gateway_exit_code=$?520 fi521 fi522 523 log_info "gateway process stopped with code ${gateway_exit_code}"524 stop_sshx_background525 backup_on_shutdown526 trap - TERM INT QUIT527 exit "$gateway_exit_code"528 }529 530 trap 'on_gateway_signal TERM' TERM531 trap 'on_gateway_signal INT' INT532 trap 'on_gateway_signal QUIT' QUIT533 534 log_info "starting gateway process (bind=${OPENCLAW_GATEWAY_BIND}, port=${OPENCLAW_GATEWAY_PORT})"535 run_as_node_background openclaw gateway --allow-unconfigured --bind "$OPENCLAW_GATEWAY_BIND" --port "$OPENCLAW_GATEWAY_PORT" "$@"536 log_info "gateway process started (pid=${OPENCLAW_CHILD_PID})"537 if ! wait "$OPENCLAW_CHILD_PID"; then538 gateway_exit_code=$?539 fi540 log_info "gateway process exited (pid=${OPENCLAW_CHILD_PID}, code=${gateway_exit_code})"541 542 stop_sshx_background543 backup_on_shutdown544 trap - TERM INT QUIT545 return "$gateway_exit_code"546}547 548main() {549 if [[ "$#" -eq 0 ]]; then550 set -- gateway551 fi552 553 local subcommand="$1"554 shift || true555 556 if [[ "$subcommand" == "gateway" ]]; then557 log_info "starting gateway bootstrap sequence"558 run_step "prepare runtime directories" mkdir_state_dirs559 run_step "restore state from backup (if configured)" restore_from_backup_on_startup560 run_step "write or update gateway config" write_or_update_config561 run_step "fix file ownership and permissions" fix_permissions562 run_step "setup backup cron (if enabled)" setup_backup_cron563 run_step "start sshx background service (if enabled)" start_sshx_background564 OPENCLAW_STEP_INDEX=$((OPENCLAW_STEP_INDEX + 1))565 log_info "step ${OPENCLAW_STEP_INDEX} start: launch gateway"566 run_gateway_with_shutdown_backup "$@"567 local gateway_exit_code=$?568 if [[ "$gateway_exit_code" -ne 0 ]]; then569 log_warn "gateway exited with non-zero code ${gateway_exit_code}"570 return "$gateway_exit_code"571 fi572 log_info "step ${OPENCLAW_STEP_INDEX} done: launch gateway"573 else574 log_info "starting subcommand: openclaw ${subcommand}"575 run_as_node openclaw "$subcommand" "$@"576 fi577}578 579main "$@"580 