Files
printserver/entrypoint.sh
T
juergen 7f26ca4f2f
ci/woodpecker/push/build Pipeline was successful
Fail loudly when CUPS cannot bind port 631
The queue was still advertised over mDNS while cupsd never bound TCP 631:
a rootless Podman container on the host network cannot bind the privileged
port. Apple clients then discovered a printer they could not reach and
reported a confusing _ipps connection failure.

* entrypoint.sh: verify that CUPS accepts connections on 631 after startup
  and exit with an actionable message otherwise
* entrypoint.sh: mark the script executable in the repository
* cupsd.conf: drop BrowseRemoteProtocols, which CUPS 2.4 no longer supports
  and which logs an error on every start
* README: add a troubleshooting section for the port-binding failure and the
  _ipps/TLS certificate requirement, and document the /etc/cups bind-mount
  caveat plus the CUPS 3 PPD deprecation
2026-10-18 21:07:35 +02:00

139 lines
5.7 KiB
Bash
Executable File

#!/bin/sh
# Startup script for the containerized CUPS print server.
#
# Starts the D-Bus system bus, Avahi (mDNS/DNS-SD) and CUPS, then registers
# the Brother HL-4150CDN queue if it does not exist yet. The CUPS daemon is
# kept in the foreground so the container lives as long as the print server.
#
# The queue uses Brother's BR-Script3 (PostScript 3) PPD and, by default,
# sends jobs to the printer over raw AppSocket (socket://<ip>:9100). CUPS
# converts client documents to PostScript and the PPD turns device options
# (tray, duplex, colour, ...) into PostScript "setpagedevice" instructions.
#
# Configuration (all optional, via environment):
# PRINTER_IP IP address / host name of the network printer.
# PRINTER_URI Full device URI; overrides PRINTER_IP when set.
# PRINTER_PROTOCOL socket (default), lpd or ipp - used with PRINTER_IP.
# PRINTER_NAME CUPS queue name and advertised (DNS-SD) name.
# PRINTER_DESCRIPTION CUPS Description (lpadmin -D) shown in the web UI.
# PPD_FILE PPD used for the queue.
set -eu
PRINTER_NAME="${PRINTER_NAME:-HL4150CDN}"
PRINTER_IP="${PRINTER_IP:-}"
PRINTER_URI="${PRINTER_URI:-}"
PRINTER_PROTOCOL="${PRINTER_PROTOCOL:-socket}"
PRINTER_DESCRIPTION="${PRINTER_DESCRIPTION:-}"
PPD_FILE="${PPD_FILE:-/usr/share/cups/model/BR4150_2.PPD}"
LOG_PREFIX="[entrypoint]"
log() { echo "${LOG_PREFIX} $*"; }
# Build a device URI from PRINTER_IP unless an explicit PRINTER_URI is given.
# Raw AppSocket is the default: the HL-4150CDN accepts PostScript directly on
# port 9100 (its BR-Script3 interpreter auto-detects the job language).
if [ -z "${PRINTER_URI}" ] && [ -n "${PRINTER_IP}" ]; then
case "${PRINTER_PROTOCOL}" in
socket)
PRINTER_URI="socket://${PRINTER_IP}:9100"
;;
lpd)
PRINTER_URI="lpd://${PRINTER_IP}/binary_p1"
;;
ipp)
PRINTER_URI="ipp://${PRINTER_IP}/ipp/print"
;;
*)
log "unknown PRINTER_PROTOCOL '${PRINTER_PROTOCOL}', defaulting to socket"
PRINTER_URI="socket://${PRINTER_IP}:9100"
;;
esac
fi
# --- D-Bus system bus -------------------------------------------------------
mkdir -p /run/dbus /var/lib/dbus
if [ ! -s /etc/machine-id ] && [ ! -s /var/lib/dbus/machine-id ]; then
log "generating D-Bus machine id"
dbus-uuidgen > /var/lib/dbus/machine-id
fi
if [ ! -S /run/dbus/system_bus_socket ]; then
log "starting dbus-daemon (system bus)"
dbus-daemon --system --fork
fi
# --- Avahi / mDNS -----------------------------------------------------------
mkdir -p /run/avahi-daemon
if ! pgrep -x avahi-daemon >/dev/null 2>&1; then
log "starting avahi-daemon"
avahi-daemon --daemonize --no-chroot
fi
# --- CUPS -------------------------------------------------------------------
log "starting cupsd"
/usr/sbin/cupsd -f &
CUPSD_PID=$!
# Wait until CUPS accepts requests.
i=0
while [ "$i" -lt 30 ]; do
if lpstat -r >/dev/null 2>&1; then
break
fi
i=$((i + 1))
sleep 0.5
done
# Share printers on the network and allow remote administration.
cupsctl --share-printers --remote-admin >/dev/null 2>&1 || true
# --- Brother HL-4150CDN queue ----------------------------------------------
if lpstat -p "${PRINTER_NAME}" >/dev/null 2>&1; then
log "printer ${PRINTER_NAME} already exists"
elif [ -n "${PRINTER_URI}" ]; then
if [ -f "${PPD_FILE}" ]; then
log "adding printer ${PRINTER_NAME} -> ${PRINTER_URI}"
lpadmin -p "${PRINTER_NAME}" -E -v "${PRINTER_URI}" -P "${PPD_FILE}" \
|| log "warning: lpadmin failed to add ${PRINTER_NAME}"
else
log "warning: PPD ${PPD_FILE} not found; skipping printer creation"
fi
else
log "neither PRINTER_URI nor PRINTER_IP is set; skipping automatic printer creation"
fi
# Make sure the queue is shared, named for advertising and set as default.
if lpstat -p "${PRINTER_NAME}" >/dev/null 2>&1; then
lpadmin -p "${PRINTER_NAME}" -o printer-is-shared=true -E >/dev/null 2>&1 || true
if [ -n "${PRINTER_DESCRIPTION}" ]; then
lpadmin -p "${PRINTER_NAME}" -D "${PRINTER_DESCRIPTION}" >/dev/null 2>&1 || true
fi
lpadmin -d "${PRINTER_NAME}" >/dev/null 2>&1 || true
log "queue ${PRINTER_NAME} is shared and set as default"
fi
# --- Verify CUPS is reachable on the network --------------------------------
# cupsd also runs with nothing but its domain socket, e.g. when it cannot bind
# the privileged port 631 (rootless Podman with host networking). It then still
# advertises the queue over mDNS, so clients see a printer they cannot reach.
# Detect that and fail loudly instead of reporting a healthy server.
if ! curl -fsS --noproxy '*' --max-time 2 -o /dev/null http://127.0.0.1:631/ 2>/dev/null; then
log "ERROR: cupsd is not listening on TCP 631 - network clients (AirPrint/IPP) cannot reach this server"
if [ -f /var/log/cups/error_log ]; then
log " recent CUPS errors:"
tail -n 5 /var/log/cups/error_log | sed 's/^/ /'
fi
log " If the log shows 'Unable to open listen socket for address 0.0.0.0:631 - Permission denied',"
log " port 631 is privileged and this rootless container may not bind it on the host network."
log " Either allow unprivileged binding of 631 (host, needs root), persistently:"
log " echo 'net.ipv4.ip_unprivileged_port_start=631' > /etc/sysctl.d/90-unprivileged-ports.conf"
log " sysctl --system"
log " or run this container as a rootful (system) service."
log " See the Troubleshooting section of the README."
exit 1
fi
log "ready: CUPS on 631/tcp, mDNS on 5353/udp"
# Keep the container running by waiting on cupsd.
wait "${CUPSD_PID}"