`iso/build-iso.sh --offline` (and the workflow's new `offline` box, on by default) adds ~420 MB to the ISO and copies it to /opt/kiosk-offline: - apt/: local repository with every package install.sh's provisioning and the CUPS addon install ask for, NodeSource's nodejs, and all dependencies a fresh server install doesn't already have. Resolved against the source ISO's own package manifest via a synthesized dpkg status (iso/offline/fake-dpkg-status.py), so apt downloads only what's missing and makes the same virtual/alternative choices as the target. - npm/: prebuilt kiosk-app (Electron 42, binary included - it no longer downloads at npm install time) and Web UI node_modules. On first boot, kiosk-firstboot installs online as before when there's internet; with none it waits KIOSK_OFFLINE_WAIT (90s), then runs `kiosk-offline-apt on` (apt.conf.d drop-in: bundle is the ONLY source) and turns it off again afterwards. install.sh notices offline mode via offline_mode_active (lib/config.sh) and installs nodejs from the bundle and unpacks prebuilt node_modules instead of npm install. Package lists moved into PROVISION_APT_PACKAGES / PROVISION_INTEL_APT_PACKAGES / CUPS_APT_PACKAGES so the bundler reads the same lists install.sh installs from. Tested: built against the 26.04.1 ISO, then in a network-less chroot of the ISO's own server filesystem installed every package set, ran provision_create_kiosk_user/install_nodejs/install_app and webui_install_app_files in offline mode (Electron runs, no missing libraries, chrome-sandbox setuid), and ran kiosk-firstboot through the offline switch with a stub install.sh. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Cjz8a58eB5P9sUweqd3NHm
167 lines
5.8 KiB
Bash
167 lines
5.8 KiB
Bash
#!/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.
|
|
################################################################################
|
|
|
|
# Also read by iso/offline/build-bundle.sh to bundle these for offline installs.
|
|
CUPS_APT_PACKAGES=(
|
|
cups cups-client cups-filters printer-driver-all
|
|
printer-driver-cups-pdf hplip printer-driver-gutenprint
|
|
foomatic-db-compressed-ppds openprinting-ppds
|
|
)
|
|
|
|
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_APT_PACKAGES[@]}"; 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; }
|
|
cups_do_uninstall
|
|
}
|
|
|
|
# The actual removal, no prompt - shared with Complete Uninstall so that
|
|
# operation doesn't need to re-implement CUPS teardown a second time.
|
|
cups_do_uninstall() {
|
|
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 2>/dev/null || true
|
|
sudo apt clean 2>/dev/null || true
|
|
|
|
log_success "CUPS completely removed"
|
|
}
|