fix(cac-lsphp): mount docroot at /home/$user + symlink for true 1:1 compatibility

Customer concern: sites with /home/<user>/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/<user>/<domain> ->
/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/<user>/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) <noreply@anthropic.com>
This commit is contained in:
2026-06-10 06:54:28 -07:00
parent e99b8cb2d1
commit fc65b68bd6
2 changed files with 41 additions and 44 deletions

View File

@@ -4,10 +4,10 @@
## (matches the shared-vhost-template.tpl convention). One directive per line — ## (matches the shared-vhost-template.tpl convention). One directive per line —
## OLS PlainConf does NOT accept ';' separators. ## OLS PlainConf does NOT accept ';' separators.
## ##
## CRITICAL (feedback_ols_lsapi_no_script_filename_remap): docRoot here MUST be ## docRoot is /mnt/users/<user>/<domain>/public_html — the shared-ols container's
## the SAME absolute path the cac-lsphp sidecar has mounted, because OLS hands ## view (bulk /docker/users->/mnt/users mount). OLS sends lsphp exactly this path
## lsphp exactly docRoot+URI as SCRIPT_FILENAME and lsphp opens it. Both are ## (no remap); the cac-lsphp sidecar symlinks /mnt/users/<user>/<domain> -> its
## /mnt/users/<user>/<domain>/public_html. The panel asserts this parity. ## real /home/<user> mount, so PHP canonicalises it to /home/<user>/public_html.
docRoot ~~DOCROOT~~ docRoot ~~DOCROOT~~
enableScript 1 enableScript 1

View File

@@ -7,18 +7,21 @@
## network (extProcessor type lsapi, address <this-container>:9000) exactly ## network (extProcessor type lsapi, address <this-container>:9000) exactly
## like the shared httpd connects to a cac-fpm container's php-fpm on :9000. ## 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 ## Structurally identical to cac-fpm/cac-litespeed: same `uid`/`user` contract,
## `uid`/`user` contract, same /home/$user/{public_html,logs} layout, same ## the customer docroot mounted at /home/$user (so PHP sees /home/$user/public_html
## per-site ini drop-in mechanism as entrypoint-litespeed.sh. The only ## EXACTLY like the standalone tiers — true 1:1 drop-in for WordPress ABSPATH,
## difference from cac-litespeed is that OLS lives elsewhere, so this PID 1 is ## config paths, and DB-stored absolute paths). The only difference is OLS lives
## lsphp itself rather than a webserver supervisor. ## in a separate container, so this PID 1 is lsphp itself.
## ##
## IMPORTANT (see whp memory feedback_ols_lsapi_no_script_filename_remap): ## THE SYMLINK (see feedback_ols_lsapi_no_script_filename_remap): OLS has no
## OLS hands lsphp exactly its vhost docRoot path, so the shared-ols vhost ## ProxyFCGISetEnvIf-style remap — it hands lsphp exactly its vhost docRoot path.
## docRoot and THIS container's docroot mount MUST be the same absolute path. ## The shared-ols container serves from its bulk /docker/users->/mnt/users mount,
## The panel mounts the site at /mnt/users/<user>/<domain> in BOTH containers; ## so its docRoot (and the SCRIPT_FILENAME it sends us) is
## this entrypoint does not assume any particular path — it just runs lsphp, ## /mnt/users/<user>/<domain>/public_html. We create a symlink
## which opens whatever absolute SCRIPT_FILENAME the webserver sends. ## /mnt/users/<user>/<domain> -> /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 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 echo "FATAL: 'uid' and 'user' env vars are required (panel sets these from WHP_UID/WHP_USER)." >&2
exit 1 exit 1
fi fi
export user : "${domain:=localhost}"
export user domain
LSPHP_BIN="/usr/local/lsws/lsphp${PHPVER}/bin/lsphp" LSPHP_BIN="/usr/local/lsws/lsphp${PHPVER}/bin/lsphp"
if [ ! -x "$LSPHP_BIN" ]; then if [ ! -x "$LSPHP_BIN" ]; then
@@ -40,30 +44,24 @@ if [ ! -x "$LSPHP_BIN" ]; then
exit 1 exit 1
fi 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 if ! id -u "$user" >/dev/null 2>&1; then
useradd -u "$uid" -m -s /bin/bash "$user" useradd -u "$uid" -m -s /bin/bash "$user"
fi fi
mkdir -p "/home/$user/public_html" "/home/$user/logs/php-fpm"
## ---- locate the customer docroot ---- ## ---- compatibility symlink for the OLS-sent path ----
## Unlike cac-fpm/cac-litespeed (docroot at /home/$user), the shared-ols tier ## OLS sends SCRIPT_FILENAME under /mnt/users/<user>/<safe-domain>/public_html
## mounts each site at /mnt/users/<user>/<domain> — the SAME absolute path the ## (the shared-ols container's view). Point that at our real /home/$user mount so
## shared-ols vhost uses as docRoot, because OLS hands lsphp exactly that path as ## the path resolves. <safe-domain> matches the on-disk convention: wildcard
## SCRIPT_FILENAME (feedback_ols_lsapi_no_script_filename_remap). The panel ## `*.foo.com` is stored as `wildcard.foo.com`.
## mounts exactly ONE site dir here, so glob it (wildcard-safe: the on-disk dir SAFE_DOMAIN="$domain"
## is wildcard.<domain> for wildcard sites, which the glob picks up verbatim). case "$domain" in
SITE_DIR="" \*.*) SAFE_DOMAIN="wildcard.${domain#\*.}" ;;
for d in /mnt/users/"$user"/*/; do esac
[ -d "$d" ] || continue mkdir -p "/mnt/users/$user"
SITE_DIR="${d%/}" ln -sfn "/home/$user" "/mnt/users/$user/$SAFE_DOMAIN"
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 ---- ## ---- detached-lsphp pool sizing ----
# shellcheck source=/dev/null # shellcheck source=/dev/null
@@ -90,7 +88,7 @@ if [ -n "$SCAN_DIR" ]; then
mkdir -p "$SCAN_DIR" mkdir -p "$SCAN_DIR"
cat > "$SCAN_DIR/99-user-error-log.ini" <<EOF cat > "$SCAN_DIR/99-user-error-log.ini" <<EOF
; rendered at container start by entrypoint-lsphp.sh ; rendered at container start by entrypoint-lsphp.sh
error_log = ${SITE_DIR}/logs/php-fpm/error.log error_log = /home/${user}/logs/php-fpm/error.log
log_errors = On log_errors = On
EOF EOF
## Per-site opcache override (panel: Advanced Tuning → OpCache size); falls ## Per-site opcache override (panel: Advanced Tuning → OpCache size); falls
@@ -106,13 +104,12 @@ EOF
fi fi
## ---- ownership ---- ## ---- ownership ----
## Own the docroot + logs so lsphp (running as $user) can read code and write ## Ensure the dirs we created + the log file are customer-owned so lsphp (running
## logs. Don't recurse the whole tree blindly — just ensure the dirs we created ## as $user) can read code and write logs. Customer content is already
## and the log file are customer-owned (customer content may be large; a full ## customer-owned from the host side, so we don't recurse the whole (potentially
## recursive chown every boot would be wasteful, and the files are already ## large) tree on every boot.
## customer-owned from the host side). touch "/home/$user/logs/php-fpm/error.log"
touch "$SITE_DIR/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
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) ---- ## ---- exec lsphp -b as the customer user (PID 1) ----
## Bind port is unprivileged (9000), so no root port-bind step is needed — start ## Bind port is unprivileged (9000), so no root port-bind step is needed — start