From fc65b68bd6e1846f5a318b45a0ec2de1c8510202 Mon Sep 17 00:00:00 2001 From: jknapp Date: Wed, 10 Jun 2026 06:54:28 -0700 Subject: [PATCH] fix(cac-lsphp): mount docroot at /home/$user + symlink for true 1:1 compatibility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Customer concern: sites with /home//public_html baked into config or the DB must keep working — a changed in-container docroot path would break WordPress ABSPATH, hardcoded includes, cached absolute paths, etc., making the upgrade a non-drop-in. Fix: the sidecar now mounts the docroot at /home/$user (IDENTICAL to cac-fpm/cac-litespeed) and the entrypoint symlinks /mnt/users// -> /home/$user. OLS still serves from its bulk /mnt/users mount and sends lsphp that path (no remap available), but the symlink resolves it to the real /home/$user files AND PHP canonicalises it — so __FILE__/__DIR__/realpath/ABSPATH all report /home//public_html. Verified end-to-end through the shared OLS: a request reports __FILE__=/home/homeuser/public_html/probe.php, ABSPATH=/home/homeuser/public_html/, and stored /home paths resolve. True 1:1 drop-in. Co-Authored-By: Claude Opus 4.8 (1M context) --- configs/shared-ols/vhconf.tpl | 8 ++-- scripts/entrypoint-lsphp.sh | 77 +++++++++++++++++------------------ 2 files changed, 41 insertions(+), 44 deletions(-) diff --git a/configs/shared-ols/vhconf.tpl b/configs/shared-ols/vhconf.tpl index 86a2d78..9f54d4f 100644 --- a/configs/shared-ols/vhconf.tpl +++ b/configs/shared-ols/vhconf.tpl @@ -4,10 +4,10 @@ ## (matches the shared-vhost-template.tpl convention). One directive per line — ## OLS PlainConf does NOT accept ';' separators. ## -## CRITICAL (feedback_ols_lsapi_no_script_filename_remap): docRoot here MUST be -## the SAME absolute path the cac-lsphp sidecar has mounted, because OLS hands -## lsphp exactly docRoot+URI as SCRIPT_FILENAME and lsphp opens it. Both are -## /mnt/users///public_html. The panel asserts this parity. +## docRoot is /mnt/users///public_html — the shared-ols container's +## view (bulk /docker/users->/mnt/users mount). OLS sends lsphp exactly this path +## (no remap); the cac-lsphp sidecar symlinks /mnt/users// -> its +## real /home/ mount, so PHP canonicalises it to /home//public_html. docRoot ~~DOCROOT~~ enableScript 1 diff --git a/scripts/entrypoint-lsphp.sh b/scripts/entrypoint-lsphp.sh index 934d20c..a07da04 100644 --- a/scripts/entrypoint-lsphp.sh +++ b/scripts/entrypoint-lsphp.sh @@ -7,18 +7,21 @@ ## 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. +## Structurally identical to cac-fpm/cac-litespeed: same `uid`/`user` contract, +## the customer docroot mounted at /home/$user (so PHP sees /home/$user/public_html +## EXACTLY like the standalone tiers — true 1:1 drop-in for WordPress ABSPATH, +## config paths, and DB-stored absolute paths). The only difference is OLS lives +## in a separate container, so this PID 1 is lsphp itself. ## -## 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. +## THE SYMLINK (see feedback_ols_lsapi_no_script_filename_remap): OLS has no +## ProxyFCGISetEnvIf-style remap — it hands lsphp exactly its vhost docRoot path. +## The shared-ols container serves from its bulk /docker/users->/mnt/users mount, +## so its docRoot (and the SCRIPT_FILENAME it sends us) is +## /mnt/users///public_html. We create a symlink +## /mnt/users// -> /home/$user so that path resolves to the real +## /home/$user/public_html files. PHP canonicalises the symlink, so +## __FILE__/__DIR__/realpath all report /home/$user/public_html (verified +## 2026-06-10) — the customer never sees the /mnt/users path. set -euo pipefail @@ -32,7 +35,8 @@ 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 +: "${domain:=localhost}" +export user domain LSPHP_BIN="/usr/local/lsws/lsphp${PHPVER}/bin/lsphp" if [ ! -x "$LSPHP_BIN" ]; then @@ -40,30 +44,24 @@ if [ ! -x "$LSPHP_BIN" ]; then exit 1 fi -## ---- user ---- +## ---- user + directories (identical to entrypoint-litespeed.sh: docroot at +## /home/$user, the customer's bind-mounted domain dir) ---- if ! id -u "$user" >/dev/null 2>&1; then useradd -u "$uid" -m -s /bin/bash "$user" fi +mkdir -p "/home/$user/public_html" "/home/$user/logs/php-fpm" -## ---- 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" +## ---- compatibility symlink for the OLS-sent path ---- +## OLS sends SCRIPT_FILENAME under /mnt/users///public_html +## (the shared-ols container's view). Point that at our real /home/$user mount so +## the path resolves. matches the on-disk convention: wildcard +## `*.foo.com` is stored as `wildcard.foo.com`. +SAFE_DOMAIN="$domain" +case "$domain" in + \*.*) SAFE_DOMAIN="wildcard.${domain#\*.}" ;; +esac +mkdir -p "/mnt/users/$user" +ln -sfn "/home/$user" "/mnt/users/$user/$SAFE_DOMAIN" ## ---- detached-lsphp pool sizing ---- # shellcheck source=/dev/null @@ -90,7 +88,7 @@ if [ -n "$SCAN_DIR" ]; then mkdir -p "$SCAN_DIR" cat > "$SCAN_DIR/99-user-error-log.ini" </dev/null || true +## Ensure the dirs we created + the log file are customer-owned so lsphp (running +## as $user) can read code and write logs. Customer content is already +## customer-owned from the host side, so we don't recurse the whole (potentially +## large) tree on every boot. +touch "/home/$user/logs/php-fpm/error.log" +chown "$uid:$uid" "/home/$user" "/home/$user/public_html" "/home/$user/logs" "/home/$user/logs/php-fpm" "/home/$user/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