#!/usr/bin/env bash ## entrypoint-lsphp.sh — PID 1 for cac-lsphp:phpNN. ## ## The per-site PHP backend for the SHARED OpenLiteSpeed tier. Runs lsphp in ## DETACHED LSAPI mode (`lsphp -b `) and nothing else — no ## webserver. The shared-ols container connects to this over the docker ## network (extProcessor type lsapi, address :9000) exactly ## like the shared httpd connects to a cac-fpm container's php-fpm on :9000. ## ## Structurally this is the LiteSpeed analogue of entrypoint-fpm.sh: same ## `uid`/`user` contract, same /home/$user/{public_html,logs} layout, same ## per-site ini drop-in mechanism as entrypoint-litespeed.sh. The only ## difference from cac-litespeed is that OLS lives elsewhere, so this PID 1 is ## lsphp itself rather than a webserver supervisor. ## ## IMPORTANT (see whp memory feedback_ols_lsapi_no_script_filename_remap): ## OLS hands lsphp exactly its vhost docRoot path, so the shared-ols vhost ## docRoot and THIS container's docroot mount MUST be the same absolute path. ## The panel mounts the site at /mnt/users// in BOTH containers; ## this entrypoint does not assume any particular path — it just runs lsphp, ## which opens whatever absolute SCRIPT_FILENAME the webserver sends. set -euo pipefail : "${PHPVER:=83}" : "${environment:=PROD}" export CONTAINER_ROLE="lsphp_only" export PHPVER environment ## ---- env validation (same contract as entrypoint-fpm / entrypoint-litespeed) ---- if [ -z "${uid:-}" ] || [ -z "${user:-}" ]; then echo "FATAL: 'uid' and 'user' env vars are required (panel sets these from WHP_UID/WHP_USER)." >&2 exit 1 fi export user LSPHP_BIN="/usr/local/lsws/lsphp${PHPVER}/bin/lsphp" if [ ! -x "$LSPHP_BIN" ]; then echo "FATAL: lsphp binary not found at $LSPHP_BIN (PHPVER=$PHPVER)." >&2 exit 1 fi ## ---- user ---- if ! id -u "$user" >/dev/null 2>&1; then useradd -u "$uid" -m -s /bin/bash "$user" fi ## ---- locate the customer docroot ---- ## Unlike cac-fpm/cac-litespeed (docroot at /home/$user), the shared-ols tier ## mounts each site at /mnt/users// — the SAME absolute path the ## shared-ols vhost uses as docRoot, because OLS hands lsphp exactly that path as ## SCRIPT_FILENAME (feedback_ols_lsapi_no_script_filename_remap). The panel ## mounts exactly ONE site dir here, so glob it (wildcard-safe: the on-disk dir ## is wildcard. for wildcard sites, which the glob picks up verbatim). SITE_DIR="" for d in /mnt/users/"$user"/*/; do [ -d "$d" ] || continue SITE_DIR="${d%/}" break done if [ -z "$SITE_DIR" ]; then ## No bind mount yet (e.g. hand-run for testing) — fall back to a sane path so ## lsphp still starts; OLS will send the real docRoot at request time. SITE_DIR="/mnt/users/$user/site" fi mkdir -p "$SITE_DIR/public_html" "$SITE_DIR/logs/php-fpm" ## ---- detached-lsphp pool sizing ---- # shellcheck source=/dev/null source /scripts/detect-memory-lsphp.sh ## LSAPI tuning (spec §5.1). PHP_LSAPI_CHILDREN MUST equal the shared-ols vhost ## maxConns — the WHP panel writes both from the single fpm_max_children value, ## so they can't drift. LSAPI_MAX_IDLE is THE RAM win: idle children exit, so an ## idle site's footprint collapses toward baseline (ondemand-like). export PHP_LSAPI_CHILDREN="${PHP_LSAPI_CHILDREN:-$LSAPI_CHILDREN}" export PHP_LSAPI_MAX_REQUESTS="${PHP_LSAPI_MAX_REQUESTS:-500}" export LSAPI_MAX_IDLE="${LSAPI_MAX_IDLE:-30}" export LSAPI_EXTRA_CHILDREN="${LSAPI_EXTRA_CHILDREN:-5}" export LSAPI_AVOID_FORK="${LSAPI_AVOID_FORK:-0}" LSPHP_BIND="${LSPHP_BIND:-0.0.0.0:9000}" echo "Container memory: ${CONTAINER_MEMORY_MB}MB | PHP_LSAPI_CHILDREN=${PHP_LSAPI_CHILDREN} | LSAPI_MAX_IDLE=${LSAPI_MAX_IDLE} | PHPVER=${PHPVER} | bind=${LSPHP_BIND}" ## ---- per-site ini drop-ins (identical mechanism to entrypoint-litespeed.sh) ---- ## error_log → the same customer-visible path cac:phpNN / cac-litespeed use, so ## "where's my PHP error log?" is answered identically across all site types. SCAN_DIR=$("$LSPHP_BIN" -i 2>/dev/null | awk -F'=> ' '/^Scan this dir/ {print $2; exit}') if [ -n "$SCAN_DIR" ]; then mkdir -p "$SCAN_DIR" cat > "$SCAN_DIR/99-user-error-log.ini" < "$SCAN_DIR/99-user-opcache.ini" fi fi ## ---- ownership ---- ## Own the docroot + logs so lsphp (running as $user) can read code and write ## logs. Don't recurse the whole tree blindly — just ensure the dirs we created ## and the log file are customer-owned (customer content may be large; a full ## recursive chown every boot would be wasteful, and the files are already ## customer-owned from the host side). touch "$SITE_DIR/logs/php-fpm/error.log" chown "$uid:$uid" "$SITE_DIR" "$SITE_DIR/public_html" "$SITE_DIR/logs" "$SITE_DIR/logs/php-fpm" "$SITE_DIR/logs/php-fpm/error.log" 2>/dev/null || true ## ---- exec lsphp -b as the customer user (PID 1) ---- ## Bind port is unprivileged (9000), so no root port-bind step is needed — start ## directly as $user. Prefer setpriv (util-linux, on the Ubuntu base); fall back ## to runuser. exec so lsphp becomes PID 1 and receives Docker's signals ## directly (clean stop/restart, matches the php-fpm container's lifecycle). echo "entrypoint-lsphp: exec $LSPHP_BIN -b $LSPHP_BIND as $user (uid=$uid)" if command -v setpriv >/dev/null 2>&1; then exec setpriv --reuid "$uid" --regid "$uid" --init-groups "$LSPHP_BIN" -b "$LSPHP_BIND" elif command -v runuser >/dev/null 2>&1; then exec runuser -u "$user" -- "$LSPHP_BIN" -b "$LSPHP_BIND" else exec sudo -u "$user" -E "$LSPHP_BIN" -b "$LSPHP_BIND" fi