Migrate CUPS Printing addon; restructure install.sh into Core Settings/Addons/Advanced; bump to v2.5.0
First Addon migrated: menus/addon_cups.sh (install, reconfigure for network access, complete uninstall/purge). Different risk profile from everything migrated so far - it genuinely mutates real system state (apt install/remove --purge, /etc/cups, ufw) at fixed paths CUPS itself doesn't let us relocate, unlike the systemd/cron/bin paths this project already controls via $SYSTEMD_DIR etc. Only the polkit rule's directory is parameterized ($POLKIT_DIR, lib/config.sh, since that one is ours to place); everything else gets full command-level `sudo` stubbing in every test - there is no scratch equivalent for a real apt-managed subsystem's own file layout. Also added $BUILD_USER (the admin account actually running the tool, as opposed to $KIOSK_USER) since CUPS needs to grant it lpadmin group membership. Restructured install.sh's top-level menu into Core Settings / Addons / Advanced (matching the legacy tool) instead of one flat list, now that Addons exists as its own category - cheap to do with one item in it, much more annoying to retrofit once the flat list has fifteen. Two bugs caught and fixed before they shipped: - A "wait for CUPS to start" retry loop used a bare `cmd1 && cmd2 && break` as its body while "simplifying" the legacy script's `if cmd1 && cmd2; then break; fi`. Being inside a loop doesn't protect a bare &&/|| list from set -e - only if/while/until conditions and the protected side of &&/|| do that - so the first command failing on an early iteration (near-certain right after a fresh install, before CUPS has actually started) would have crashed the entire session. Restored the `if` form; noted the lesson in the file's own header comment since it's a general trap, not CUPS-specific. - Resolved real uncertainty, rather than assuming: how far does run_menu's `handler || true` guard (v2.1.0) actually protect? Wrote a minimal isolated test (a bare `false` three function calls deep, called via `outer || true` at the top) and confirmed bash's errexit exemption for the left side of `||` covers the *entire* evaluation, arbitrarily deep through function calls - not just the immediately invoked function. So the session-crash risk this project has been chasing since v2.1.0 is already covered end-to-end by that one fix. Per-statement guards (`|| true`, explicit `if`) still earn their keep for a different reason: without them a deep failure bubbles silently past the menu actually responsible for it to wherever the nearest `|| true` happens to sit, which can be several menu levels above where the user actually was - not a crash, but a confusing jump. Verified: - Full regression: re-ran every existing scratch-config/stub test suite after the lib/config.sh change (new $BUILD_USER/$POLKIT_DIR) and after the install.sh restructuring - all still clean. - New scratch/stub test for addon_cups.sh: full state-machine coverage (not installed -> decline -> install -> running -> reconfigure -> stopped -> start -> uninstall decline -> uninstall confirm -> not installed again) with every `sudo` call intercepted and only `rm` targeting the scratch $POLKIT_DIR ever actually executed; confirmed the polkit rule's content and that declining install makes zero sudo calls. Added both apt-failure paths (update fails, install fails) and confirmed the tool reports clearly and returns to the menu instead of dying, exercising the exact bug class just fixed. - End-to-end: ran the real install.sh as a genuine non-root, non- "kiosk" user through the full new three-level structure - Core Settings -> Sites -> back -> back, Addons -> CUPS -> declined install (using this container's real, unstubbed dpkg check, correctly reporting "not installed" and making no apt/systemctl calls) -> back -> back, Advanced -> Diagnostics -> System status -> back -> back -> Exit. Zero invalid-choice errors, clean exit code 0 throughout.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# Ubuntu Based Kiosk
|
||||
|
||||
**Current Version:** 2.4.0 (check script header for latest version)
|
||||
**Current Version:** 2.5.0 (check script header for latest version)
|
||||
**Built with Claude Sonnet 4.6 AI assistance**
|
||||
**License:** GPL v3 - Keep derivatives open source
|
||||
**Repository:** https://github.com/outis1one/ubuntu-based-kiosk/
|
||||
@@ -1205,7 +1205,12 @@ terminal menu and the web UI, so they can't drift apart).
|
||||
- `menus/diagnostics.sh` — **Diagnostics**: system status, log viewing,
|
||||
audio diagnostics, network test — 4 of the legacy Advanced menu's 12
|
||||
items, all read-only.
|
||||
- `install.sh` — entry point for the modular tool. Run it against an
|
||||
- `menus/addon_cups.sh` — **CUPS Printing** (Addons): install,
|
||||
reconfigure for network access, complete uninstall (purge). The first
|
||||
Addon migrated — genuinely mutates real system state (apt packages,
|
||||
`/etc/cups`, ufw) rather than this project's own files.
|
||||
- `install.sh` — entry point for the modular tool, now grouped **Core
|
||||
Settings / Addons / Advanced** like the legacy menu. Run it against an
|
||||
*already-installed* kiosk:
|
||||
```bash
|
||||
git clone https://github.com/outis1one/ubuntu-based-kiosk/
|
||||
@@ -1216,7 +1221,7 @@ terminal menu and the web UI, so they can't drift apart).
|
||||
**Honest status:** this does not yet replace first-time installation, or
|
||||
most of the old installer. `ubuntu-based-kiosk.sh` is still ~12,000
|
||||
lines and still contains its own unremoved, unmodified copies of every
|
||||
menu above (plus Upgrade, Reinstall, Uninstall, all Addons, and the
|
||||
menu above (plus Upgrade, Reinstall, Uninstall, 4 more Addons, and the
|
||||
other 8 Advanced items — none of that has moved yet). Both copies
|
||||
coexist deliberately: the old ones stay until enough
|
||||
of Core Settings/Addons/Advanced is migrated to
|
||||
@@ -1229,9 +1234,15 @@ at all.
|
||||
|
||||
## Project Status & Future Plans
|
||||
|
||||
**Current Version:** 2.4.0
|
||||
**Current Version:** 2.5.0
|
||||
|
||||
**Recent Updates (v2.4.0):**
|
||||
**Recent Updates (v2.5.0):**
|
||||
- **First Addon migrated:** CUPS Printing — install/reconfigure/complete uninstall, in `./install.sh`. Genuinely mutates real system state (`apt install`/`remove --purge`, `/etc/cups`, `ufw`) at fixed paths CUPS itself doesn't let us relocate, so every test uses full command-level `sudo` stubbing rather than the scratch-directory approach used for this project's own files.
|
||||
- **Menu restructured:** `install.sh`'s top level is now grouped Core Settings / Addons / Advanced, matching the legacy tool, instead of one flat list — done now while it's cheap, ahead of the list getting unwieldy.
|
||||
- **Bug fix:** a "wait for service to start" retry loop used a bare `cmd1 && cmd2 && break` as its body — that's not made safe by being inside a loop; a bare `&&`/`||` list used as a standalone statement is fully subject to `set -e`, and the first command failing on an early iteration (near-certain right after a fresh install) would have killed the whole session. Restored the `if cmd1 && cmd2; then break; fi` form.
|
||||
- **Resolved:** real uncertainty about how far `run_menu`'s `handler || true` guard (added in v2.1.0) actually reaches — confirmed with an isolated test that it protects against a failing command no matter how many function calls deep, so the session-crash risk chased since v2.1.0 is already covered end-to-end by that one fix. Per-statement guards still matter for a different reason: without them, a deep failure bubbles past the menu actually responsible for it to wherever the nearest `|| true` happens to catch it.
|
||||
|
||||
**Previous (v2.4.0):**
|
||||
- **Diagnostics migrated** — system status, log viewing (Electron/LightDM/journal), an 8-step audio diagnostic, and a ping+DNS network test, from the legacy Advanced menu. A change of pace: everything here is read-only, no destructive-action risk to manage.
|
||||
- **Bug fix (set -e safety):** every diagnostic whose failure is the expected case — no lightdm running, no audio hardware, no network, missing logs, `ping`/`nslookup` not even installed — was a bare unguarded statement that would have crashed the whole session instead of reporting "not found" and moving on. Fixed throughout; a diagnostics tool has to survive exactly the broken states it exists to diagnose.
|
||||
- Manual Electron Update, Factory Reset, Export/Import Settings, Emergency Hotspot, and Fix Blank Screen are staying in the legacy script for now — destructive/mutating, and some share Upgrade's coupling to the legacy script's self-extraction mechanism (see v2.3.0 notes).
|
||||
|
||||
+40
-10
@@ -12,12 +12,13 @@
|
||||
# at a time, so a change to (say) the Sites menu can't accidentally break
|
||||
# WiFi setup or the uninstaller three thousand lines away.
|
||||
#
|
||||
# Migrated so far: Sites & Page Timing (menus/sites.sh), Display &
|
||||
# Interaction (menus/display.sh), Timezone (menus/timezone.sh), Hidden
|
||||
# Site PIN (menus/hidden_pin.sh), Password Protection & Lockout
|
||||
# (menus/lockout.sh), WiFi (menus/wifi.sh), Power/Display/Quiet Hours
|
||||
# (menus/power_schedule.sh), Diagnostics (menus/diagnostics.sh - system
|
||||
# status/logs/audio/network from the legacy Advanced menu).
|
||||
# Migrated so far, grouped the same way the legacy menu groups them:
|
||||
# Core Settings: Sites & Page Timing, Display & Interaction, Timezone,
|
||||
# Hidden Site PIN, Password Protection & Lockout, WiFi,
|
||||
# Power/Display/Quiet Hours.
|
||||
# Addons: CUPS Printing (menus/addon_cups.sh).
|
||||
# Advanced: Diagnostics (menus/diagnostics.sh - system status/logs/
|
||||
# audio/network).
|
||||
#
|
||||
# Usage (once the kiosk has already been installed):
|
||||
# git clone <repo>
|
||||
@@ -49,6 +50,8 @@ source "$SCRIPT_DIR/menus/wifi.sh"
|
||||
source "$SCRIPT_DIR/menus/power_schedule.sh"
|
||||
# shellcheck source=menus/diagnostics.sh
|
||||
source "$SCRIPT_DIR/menus/diagnostics.sh"
|
||||
# shellcheck source=menus/addon_cups.sh
|
||||
source "$SCRIPT_DIR/menus/addon_cups.sh"
|
||||
|
||||
################################################################################
|
||||
# Preflight
|
||||
@@ -81,10 +84,12 @@ if ! is_kiosk_installed; then
|
||||
fi
|
||||
|
||||
################################################################################
|
||||
# Top-level menu
|
||||
# Top-level menu - grouped the same way the legacy menu groups them
|
||||
# (Core Settings / Addons / Advanced), so the structure stays familiar
|
||||
# and the flat list doesn't grow unwieldy as more menus migrate in.
|
||||
################################################################################
|
||||
|
||||
main_menu_builder() {
|
||||
core_settings_menu_builder() {
|
||||
MENU_LABELS=(
|
||||
"Sites & Page Timing"
|
||||
"Display & Interaction"
|
||||
@@ -93,7 +98,6 @@ main_menu_builder() {
|
||||
"Password Protection & Lockout"
|
||||
"WiFi"
|
||||
"Power/Display/Quiet Hours"
|
||||
"Diagnostics"
|
||||
)
|
||||
MENU_HANDLERS=(
|
||||
sites_menu
|
||||
@@ -103,10 +107,36 @@ main_menu_builder() {
|
||||
lockout_menu
|
||||
wifi_menu
|
||||
power_schedule_menu
|
||||
diagnostics_menu
|
||||
)
|
||||
}
|
||||
|
||||
core_settings_menu() {
|
||||
run_menu "CORE SETTINGS" core_settings_menu_builder
|
||||
}
|
||||
|
||||
addons_menu_builder() {
|
||||
MENU_LABELS=("CUPS Printing")
|
||||
MENU_HANDLERS=(addon_cups_menu)
|
||||
}
|
||||
|
||||
addons_menu() {
|
||||
run_menu "ADDONS" addons_menu_builder
|
||||
}
|
||||
|
||||
advanced_menu_builder() {
|
||||
MENU_LABELS=("Diagnostics")
|
||||
MENU_HANDLERS=(diagnostics_menu)
|
||||
}
|
||||
|
||||
advanced_menu() {
|
||||
run_menu "ADVANCED" advanced_menu_builder
|
||||
}
|
||||
|
||||
main_menu_builder() {
|
||||
MENU_LABELS=("Core Settings" "Addons" "Advanced")
|
||||
MENU_HANDLERS=(core_settings_menu addons_menu advanced_menu)
|
||||
}
|
||||
|
||||
main_menu_status() {
|
||||
echo "Managing kiosk at: ${KIOSK_DIR}"
|
||||
}
|
||||
|
||||
@@ -29,6 +29,12 @@
|
||||
: "${CRON_D_DIR:=/etc/cron.d}"
|
||||
: "${BIN_DIR:=/usr/local/bin}"
|
||||
: "${NETPLAN_DIR:=/etc/netplan}"
|
||||
: "${POLKIT_DIR:=/etc/polkit-1/localauthority/50-local.d}"
|
||||
|
||||
# The admin account actually running this tool (as opposed to $KIOSK_USER,
|
||||
# the kiosk's own restricted account) - used where an addon needs to grant
|
||||
# *this* user a group membership (e.g. lpadmin for CUPS).
|
||||
: "${BUILD_USER:=${SUDO_USER:-$(whoami)}}"
|
||||
|
||||
# Site/tab arrays
|
||||
declare -a URLS=()
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
#!/bin/bash
|
||||
################################################################################
|
||||
# menus/addon_cups.sh - "CUPS Printing" addon (from the legacy Addons menu).
|
||||
#
|
||||
# First Addon migrated. Genuinely mutates real system state - installs/
|
||||
# purges apt packages, writes /etc/cups/cupsd.conf and a polkit rule,
|
||||
# touches ufw - at fixed paths CUPS itself doesn't let us relocate the
|
||||
# way $SYSTEMD_DIR/$CRON_D_DIR/etc let us relocate our own files. Only
|
||||
# the polkit rule's directory is parameterized ($POLKIT_DIR, since that's
|
||||
# ours to place); everything else (cupsd.conf, apt, systemctl, ufw) gets
|
||||
# full command-level `sudo` stubbing in every test - there is no scratch
|
||||
# equivalent for a real apt-managed subsystem's own file layout.
|
||||
#
|
||||
# Depends on: lib/menu.sh, lib/config.sh being sourced first.
|
||||
################################################################################
|
||||
|
||||
cups_is_installed() {
|
||||
dpkg -l 2>/dev/null | grep -q "^ii\s\+cups\s"
|
||||
}
|
||||
|
||||
cups_is_active() {
|
||||
systemctl is-active --quiet cups
|
||||
}
|
||||
|
||||
addon_cups_status() {
|
||||
if cups_is_installed && cups_is_active; then
|
||||
echo "CUPS: installed and running (http://$(get_ip_address):631)"
|
||||
elif cups_is_installed; then
|
||||
echo "CUPS: installed but not running"
|
||||
else
|
||||
echo "CUPS: not installed"
|
||||
fi
|
||||
}
|
||||
|
||||
addon_cups_menu_builder() {
|
||||
if cups_is_installed && cups_is_active; then
|
||||
MENU_LABELS=("Reconfigure for network access" "Complete uninstall (purge)")
|
||||
MENU_HANDLERS=(action_reconfigure_cups action_cups_uninstall)
|
||||
elif cups_is_installed; then
|
||||
MENU_LABELS=("Start CUPS" "Complete uninstall (purge)")
|
||||
MENU_HANDLERS=(action_start_cups action_cups_uninstall)
|
||||
else
|
||||
MENU_LABELS=("Install CUPS printing")
|
||||
MENU_HANDLERS=(action_install_cups)
|
||||
fi
|
||||
}
|
||||
|
||||
addon_cups_menu() {
|
||||
run_menu "CUPS PRINTING SUPPORT" addon_cups_menu_builder addon_cups_status
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# Actions
|
||||
################################################################################
|
||||
|
||||
action_install_cups() {
|
||||
echo
|
||||
ask_yes_no "Install CUPS printing?" "n" || { echo "Cancelled"; return; }
|
||||
|
||||
echo "Installing CUPS from scratch..."
|
||||
if ! sudo apt update; then
|
||||
log_error "apt update failed - check network/package sources and try again"
|
||||
return 1
|
||||
fi
|
||||
if ! sudo apt install -y cups cups-client cups-filters printer-driver-all \
|
||||
printer-driver-cups-pdf hplip printer-driver-gutenprint \
|
||||
foomatic-db-compressed-ppds openprinting-ppds; then
|
||||
log_error "CUPS package installation failed"
|
||||
return 1
|
||||
fi
|
||||
|
||||
sudo systemctl enable cups 2>/dev/null || true
|
||||
sudo systemctl start cups 2>/dev/null || true
|
||||
|
||||
echo "Waiting for CUPS to start..."
|
||||
for _ in {1..30}; do
|
||||
# Must stay in an `if` - a bare `cmd1 && cmd2` statement is
|
||||
# subject to set -e itself when cmd1 fails, which is virtually
|
||||
# guaranteed on early iterations right after install.
|
||||
if cups_is_active && lpstat -r &>/dev/null 2>&1; then
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
# $BUILD_USER already resolves to $SUDO_USER when the tool was run via
|
||||
# sudo, so a single usermod covers it - the legacy code ran this twice
|
||||
# (once for a hardcoded computed user, once again for $SUDO_USER
|
||||
# directly), which was harmless but genuinely redundant.
|
||||
sudo usermod -aG lpadmin "$BUILD_USER"
|
||||
|
||||
action_reconfigure_cups
|
||||
|
||||
log_success "CUPS installed"
|
||||
echo " Web interface: http://$(get_ip_address):631"
|
||||
}
|
||||
|
||||
action_start_cups() {
|
||||
sudo systemctl enable cups
|
||||
sudo systemctl start cups
|
||||
log_success "CUPS started"
|
||||
}
|
||||
|
||||
action_reconfigure_cups() {
|
||||
if [[ -f /etc/cups/cupsd.conf ]]; then
|
||||
sudo cp /etc/cups/cupsd.conf "/etc/cups/cupsd.conf.backup-$(date +%Y%m%d-%H%M%S)"
|
||||
fi
|
||||
|
||||
if command -v cupsctl &>/dev/null; then
|
||||
sudo cupsctl --remote-admin --remote-any --share-printers 2>/dev/null || true
|
||||
fi
|
||||
|
||||
sudo sed -i 's/^Listen localhost:631/Port 631/' /etc/cups/cupsd.conf 2>/dev/null || true
|
||||
sudo sed -i 's/^Listen 127.0.0.1:631/Port 631/' /etc/cups/cupsd.conf 2>/dev/null || true
|
||||
|
||||
sudo mkdir -p "$POLKIT_DIR"
|
||||
sudo tee "$POLKIT_DIR/kiosk-printing.pkla" > /dev/null <<EOF
|
||||
[Allow kiosk printing]
|
||||
Identity=unix-user:${KIOSK_USER}
|
||||
Action=org.opensuse.cupspkhelper.mechanism.*
|
||||
ResultAny=yes
|
||||
ResultInactive=yes
|
||||
ResultActive=yes
|
||||
EOF
|
||||
|
||||
sudo ufw allow 631/tcp comment 'CUPS' 2>/dev/null || true
|
||||
sudo systemctl restart cups 2>/dev/null || true
|
||||
|
||||
log_success "CUPS configured for network access"
|
||||
}
|
||||
|
||||
action_cups_uninstall() {
|
||||
echo
|
||||
ask_yes_no "Completely remove CUPS, including all queues and settings (purge)?" "n" || { echo "Cancelled"; return; }
|
||||
|
||||
echo "Performing complete CUPS uninstall..."
|
||||
|
||||
sudo systemctl stop cups cups-browsed 2>/dev/null || true
|
||||
sudo systemctl disable cups cups-browsed 2>/dev/null || true
|
||||
|
||||
sudo apt remove --purge -y cups cups-daemon cups-client cups-filters \
|
||||
cups-common cups-core-drivers cups-server-common cups-browsed \
|
||||
cups-ppdc cups-bsd libcups2 libcupsimage2 2>/dev/null || true
|
||||
|
||||
sudo apt remove --purge -y printer-driver-all printer-driver-cups-pdf \
|
||||
hplip printer-driver-gutenprint foomatic-db-compressed-ppds \
|
||||
openprinting-ppds 2>/dev/null || true
|
||||
|
||||
sudo rm -rf /etc/cups /var/cache/cups /var/spool/cups /var/log/cups /usr/share/cups
|
||||
sudo rm -f "$POLKIT_DIR/kiosk-printing.pkla"
|
||||
|
||||
sudo apt autoremove -y
|
||||
sudo apt clean
|
||||
|
||||
log_success "CUPS completely removed"
|
||||
}
|
||||
+40
-2
@@ -1,8 +1,46 @@
|
||||
#!/bin/bash
|
||||
################################################################################
|
||||
### Ubuntu Based Kiosk v2.4.0 ###
|
||||
### Ubuntu Based Kiosk v2.5.0 ###
|
||||
################################################################################
|
||||
#
|
||||
# RELEASE v2.5.0 - First Addon Migrated (CUPS), Menu Restructured
|
||||
# - New in ./install.sh: CUPS Printing (menus/addon_cups.sh) - the first
|
||||
# Addon migrated. Install/reconfigure/complete uninstall (purge),
|
||||
# genuinely mutating real system state (apt install/remove --purge,
|
||||
# /etc/cups, ufw) at fixed paths CUPS itself doesn't let us relocate -
|
||||
# unlike the systemd/cron/bin paths this project controls, there is no
|
||||
# scratch equivalent for a real apt-managed subsystem's own file
|
||||
# layout, so every test uses full command-level `sudo` stubbing
|
||||
# instead. Only the polkit rule's directory is parameterized
|
||||
# ($POLKIT_DIR, since that one is ours to place).
|
||||
# - install.sh's top-level menu is now grouped the same way the legacy
|
||||
# menu groups things - Core Settings / Addons / Advanced - instead of
|
||||
# one flat list, ahead of that list getting unwieldy as more Addons
|
||||
# and Advanced items migrate in.
|
||||
# - Two bugs caught and fixed before they ever shipped, both instructive
|
||||
# beyond this one file:
|
||||
# - A "wait for service to start" retry loop used a bare `cmd1 &&
|
||||
# cmd2 && break` as its body. That's not safe merely because it's
|
||||
# inside a loop - a bare &&/|| list used as a standalone statement
|
||||
# (not the condition of if/while/until) is fully subject to set -e,
|
||||
# and cmd1 failing on an early iteration (near-certain right after
|
||||
# a fresh install) would have killed the whole session. Restored
|
||||
# the `if cmd1 && cmd2; then break; fi` form the legacy script
|
||||
# already used correctly, rather than "simplifying" it away.
|
||||
# - Resolved real uncertainty about how far run_menu's `handler ||
|
||||
# true` guard (added in v2.1.0) actually reaches: verified with a
|
||||
# minimal isolated test that it protects against a bare failing
|
||||
# command no matter how many function calls deep it occurs - bash's
|
||||
# errexit exemption for the left side of `||` covers the entire
|
||||
# evaluation, not just the immediately-called function. So the
|
||||
# session-crash risk this project has been chasing since v2.1.0 is
|
||||
# already covered end-to-end by that one fix. Per-statement guards
|
||||
# (`|| true`, explicit `if`) still matter for a different reason:
|
||||
# without them a deep failure silently bubbles up past the menu
|
||||
# that's actually responsible for it to wherever the nearest `||
|
||||
# true` happens to catch it, which may be several menu levels
|
||||
# higher than where the user actually was.
|
||||
#
|
||||
# RELEASE v2.4.0 - Diagnostics Migrated
|
||||
# - New in ./install.sh: Diagnostics (menus/diagnostics.sh) - system
|
||||
# status, log viewing (Electron/LightDM/journal), an 8-step audio
|
||||
@@ -188,7 +226,7 @@ set -euo pipefail
|
||||
### SECTION 1: CONSTANTS & GLOBALS
|
||||
################################################################################
|
||||
|
||||
SCRIPT_VERSION="2.4.0"
|
||||
SCRIPT_VERSION="2.5.0"
|
||||
|
||||
# Resolve the real path to this script file.
|
||||
# When piped (curl|bash or wget|bash), BASH_SOURCE[0] is a pipe descriptor,
|
||||
|
||||
Reference in New Issue
Block a user