cups-browsed is the daemon that watches the network and creates print queues by itself. Hardening it took a root daemon with a predictable cache down to a confined service account, but a daemon that turns anything advertising itself on the network into a print queue is a lot of exposure for a convenience, so it comes out of the default install while that is reworked. Only the discovery half: CUPS itself stays and printing keeps working, with each printer added by hand in Print Settings. The migration disables the unit before removing the package because that is the only order that works: pacman deletes the unit file but not the enable symlink, and once the unit is gone systemd can no longer resolve it by name to clean that up. It then removes the queues discovery generated. cups-browsed keeps those when it stops, since KeepGeneratedQueuesOnShutdown defaults to Yes, and they route through its own implicitclass backend, which goes with the package, so they cannot print again. Idle ones go. A queue with jobs on it is left alone and named: implicitclass only needs cups-browsed to choose a destination, so a job already past that point finishes on its own, and deleting the queue would abort it. One printer's job does not hold up the removal. A printer added by hand has an ipp:// or usb:// device and is left where it is. A queue whose jobs cannot be asked about is left alone rather than assumed idle, including one named so that lpstat would misread it -- "all" is its word for every destination, and a leading dash or a comma reads as another option or a list. Where CUPS does not answer at all, or a queue will not delete, discovery is still stopped but the package stays and no marker is written. omarchy-migrate records a migration for the user as soon as it exits zero, so that is where the machine stays until someone removes the package by hand, and the message says so rather than implying a retry. The queue list is read under LC_ALL=C because lpstat translates "device for", and captured rather than piped, so a cupsd it cannot reach is reported instead of reading like a machine with nothing to clean up. It removes with plain pacman -R rather than omarchy-pkg-drop, which passes -n and would discard /etc/cups/cups-browsed.conf instead of keeping it as a .pacsave. A removal meant to be temporary should not delete the machine's copy of its own configuration. Without -s either, so it only ever removes the package it names: sweeping newly unneeded dependencies is nothing today, but it is not a promise a rolling dependency graph can keep. Queue names come off the network, since cups-browsed names its queues after what the printer advertised. CUPS allows every printable character but space, tab, / and #, and lpstat and lpadmin take a destination as an option value, so a name with a leading dash or a comma is reported rather than passed to them and guessed at. Migration state is per user, so a machine-wide marker records the one removal. Without it, an account whose first migration run came after someone deliberately reinstalled discovery would quietly take it back out again. The install-time override for cups-browsed.conf now waits for cups-browsed rather than for CUPS. Guarding it on a file CUPS still ships would write a configuration file for a package nothing installed, and pacman would later land the package's own copy beside it as a .pacnew. The hardened configuration stays in the tree. omarchy-settings still ships the cups-browsed.conf override, the sysusers account and the service drop-in, so they are what discovery returns onto. Co-Authored-By: Codex XHigh <noreply@openai.com>
132 lines
6.7 KiB
Bash
132 lines
6.7 KiB
Bash
echo "Temporarily remove automatic printer discovery"
|
|
|
|
# cups-browsed is the daemon that watches the network and creates print queues
|
|
# by itself. Hardening it (1787815267) took a root daemon with a predictable
|
|
# cache down to a confined service account, but a daemon that turns anything
|
|
# advertising itself on the network into a print queue is a lot of exposure for
|
|
# a convenience, so it comes out of the default install while that is reworked.
|
|
# Temporarily, and only the discovery half: CUPS itself stays, printing keeps
|
|
# working, and a printer is added by hand in Print Settings instead of
|
|
# appearing on its own.
|
|
machine_marker="${OMARCHY_CUPS_BROWSED_REMOVAL_MARKER:-/var/lib/omarchy/migrations/1788009111}"
|
|
|
|
[[ ! -e $machine_marker ]] || exit 0
|
|
|
|
# Nothing to do on a machine that never had it. Checked before any sudo so those
|
|
# runs never prompt for a password, and before the marker so no machine pays a
|
|
# password prompt for a removal it does not need.
|
|
omarchy-pkg-present cups-browsed || exit 0
|
|
|
|
# Ask pacman whether the removal is possible before touching the service. If
|
|
# something here depends on cups-browsed, the alternative is a machine whose
|
|
# discovery daemon has been stopped and whose package removal then failed --
|
|
# broken rather than removed.
|
|
if ! pacman -R --print cups-browsed >/dev/null 2>&1; then
|
|
echo " Something else on this machine still depends on cups-browsed, so it is staying installed."
|
|
exit 0
|
|
fi
|
|
|
|
# Disable before removing, and this is the only window in which it works.
|
|
# pacman deletes the unit file but not the enable symlink systemd wrote under
|
|
# /etc, and once the unit is gone `systemctl disable` refuses it by name and
|
|
# leaves the symlink dangling with nothing left that can clean it. Stopping is
|
|
# part of the same step: a daemon whose executable has been unlinked keeps
|
|
# running until it is told not to. A masked or already-disabled unit reports
|
|
# not-enabled and is left alone. Doing it before the queue list is read also
|
|
# means the list cannot grow a new entry while it is being acted on.
|
|
if systemctl is-enabled --quiet cups-browsed.service 2>/dev/null; then
|
|
sudo systemctl disable --now cups-browsed.service
|
|
elif systemctl is-active --quiet cups-browsed.service 2>/dev/null; then
|
|
sudo systemctl stop cups-browsed.service
|
|
fi
|
|
|
|
# cups-browsed keeps the queues it generated when it stops --
|
|
# KeepGeneratedQueuesOnShutdown defaults to Yes and nothing here overrides it --
|
|
# and those queues route through its own implicitclass backend, which goes with
|
|
# the package, so none of them can print again. The idle ones are removed rather
|
|
# than left in Print Settings looking like printers; the ones with jobs on them
|
|
# are handled below. Only queues on that backend: a printer added by hand has an
|
|
# ipp:// or usb:// device and is left alone.
|
|
#
|
|
# LC_ALL=C because lpstat translates "device for", and on a German or French
|
|
# machine the untranslated pattern would match nothing and read exactly like a
|
|
# machine that had no queues to clean up. Captured rather than piped so that
|
|
# failing to reach cupsd is distinguishable from finding nothing. The name is
|
|
# matched greedily because CUPS allows a colon in a queue name but never a
|
|
# space, so the last ": implicitclass://" is the separator and an earlier colon
|
|
# belongs to the name.
|
|
if ! queue_report=$(LC_ALL=C lpstat -v 2>/dev/null); then
|
|
echo " Could not ask CUPS which queues discovery had created."
|
|
echo " Discovery is off, but cups-browsed stays installed; remove it by hand once CUPS answers."
|
|
exit 0
|
|
fi
|
|
|
|
generated_queues=$(printf '%s\n' "$queue_report" |
|
|
sed -n 's|^device for \(.*\): implicitclass://.*|\1|p')
|
|
|
|
unremoved=0
|
|
|
|
while IFS= read -r queue; do
|
|
[[ -n $queue ]] || continue
|
|
|
|
# A queue name is whatever the printer advertised, put through cups-browsed's
|
|
# own sanitizer. `lpstat -o` takes its destination as an optional argument, so
|
|
# a leading dash reads as the next option and a comma as a list separator, and
|
|
# "all" is its word for every destination. The jobs on such a queue cannot be
|
|
# asked about, so it is left alone and named. Never a reason to keep the
|
|
# package, though: that would hand a printer that picked its own name a veto
|
|
# over the removal.
|
|
if [[ $queue == -* || $queue == *,* || $queue == "all" ]]; then
|
|
echo " Cannot safely ask about jobs on the queue named '$queue'; remove it in Print Settings."
|
|
continue
|
|
fi
|
|
|
|
# Removing a queue aborts what is printing on it. The implicitclass backend
|
|
# only needs cups-browsed to pick a destination, so a job already past that
|
|
# point finishes on its own even though the daemon has stopped -- and a job
|
|
# that has not is one this cannot route anyway. Either way the queue is left
|
|
# for the person whose job it is, and named so they know to remove it.
|
|
if job_report=$(LC_ALL=C lpstat -o "$queue" 2>/dev/null); then
|
|
if [[ -n $job_report ]]; then
|
|
echo " $queue still has jobs, so it is being left alone."
|
|
echo " Once they finish or are cancelled, remove it in Print Settings; it cannot print again."
|
|
continue
|
|
fi
|
|
else
|
|
echo " Could not check for jobs on $queue, so it is being left alone."
|
|
continue
|
|
fi
|
|
|
|
if ! sudo lpadmin -x "$queue"; then
|
|
echo " Could not remove the queue $queue."
|
|
unremoved=1
|
|
fi
|
|
done <<<"$generated_queues"
|
|
|
|
# A queue that would not delete is a CUPS that is not answering as expected, so
|
|
# the package stays rather than deleting the backend out from under it. Discovery
|
|
# is stopped either way, which is the half that mattered. omarchy-migrate records
|
|
# this migration for the user as soon as it exits zero, so this is where the
|
|
# machine stays until someone removes the package by hand -- said plainly rather
|
|
# than dressed up as a retry.
|
|
if ((unremoved)); then
|
|
echo " Leaving cups-browsed installed. Discovery is off; remove the package by hand once those queues are gone."
|
|
exit 0
|
|
fi
|
|
|
|
# Raw pacman rather than omarchy-pkg-drop, which passes -n: that discards the
|
|
# files pacman has marked as backups instead of renaming them .pacsave, and
|
|
# /etc/cups/cups-browsed.conf is one of them. A removal meant to be temporary
|
|
# should leave the machine's copy of its own configuration behind. Plain -R
|
|
# rather than -Rs, so this only ever removes the one package it names: -s also
|
|
# sweeps dependencies that have become unneeded, which is nothing today but is
|
|
# a promise the dependency graph of a rolling distribution cannot keep.
|
|
# pacman's systemd hook reloads the system manager once the unit file goes.
|
|
sudo pacman -R --noconfirm cups-browsed
|
|
|
|
# Migration state is per user, so every account on this machine runs every
|
|
# migration. Without machine-wide state, an account whose first run comes after
|
|
# someone deliberately reinstalled cups-browsed would quietly take it back out
|
|
# again. This records that the machine has had its one removal.
|
|
sudo install -Dm644 /dev/null "$machine_marker"
|