mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-10-04 06:15:09 +00:00
* fix(cache): web UI can read what the display service caches again ledmatrix-web.service carried CacheDirectory=ledmatrix. With User= set to the installing user, systemd re-owns /var/cache/ledmatrix and everything in it to that user and its primary group whenever the directory's owner differs -- for a directory root created, on the first start. That erased the root:ledmatrix setgid layout the installers set up, so every file the display service (root) wrote afterwards was root:root 0660 and unreadable by the web interface: WARNING - Permission denied loading cache for display_current_state ... Since #547 install_service.sh renders the web unit from the template, so every fresh install hit this. Measured on one rig: 392 unreadable files, and the web UI's display status, on-demand state and plugin health empty. Existing installs only receive `git pull`, never a reinstalled unit, so the fix for them is in the code the root display service runs: - DiskCache.set gives each file the directory's group (when the directory is group-writable) and 0660 on the open descriptor before the rename, independent of setgid. This also closes a window where a fresh file was visible as mkstemp's 0600. - DiskCache.share_existing_files repairs files an older version left behind, once per process from the cleanup thread. It works through O_NOFOLLOW descriptors and skips hard links and other users' files: the directory is writable by the web user, and root must not be steered into changing a file outside it. For new installs, the web unit drops CacheDirectory=/CacheDirectoryMode=, and install_web_service.sh stops replacing an existing directory's ledmatrix group with the user's group. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(web): on-demand and current-display status read the display's latest state Found testing the cache-permission fix on a rig: once the web interface could read display_on_demand_state at all, /display/on-demand/status kept answering "active" for over 100 seconds while the file on disk said "idle". Both status routes read the display service's keys through the web process's memory tier, which serves the first copy it read for the full max_age (120s). Read them with memory_ttl=0, as every other cross-process reader (plugin health/metrics, the on-demand mailbox) already does. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(install): re-group the cache dir whenever the web user is outside its group install_web_service.sh replaced an existing cache directory's group only when it was root's. A directory in any other group the web user is not a member of -- root:ledmatrix, for a user who is not in ledmatrix -- was left alone, and every file root wrote there stayed unreadable to the web interface. Replace the group whenever the installing user is not in it. A directory whose group the user is already in (ledmatrix, or the user's own group where CacheDirectory= left it) is still left as it is: re-grouping a working directory strands the files already in it on the old group. When the group does change and root-owned JSON files carrying the old group are present, try-restart ledmatrix.service so DiskCache.share_existing_files re-groups them through its symlink- and hard-link-safe path, rather than a recursive chgrp. Verified under WSL's systemd for seven directory states (user group, ledmatrix member, ledmatrix non-member with and without root files, root:root, missing, unnamed gid); the previous version left the non-member case unchanged. Addresses CodeRabbit review on #593. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
170 lines
6.9 KiB
Bash
Executable File
170 lines
6.9 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# Exit on error
|
|
set -e
|
|
|
|
# Get the actual user who invoked sudo
|
|
if [ -n "$SUDO_USER" ]; then
|
|
ACTUAL_USER="$SUDO_USER"
|
|
else
|
|
ACTUAL_USER=$(whoami)
|
|
fi
|
|
|
|
# Get the home directory of the actual user
|
|
USER_HOME=$(eval echo ~$ACTUAL_USER)
|
|
|
|
# Determine the Project Root Directory (parent of scripts/install/)
|
|
PROJECT_ROOT_DIR=$(cd "$(dirname "$0")/../.." && pwd)
|
|
|
|
# shellcheck source=scripts/install/lib_systemd_render.sh
|
|
source "$PROJECT_ROOT_DIR/scripts/install/lib_systemd_render.sh"
|
|
|
|
echo "Installing LED Matrix Display Service for user: $ACTUAL_USER"
|
|
echo "Using home directory: $USER_HOME"
|
|
echo "Project root directory: $PROJECT_ROOT_DIR"
|
|
|
|
# Render the main display unit from its template. The display service runs as
|
|
# root (it needs GPIO), so __USER__ is always root here -- unlike the web unit
|
|
# below, which runs as whoever installed it.
|
|
#
|
|
# A missing template or a failed render is fatal: falling through would leave
|
|
# whatever unit already sits at /etc/systemd/system/ledmatrix.service (from a
|
|
# previous install) untouched, and the enable/start step below would then
|
|
# silently reuse that stale unit instead of the one this run was asked to
|
|
# install.
|
|
if [ -f "$PROJECT_ROOT_DIR/systemd/ledmatrix.service" ]; then
|
|
ESCAPED_PROJECT_ROOT_DIR=$(sed_escape_replacement "$PROJECT_ROOT_DIR")
|
|
MAIN_UNIT_TMP=$(mktemp)
|
|
trap 'rm -f "$MAIN_UNIT_TMP"' EXIT
|
|
if ! sed "s|__PROJECT_ROOT_DIR__|$ESCAPED_PROJECT_ROOT_DIR|g; s|__USER__|root|g" \
|
|
"$PROJECT_ROOT_DIR/systemd/ledmatrix.service" > "$MAIN_UNIT_TMP"; then
|
|
echo "ERROR: failed to render ledmatrix.service from its template." >&2
|
|
exit 1
|
|
fi
|
|
# Copy the service file to the systemd directory
|
|
sudo cp "$MAIN_UNIT_TMP" /etc/systemd/system/ledmatrix.service
|
|
# Clean up
|
|
rm -f "$MAIN_UNIT_TMP"
|
|
trap - EXIT
|
|
else
|
|
echo "ERROR: ledmatrix.service template not found at $PROJECT_ROOT_DIR/systemd/ledmatrix.service." >&2
|
|
exit 1
|
|
fi
|
|
|
|
|
|
# Reload systemd to recognize the new service (or modified service)
|
|
sudo systemctl daemon-reload
|
|
|
|
if [ -f "/etc/systemd/system/ledmatrix.service" ]; then
|
|
echo "Enabling ledmatrix.service (main display) to start on boot..."
|
|
sudo systemctl enable ledmatrix.service
|
|
echo "Starting ledmatrix.service (main display)..."
|
|
sudo systemctl start ledmatrix.service
|
|
else
|
|
echo "Skipping enable/start for ledmatrix.service as it was not configured."
|
|
fi
|
|
|
|
# === LEDMatrix Web Interface service (ledmatrix-web.service) ===
|
|
echo "Installing LEDMatrix Web Interface service (ledmatrix-web.service)..."
|
|
|
|
# Rendered from systemd/ledmatrix-web.service, the same template
|
|
# install_web_service.sh uses. This was an inline heredoc until it drifted from
|
|
# the template: it had lost Wants=network-online.target, RestartSec,
|
|
# SyslogIdentifier and Environment=USE_THREADING. Because
|
|
# src/startup_validator.py compares the installed unit against the template,
|
|
# every boot warned "re-run install_service.sh" -- and doing so reinstalled the
|
|
# same stale copy, so the warning could never clear.
|
|
#
|
|
# As with the main unit above, a missing template or a failed render is
|
|
# fatal -- otherwise the enable/start check below would fall back to
|
|
# whatever unit (possibly stale) already exists at the destination path.
|
|
if [ -f "$PROJECT_ROOT_DIR/systemd/ledmatrix-web.service" ]; then
|
|
ESCAPED_ACTUAL_USER=$(sed_escape_replacement "$ACTUAL_USER")
|
|
WEB_UNIT_TMP=$(mktemp)
|
|
trap 'rm -f "$WEB_UNIT_TMP"' EXIT
|
|
if ! sed "s|__PROJECT_ROOT_DIR__|$ESCAPED_PROJECT_ROOT_DIR|g; s|__USER__|$ESCAPED_ACTUAL_USER|g" \
|
|
"$PROJECT_ROOT_DIR/systemd/ledmatrix-web.service" > "$WEB_UNIT_TMP"; then
|
|
echo "ERROR: failed to render ledmatrix-web.service from its template." >&2
|
|
exit 1
|
|
fi
|
|
sudo cp "$WEB_UNIT_TMP" /etc/systemd/system/ledmatrix-web.service
|
|
rm -f "$WEB_UNIT_TMP"
|
|
trap - EXIT
|
|
else
|
|
echo "ERROR: ledmatrix-web.service template not found at $PROJECT_ROOT_DIR/systemd/ledmatrix-web.service." >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Health check / rollback units for automatic updates; see install_web_service.sh.
|
|
for VERIFY_UNIT in ledmatrix-update-verify.service ledmatrix-update-verify.path; do
|
|
if [ -f "$PROJECT_ROOT_DIR/systemd/$VERIFY_UNIT" ]; then
|
|
VERIFY_UNIT_TMP=$(mktemp)
|
|
if sed "s|__PROJECT_ROOT_DIR__|$ESCAPED_PROJECT_ROOT_DIR|g; s|__USER__|$ESCAPED_ACTUAL_USER|g" "$PROJECT_ROOT_DIR/systemd/$VERIFY_UNIT" > "$VERIFY_UNIT_TMP"; then
|
|
sudo cp "$VERIFY_UNIT_TMP" "/etc/systemd/system/$VERIFY_UNIT"
|
|
else
|
|
echo "WARNING: failed to render $VERIFY_UNIT; automatic code updates will stay paused." >&2
|
|
fi
|
|
rm -f "$VERIFY_UNIT_TMP"
|
|
fi
|
|
done
|
|
|
|
echo "Reloading systemd daemon for web service..."
|
|
sudo systemctl daemon-reload
|
|
|
|
if [ -f "/etc/systemd/system/ledmatrix-web.service" ]; then
|
|
echo "Enabling ledmatrix-web.service to start on boot..."
|
|
sudo systemctl enable ledmatrix-web.service
|
|
|
|
if [ -f /etc/systemd/system/ledmatrix-update-verify.path ]; then
|
|
echo "Enabling ledmatrix-update-verify.path (automatic update health check)..."
|
|
sudo systemctl enable --now ledmatrix-update-verify.path || echo "WARNING: could not enable ledmatrix-update-verify.path; automatic code updates will stay paused" >&2
|
|
fi
|
|
|
|
echo "Starting ledmatrix-web.service..."
|
|
sudo systemctl start ledmatrix-web.service
|
|
|
|
echo "LEDMatrix Web Interface service (ledmatrix-web.service) installation complete."
|
|
echo "It will start based on the 'web_display_autostart' setting in config/config.json."
|
|
else
|
|
echo "Skipping enable/start for ledmatrix-web.service as it was not configured."
|
|
fi
|
|
# === End of LEDMatrix Web Interface service ===
|
|
|
|
|
|
# Check the status
|
|
echo "Service status for main display (ledmatrix.service):"
|
|
sudo systemctl status ledmatrix.service || echo "ledmatrix.service not found or failed to get status."
|
|
echo "Service status for web interface (ledmatrix-web.service):"
|
|
sudo systemctl status ledmatrix-web.service || echo "ledmatrix-web.service not found or failed to get status."
|
|
|
|
echo ""
|
|
echo "LED Matrix Services have been processed."
|
|
echo ""
|
|
echo "To stop the main display when you SSH in:"
|
|
echo " sudo systemctl stop ledmatrix.service"
|
|
echo "To stop the web interface:"
|
|
echo " sudo systemctl stop ledmatrix-web.service"
|
|
|
|
echo ""
|
|
echo "To check if the main display service is running:"
|
|
echo " sudo systemctl status ledmatrix.service"
|
|
echo "To check if the web interface service is running:"
|
|
echo " sudo systemctl status ledmatrix-web.service"
|
|
|
|
echo ""
|
|
echo "To restart the main display service:"
|
|
echo " sudo systemctl restart ledmatrix.service"
|
|
echo "To restart the web interface service:"
|
|
echo " sudo systemctl restart ledmatrix-web.service"
|
|
|
|
echo ""
|
|
echo "To view logs for the main display:"
|
|
echo " journalctl -u ledmatrix.service"
|
|
echo "To view logs for the web interface:"
|
|
echo " journalctl -u ledmatrix-web.service"
|
|
|
|
echo ""
|
|
echo "To disable autostart for the main display:"
|
|
echo " sudo systemctl disable ledmatrix.service"
|
|
echo "To disable autostart for the web interface:"
|
|
echo " sudo systemctl disable ledmatrix-web.service" |