Files

5.3 KiB

Design

Context

entrypoint.sh starts D-Bus, Avahi and cupsd, configures the managed queue (lpadmin -D/-o/-E on every start) and then verifies cupsd answers on TCP 631. Any queue present in cupsd — including ones added manually via the web UI — is advertised as the DNS-SD instance "<Description> @ <host>" (queue name when no Description is set). Avahi registers only one instance per name: with two queues deriving the same name, one is silently never advertised, and CUPS 2.4 does not rename on collision (upstream scheduler/dnssd.c, TODO: Handle collisions). Clients that pinned the losing name (macOS stores it in the dnssd:// device URI) then report the printer offline. See proposal.md for the incident. The container ships cups-client (guaranteed lpstat), avahi-utils and curl (Containerfile).

Goals / Non-Goals

Goals:

  • At startup, detect when two or more queues would derive the same DNS-SD instance name and log an actionable warning naming the queues.
  • Keep the check strictly best-effort: it must never affect startup success or queue configuration (consistent with the existing warning style under set -eu).
  • Give operators a documented way to confirm and fix the condition by hand (README troubleshooting entry).

Non-Goals:

  • No automatic remediation (no renaming, no un-sharing, no deleting queues).
  • No change to how/when lpadmin runs — the restart re-registration churn is out of scope for this change.
  • No client-side tooling; the check runs only inside the container.

Decisions

D1 — Derive names from live cupsd data, not from mDNS. The check compares derived instance names (Description or queue name per queue) instead of listening to actual Avahi registrations. Derivation is deterministic and race-free at startup; observing mDNS (avahi-browse) would depend on registration timing and only detect collisions after the fact.

  • Alternative considered: post-startup mDNS audit ("shared queue missing from advertisements") — catches more failure modes but is timing-sensitive and needs instance→queue mapping via TXT rp; rejected as the primary mechanism, still documented as a manual verification step in the README.

D2 — Inputs: LC_ALL=C lpstat -l -p for names/Descriptions, printers.conf for the shared flag.

  • Queue names + Descriptions come from lpstat -l -p run with LC_ALL=C so the Description: line parses regardless of container locale (the live value is authoritative — it is exactly what cupsd will register).
  • Only shared queues are advertised, so the shared flag comes from /etc/cups/printers.conf (<Printer …> block: Shared yes). When the flag cannot be determined (file not yet flushed after this run's lpadmin, queue missing from it), the queue is treated as shared — a collision warning for a not-yet-shared queue is still actionable, whereas staying silent on a shared one reproduces the original incident.
  • Alternative considered: ipptool Get-Printer-Attributes — richer data, but availability of ipptool in the image is not guaranteed by the Containerfile package list; rejected to keep the check dependency-free beyond cups-client.

D3 — Placement and failure mode. A check_dnssd_name_collisions function runs after the queue configuration blocks and the TCP reachability check, immediately before the final ready log line, so the warning is the last thing an operator sees in podman logs. It logs through the existing log() helper ([entrypoint] warning: … followed by indented detail lines, mirroring the TCP-631 error block) and never exits non-zero; every parse failure degrades to "check skipped" (the script runs under set -eu, so all command substitutions are guarded with || true).

D4 — Collision = exact match on the name base. The @ <host> suffix is identical for every queue on the server, so equality is decided on the base alone; the full derived name (base + @ <short-host>) is still printed in the warning so it matches what dns-sd -B shows. The remedy text offers the two fixes that worked on the live system: give each queue a unique Description (lpadmin -D), or remove the redundant queue (lpadmin -x), followed by remove/re-add of the client queue (README details why clients must be re-added).

Risks / Trade-offs

  • [Queues created after startup are not re-checked] → acceptable: the check runs on every container start, and the README documents a manual dns-sd -B _ipp._tcp verification for live changes.
  • [Stale shared flag if printers.conf is not flushed in time] → mitigated by assuming shared when unknown (errs toward warning), see D2.
  • [False warning when a shared flag parse misreads a queue block] → parsing is scoped to <Printer name> … </Printer> blocks with exact keyword matches; a misread can at worst add a queue to the comparison, and the warning is advisory only.
  • [Warning becomes noise on intentionally duplicated setups] → duplication is never functional in CUPS/Avahi (one registrant always loses), so there is no legitimate configuration the warning would nag about.

Migration Plan

Log-output-only change: deploy by rebuilding the image and restarting the container (normal quadlet cycle). No state, config or client impact; rollback is reverting entrypoint.sh/README.