Merge pull request #9002 from acrogenesis/remove-legacy-installer-privileged-files

Repair legacy paths and privileged files left by retired installers

(cherry picked from commit 943d2fcbe9)
This commit is contained in:
Ryan Hughes committed 2026-08-30 12:08:00 -04:00
1 parent 47ce81ebca
commit cf3d69c38d
45 files changed
+3279 -8

No files matched your search

+339
View File
@@ -0,0 +1,339 @@
echo "Remove privileged files left behind by retired Omarchy installers"
sudoers_dir="${OMARCHY_SUDOERS_DIR:-/etc/sudoers.d}"
systemd_dir="${OMARCHY_SYSTEMD_SYSTEM_DIR:-/etc/systemd/system}"
machine_marker="${OMARCHY_RETIRED_INSTALLER_ARTIFACTS_MARKER:-/var/lib/omarchy/migrations/1788025225}"
reload_needed_marker="$machine_marker.daemon-reload"
[[ ! -e $machine_marker ]] || exit 0
as_root() {
if (( EUID == 0 )); then
"$@"
else
sudo "$@"
fi
}
# Three installers that no longer exist each left a root-owned file behind, and
# nothing in Omarchy has ever removed any of them. Each is judged against what
# the installer that wrote it actually produced, so a file of the same name that
# an administrator wrote themselves is left alone.
#
# Emit the lines a parser would act on: comments and blanks dropped, backslash
# continuations joined, and runs of whitespace collapsed so a reformatted copy
# still compares equal. Reads the body on stdin, because /etc/sudoers.d is 0750
# root:root and the caller has to hand us an elevated read.
#
# Comments are tested before continuations are joined, which is the order every
# consumer here uses: udev's parse_file discards a '#' line without looking at a
# trailing backslash (`udevadm verify` on "# disabled \" plus a bogus key reports
# the error on line 2), sudo's toke.l comment rule consumes to the newline and
# clears its continuation flag, and systemd's config_parse tests the comment
# characters before appending to a continuation. Joining first would let a
# comment ending in a backslash swallow the live line beneath it.
#
# FORMAT is sudoers or systemd. systemd takes ';' as well as '#'. sudo does not
# treat every '#' as a comment: toke.l has INITIAL rules for ^#include and
# ^#includedir, and its comment pattern excludes '#' followed by a digit or
# -digit so those reach the ID token as a numeric uid user spec. Those lines are
# active directives, and a file carrying one must not read as though it held only
# generated lines.
active_lines() {
local format="$1"
local comments='#'
local line logical=""
[[ $format == "systemd" ]] && comments='#;'
while IFS= read -r line || [[ -n $line ]]; do
if [[ $line =~ ^[[:space:]]*[$comments] ]] &&
! { [[ $format == "sudoers" ]] && sudoers_hash_is_active "$line"; }; then
# The two consumers part company here. sudo ends the logical line at a
# comment and keeps what came before it, so `visudo -cf` reads a spec
# ending in a backslash, then a comment, then a second spec as two live
# specs; dropping the pending half would hide an administrator's grant and
# let this file read as though the installer had written all of it. systemd
# resumes the continuation instead: `systemd-analyze verify` on "ExecStop=\"
# + "; c" + a path resolves that path, so the pending half has to stay.
if [[ $format == "sudoers" ]]; then
emit_logical "$logical"
logical=""
fi
continue
fi
if [[ $line == *\\ ]]; then
logical+="${line%\\} "
continue
fi
emit_logical "$logical$line"
logical=""
done
# A file whose last line ends in a backslash still carries a live directive for
# systemd: `systemd-analyze verify` resolves an ExecStop= written that way.
# udev ignores the dangling line and sudo rejects the file outright, so emitting
# it costs those two nothing.
emit_logical "$logical"
}
# One logical line, whitespace collapsed so a reformatted copy still compares
# equal, and nothing at all for a line that held only whitespace.
emit_logical() {
local -a parts
read -ra parts <<<"$1"
if (( ${#parts[@]} )); then
printf '%s\n' "${parts[*]}"
fi
}
sudoers_hash_is_active() {
local line="$1"
[[ $line =~ ^[[:space:]]*#include[[:blank:]] ]] && return 0
[[ $line =~ ^[[:space:]]*#includedir[[:blank:]] ]] && return 0
[[ $line =~ ^[[:space:]]*#-?[0-9] ]] && return 0
return 1
}
# install/preflight/first-run-mode.sh (2025-08-25 to 2026-05-25) granted the
# installing account passwordless sudo for the rest of the first boot, including
# an unrestricted /usr/bin/systemctl from 2025-10-14 on -- enough to link and
# start a unit of the user's own, which is root. bin/omarchy-first-run was meant
# to delete the grant, but it clears its first-run.mode guard as the very first
# statement and only reaches the removal after eight set -e steps, two of which
# touch the network. Any failure in between leaves the grant on the machine with
# nothing left to retry it.
#
# The installer rewrote this file nine times across two locations -- the last two
# bodies came from install/post-install/first-run-mode.sh, whose cleanup alias
# names /usr/bin/rm as well as /bin/rm -- and only the later ones carry both
# Cmnd_Alias lines, so keying on those would walk past the earlier ones. Instead
# require every active line to be one the installer itself emitted, plus at least
# one line that is unmistakably this grant: its own self-cleanup. One
# hand-written line anywhere in the file and it is not ours to delete.
first_run_sudoers_is_generated() {
local spec_pattern='^([^[:space:]]+) ALL=\(ALL\) NOPASSWD: (.+)$'
local marker_pattern='^/bin/rm -f /home/([^/]+)/\.local/state/omarchy/first-run\.mode$'
local line user command marker_user generated_user=""
local seen_any=0 seen_marker=0 seen_spec=0
while IFS= read -r line; do
seen_any=1
case "$line" in
"Cmnd_Alias SYMLINK_RESOLVED = /usr/bin/ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf")
continue
;;
"Cmnd_Alias FIRST_RUN_CLEANUP = /bin/rm -f /etc/sudoers.d/first-run" | \
"Cmnd_Alias FIRST_RUN_CLEANUP = /bin/rm -f /etc/sudoers.d/first-run, /bin/rm -f /etc/sudoers.d/99-omarchy-installer-reboot" | \
"Cmnd_Alias FIRST_RUN_CLEANUP = /usr/bin/rm -f /etc/sudoers.d/first-run, /bin/rm -f /etc/sudoers.d/first-run")
seen_marker=1
continue
;;
esac
# Everything else the installer wrote is a user spec naming the installing
# account, whose name cannot be assumed here: it may since have been renamed
# or removed, and a second account runs this migration too.
if [[ ! $line =~ $spec_pattern ]]; then
return 1
fi
user=${BASH_REMATCH[1]}
command=${BASH_REMATCH[2]}
if [[ -n $generated_user && $user != "$generated_user" ]]; then
return 1
fi
generated_user=$user
seen_spec=1
case "$command" in
"/usr/bin/systemctl" | "/usr/bin/ufw" | "/usr/bin/ufw-docker" | \
"/usr/bin/gtk-update-icon-cache" | "/usr/bin/udevadm" | \
"/usr/bin/tee /etc/udev/rules.d/*" | "SYMLINK_RESOLVED")
continue
;;
"FIRST_RUN_CLEANUP" | "/bin/rm -f /etc/sudoers.d/first-run")
seen_marker=1
continue
;;
esac
if [[ $command =~ $marker_pattern ]]; then
marker_user=${BASH_REMATCH[1]}
[[ $marker_user == "$generated_user" ]] || return 1
seen_marker=1
continue
fi
return 1
done < <(active_lines sudoers)
(( seen_any && seen_marker && seen_spec ))
}
# bin/omarchy-install-tailscale (2025-08-22 to 2026-02-02) ran
# "echo \"\$USER ALL=(ALL) NOPASSWD: \$(which tsui)\" | sudo tee
# /etc/sudoers.d/tsui" one line after installing tsui by piping a vendor script
# to bash with no sudo at all, so the path it resolved was usually the user's own
# ~/.local/bin. Overwrite that file, run sudo tsui, and you are root. The grant
# goes whatever the path turned out to be: the feature was dropped from Omarchy,
# and unrestricted NOPASSWD on a TUI that can shell out is an escalation from a
# root-owned path too.
tsui_sudoers_is_generated() {
local spec_pattern='^[^[:space:]]+ ALL=\(ALL\) NOPASSWD: ([^[:space:]]+)$'
local line command="" count=0
while IFS= read -r line; do
count=$(( count + 1 ))
if (( count > 1 )); then
return 1
fi
if [[ ! $line =~ $spec_pattern ]]; then
return 1
fi
command=${BASH_REMATCH[1]}
done < <(active_lines sudoers)
if (( count == 1 )) && [[ ${command##*/} == "tsui" ]]; then
return 0
fi
return 1
}
# install/plymouth.sh wrote this unit for two days (2025-07-05 to 2025-07-07)
# with an unquoted heredoc, so ExecStop names the installing user's home. The
# unit is enabled WantedBy=multi-user.target, so systemd runs that path as uid 0
# on every shutdown, with no hardware event needed to reach it.
plymouth_unit_runs_from_home() {
local binary="omarchy-plymouth-shutdown-sync"
local exec_stop_pattern='^ExecStop[[:space:]]*=[[:space:]]*(.*)$'
local home_pattern="^/.+/\\.local/share/omarchy/bin/$binary\$"
local line word
local -a words
local matched=1
while IFS= read -r line; do
if [[ ! $line =~ $exec_stop_pattern ]]; then
continue
fi
read -ra words <<<"${BASH_REMATCH[1]}"
if (( ! ${#words[@]} )); then
# An empty assignment resets the list, so nothing named before it still
# runs. `systemd-analyze verify` reports the missing command for a unit
# with one ExecStop=, and reports nothing once a bare ExecStop= follows it.
# An administrator who neutralised the unit this way is left alone.
matched=1
continue
fi
# systemd reads -, @, +, ! and : ahead of the command as flags, not as part
# of the path it runs.
word=${words[0]}
while [[ $word == [-@+!:]* ]]; do
word=${word:1}
done
if [[ $word =~ $home_pattern ]]; then
# Non-empty ExecStop= assignments append to the command list. Once a
# vulnerable command is present it stays live until an empty assignment
# explicitly resets the list; a later packaged command does not replace it.
matched=0
fi
done < <(active_lines systemd)
return $matched
}
# /etc/sudoers.d is 0750 root:root as shipped, and omarchy-migrate runs as the
# logged-in user, so an unelevated [[ -f ]] on a file in there is false whether or
# not the file exists and an unelevated read returns nothing. Both tests and both
# reads have to be elevated or this migration reports success having done nothing.
first_run_sudoers="$sudoers_dir/first-run"
tsui_sudoers="$sudoers_dir/tsui"
fail_privileged_repair() {
echo "Cannot complete the privileged installer-artifact repair. An administrator must run omarchy-migrate to repair this machine." >&2
exit 1
}
# This is a machine-wide repair with per-user migration markers. A root-owned,
# readable marker lets later non-sudo users finish their own migration run after
# one privileged account has inspected and repaired the machine. Until then,
# the migration fails loudly and remains pending. After an administrator repairs
# the machine, this marker lets every other account complete without using sudo.
if ! as_root true 2>/dev/null; then
fail_privileged_repair
fi
# Removing a unit and reloading systemd are one repair. Persist the second half
# before removing the file so a failed daemon-reload cannot be forgotten on a
# retry that now sees no unit on disk.
if [[ -e $reload_needed_marker ]]; then
if ! as_root systemctl daemon-reload >/dev/null 2>&1; then
fail_privileged_repair
fi
if ! as_root rm -f "$reload_needed_marker"; then
fail_privileged_repair
fi
fi
inspect_sudoers_file() {
local file="$1" predicate="$2" kind content
# Emit an explicit state from the elevated process. A bare `sudo test -f` in
# an if-condition makes "file missing" indistinguishable from "sudo failed",
# which could mark a live grant repaired without ever reading it.
if ! kind=$(as_root bash -c 'if [[ -f $1 ]]; then printf file; elif [[ -e $1 ]]; then printf other; else printf missing; fi' bash "$file"); then
fail_privileged_repair
fi
[[ $kind == "file" ]] || return 0
if ! content=$(as_root cat "$file"); then
fail_privileged_repair
fi
if "$predicate" <<<"$content"; then
if ! as_root rm -f "$file"; then
fail_privileged_repair
fi
fi
}
inspect_sudoers_file "$first_run_sudoers" first_run_sudoers_is_generated
inspect_sudoers_file "$tsui_sudoers" tsui_sudoers_is_generated
# /etc/systemd/system is 0755, so this one needs no elevation to look at.
plymouth_unit="$systemd_dir/omarchy-plymouth-shutdown.service"
if [[ -f $plymouth_unit ]] && plymouth_unit_runs_from_home <"$plymouth_unit"; then
# Disable, never stop. Stopping the unit is precisely what runs ExecStop, and
# ExecStop is the path this migration exists to keep root away from; disabling
# only drops the multi-user.target symlink.
if ! as_root install -Dm644 /dev/null "$reload_needed_marker"; then
fail_privileged_repair
fi
if ! as_root systemctl disable omarchy-plymouth-shutdown.service >/dev/null 2>&1; then
fail_privileged_repair
fi
if ! as_root rm -f "$plymouth_unit"; then
fail_privileged_repair
fi
# systemd keeps serving the copy it already loaded until it rereads the
# directory, so without this the unit is still there to run at shutdown.
if ! as_root systemctl daemon-reload >/dev/null 2>&1; then
fail_privileged_repair
fi
if ! as_root rm -f "$reload_needed_marker"; then
fail_privileged_repair
fi
fi
if ! as_root install -Dm644 /dev/null "$machine_marker"; then
fail_privileged_repair
fi
+257
View File
@@ -0,0 +1,257 @@
echo "Repair legacy XCompose and remove vulnerable Omarchy 3 power udev rules"
xcompose="$HOME/.XCompose"
packaged_xcompose="$OMARCHY_PATH/default/xcompose"
legacy_xcompose_pattern='^[[:space:]]*include[[:space:]]+"[^"]*/\.local/share/omarchy/default/xcompose"[[:space:]]*$'
# Omarchy 3 pointed the user's compose file through the checkout compatibility
# link. Preserve their own sequences while moving that include to the packaged
# tree. A failed live restart is harmless: the next graphical login reads the
# repaired file.
if [[ -f $xcompose ]] && grep -Eq "$legacy_xcompose_pattern" "$xcompose"; then
xcompose_replacement=${packaged_xcompose//\\/\\\\}
xcompose_replacement=${xcompose_replacement//&/\\&}
xcompose_replacement=${xcompose_replacement//|/\\|}
sed -i -E "s|^([[:space:]]*include[[:space:]]+\")[^\"]*/\\.local/share/omarchy/default/xcompose\"[[:space:]]*$|\\1$xcompose_replacement\"|" "$xcompose"
omarchy-restart-xcompose >/dev/null 2>&1 || true
fi
rules_dir=/etc/udev/rules.d
reload_marker_prefix=/var/lib/omarchy/migrations/1788102906-udev-reload-needed
udev_control=/run/udev/control
install_command=/usr/bin/install
mv_command=/usr/bin/mv
rm_command=/usr/bin/rm
udevadm_command=/usr/bin/udevadm
as_root() {
if (( EUID == 0 )); then
"$@"
else
sudo "$@"
fi
}
# Omarchy 3 generated these two rules with an unquoted heredoc, so the installing
# user's $HOME was expanded and the file on disk names that absolute home path.
# udev runs RUN+= as root, and
# ~/.local/share/omarchy is a symlink that same unprivileged user owns: replacing
# it with a tree of their own and provoking a power_supply event runs their code
# as root. Quattro ships the rules as 99-omarchy-*.rules under /usr/bin, but the
# one-shot migration that swept the old filenames was itself dropped, so an
# install that came up through the 3.x line keeps the old file until this
# migration removes it.
#
# Pre-4 layout work normally belongs in the Omarchy 4 upgrade command, but that
# command only runs on a machine still making the crossing, so an install that
# crossed already would never see it. The upgrade command ends by running
# omarchy-migrate, so this covers the installs still to upgrade as well.
#
# Only remove an exact two-line body that one of the retired installers wrote.
# A same-named file with no active vulnerable RUN survives; one that still has
# the vulnerable command but also contains administrator changes is quarantined
# under a non-.rules suffix for review.
rule_runs_from_home() {
local file="$1" binary="$2"
local run_pattern='RUN([[:space:]]*\{[^}]*\})?[[:space:]]*(\+|:)?=[[:space:]]*(e)?"([^"]*)"'
local line logical="" rest command
while IFS= read -r line || [[ -n $line ]]; do
# udev tests for a comment before it joins continuations, and skipping one
# does not end a continuation already under way. Both halves verified with
# `udevadm verify`: "# disabled \" followed by a bogus key reports the error
# on line 2, so a comment's own trailing backslash swallows nothing, while
# 'SUBSYSTEM=="power_supply" \' + "# c" + ', RUN+="..."' reports its style
# warning on line 1, so the rule spans the comment. Testing the comment after
# the join would hide a live rule; clearing the pending line here would hide
# one just as well.
if [[ $line =~ ^[[:space:]]*# ]]; then
continue
fi
# A trailing backslash continues the rule on the next line.
if [[ $line == *\\ ]]; then
logical+=${line%\\}
continue
fi
rest=$logical$line
logical=""
while [[ $rest =~ $run_pattern ]]; do
command=${BASH_REMATCH[4]}
rest=${rest#*"${BASH_REMATCH[0]}"}
# This is only the fail-closed detector; wholesale deletion still requires
# an exact historical body below. Match spacing edits and wrapper arguments
# conservatively so a modified active rule is never mistaken for a safe one.
if [[ $command == *"/.local/share/omarchy/bin/$binary"* ]]; then
return 0
fi
done
done <"$file"
return 1
}
mark_reload_needed() {
if ! as_root "$install_command" -Dm644 /dev/null "$reload_needed_marker"; then
echo "Administrator privileges are required to repair the vulnerable legacy udev rule. Ask an administrator to run omarchy-migrate." >&2
exit 1
fi
}
# The retired installers overwrote each file with one of five known two-line
# bodies. Only those exact bodies are safe to delete wholesale. If a vulnerable
# rule has since been edited or extended, preserve it under an inactive name
# rather than taking unrelated rules with it.
rule_is_exact_generated() {
local file="$1" binary="$2"
local prefix suffix home expected
local -a lines
mapfile -t lines <"$file"
(( ${#lines[@]} == 2 )) || return 1
case "$binary" in
omarchy-powerprofiles-set)
# Initial userland helper: separate AC/battery units and arguments.
prefix='SUBSYSTEM=="power_supply", ATTR{type}=="Mains", ATTR{online}=="0", RUN+="/usr/bin/systemd-run --no-block --collect --unit=omarchy-power-profile-battery --property=After=power-profiles-daemon.service '
suffix='/.local/share/omarchy/bin/omarchy-powerprofiles-set battery"'
if [[ ${lines[0]} == "$prefix"*"$suffix" ]]; then
home=${lines[0]#"$prefix"}
home=${home%"$suffix"}
expected='SUBSYSTEM=="power_supply", ATTR{type}=="Mains", ATTR{online}=="1", RUN+="/usr/bin/systemd-run --no-block --collect --unit=omarchy-power-profile-ac --property=After=power-profiles-daemon.service '"$home"'/.local/share/omarchy/bin/omarchy-powerprofiles-set ac"'
[[ $home == /* && $home != *'"'* && ${lines[1]} == "$expected" ]] && return 0
fi
# USB-C support: one fixed transient-unit name and no profile argument.
prefix='SUBSYSTEM=="power_supply", ATTR{type}=="Mains", RUN+="/usr/bin/systemd-run --no-block --collect --unit=omarchy-power-profile --property=After=power-profiles-daemon.service '
suffix='/.local/share/omarchy/bin/omarchy-powerprofiles-set"'
if [[ ${lines[0]} == "$prefix"*"$suffix" ]]; then
home=${lines[0]#"$prefix"}
home=${home%"$suffix"}
expected='SUBSYSTEM=="power_supply", ATTR{type}=="USB", RUN+="/usr/bin/systemd-run --no-block --collect --unit=omarchy-power-profile --property=After=power-profiles-daemon.service '"$home"'/.local/share/omarchy/bin/omarchy-powerprofiles-set"'
[[ $home == /* && $home != *'"'* && ${lines[1]} == "$expected" ]] && return 0
fi
# The final Omarchy 3 revision dropped the fixed transient-unit name.
prefix='SUBSYSTEM=="power_supply", ATTR{type}=="Mains", RUN+="/usr/bin/systemd-run --no-block --collect --property=After=power-profiles-daemon.service '
suffix='/.local/share/omarchy/bin/omarchy-powerprofiles-set"'
if [[ ${lines[0]} == "$prefix"*"$suffix" ]]; then
home=${lines[0]#"$prefix"}
home=${home%"$suffix"}
expected='SUBSYSTEM=="power_supply", ATTR{type}=="USB", RUN+="/usr/bin/systemd-run --no-block --collect --property=After=power-profiles-daemon.service '"$home"'/.local/share/omarchy/bin/omarchy-powerprofiles-set"'
[[ $home == /* && $home != *'"'* && ${lines[1]} == "$expected" ]] && return 0
fi
;;
omarchy-wifi-powersave)
# Initial Wi-Fi helper: invoke the userland command directly.
prefix='SUBSYSTEM=="power_supply", ATTR{type}=="Mains", ATTR{online}=="0", RUN+="'
suffix='/.local/share/omarchy/bin/omarchy-wifi-powersave on"'
if [[ ${lines[0]} == "$prefix"*"$suffix" ]]; then
home=${lines[0]#"$prefix"}
home=${home%"$suffix"}
expected='SUBSYSTEM=="power_supply", ATTR{type}=="Mains", ATTR{online}=="1", RUN+="'"$home"'/.local/share/omarchy/bin/omarchy-wifi-powersave off"'
[[ $home == /* && $home != *'"'* && ${lines[1]} == "$expected" ]] && return 0
fi
# Later revision deferred each change through its own transient unit.
prefix='SUBSYSTEM=="power_supply", ATTR{type}=="Mains", ATTR{online}=="0", RUN+="/usr/bin/systemd-run --no-block --collect --unit=omarchy-wifi-powersave-on '
suffix='/.local/share/omarchy/bin/omarchy-wifi-powersave on"'
if [[ ${lines[0]} == "$prefix"*"$suffix" ]]; then
home=${lines[0]#"$prefix"}
home=${home%"$suffix"}
expected='SUBSYSTEM=="power_supply", ATTR{type}=="Mains", ATTR{online}=="1", RUN+="/usr/bin/systemd-run --no-block --collect --unit=omarchy-wifi-powersave-off '"$home"'/.local/share/omarchy/bin/omarchy-wifi-powersave off"'
[[ $home == /* && $home != *'"'* && ${lines[1]} == "$expected" ]] && return 0
fi
;;
esac
return 1
}
finish_pending_reload() {
# With no control socket there is no running udevd holding the deleted rule;
# the next daemon start reads the directory from disk. If a daemon is running,
# a failed reload must keep this migration pending so the in-memory root rule
# cannot outlive the per-user completion marker.
if [[ -e $udev_control ]] && ! as_root "$udevadm_command" control --reload 2>/dev/null; then
echo "Could not reload udev after removing a vulnerable legacy rule. Ask an administrator to run omarchy-migrate." >&2
exit 1
fi
if ! as_root "$rm_command" -f "$reload_needed_marker"; then
echo "Could not finish the legacy udev-rule repair. Ask an administrator to run omarchy-migrate." >&2
exit 1
fi
}
quarantine_rule() {
local rule_file="$1"
local backup="$rule_file.omarchy-disabled"
local suffix=0
# udev only loads files ending in .rules. Preserve an administrator-modified
# file byte-for-byte under a suffix udev ignores instead of either deleting
# their additions or leaving its user-controlled command active as root.
while [[ -e $backup || -L $backup ]]; do
((++suffix))
backup="$rule_file.omarchy-disabled.$suffix"
done
mark_reload_needed
if ! as_root "$mv_command" --no-clobber -- "$rule_file" "$backup"; then
echo "Administrator privileges are required to quarantine the vulnerable legacy udev rule. Ask an administrator to run omarchy-migrate." >&2
exit 1
fi
if [[ -e $rule_file || -L $rule_file ]]; then
echo "Could not quarantine the vulnerable legacy udev rule at $rule_file. Ask an administrator to run omarchy-migrate." >&2
exit 1
fi
finish_pending_reload
echo "Quarantined the modified legacy rule as $backup so udev cannot execute it. Review the preserved file before restoring any safe custom actions." >&2
}
if [[ -d $rules_dir && ! -x $rules_dir ]]; then
echo "Could not inspect legacy udev rules under $rules_dir. Ask an administrator to run omarchy-migrate." >&2
exit 1
fi
for legacy_rule in "99-power-profile.rules:omarchy-powerprofiles-set" "99-wifi-powersave.rules:omarchy-wifi-powersave"; do
rule_name="${legacy_rule%%:*}"
rule_file="$rules_dir/$rule_name"
binary="${legacy_rule##*:}"
reload_needed_marker="$reload_marker_prefix-$rule_name"
# The marker is written before removal so a crash cannot lose the need to
# reload. Only consume it early when the corresponding active file is already
# gone; another user's concurrent run may still be between those two steps.
if [[ -e $reload_needed_marker && ! -e $rule_file && ! -L $rule_file ]]; then
finish_pending_reload
fi
if [[ -f $rule_file && ! -r $rule_file ]]; then
echo "Could not inspect the legacy udev rule at $rule_file. Ask an administrator to run omarchy-migrate." >&2
exit 1
fi
if [[ -f $rule_file ]] && rule_is_exact_generated "$rule_file" "$binary"; then
mark_reload_needed
if ! as_root "$rm_command" -f "$rule_file"; then
echo "Administrator privileges are required to remove the vulnerable legacy udev rule. Ask an administrator to run omarchy-migrate." >&2
exit 1
fi
finish_pending_reload
elif [[ -f $rule_file ]] && rule_runs_from_home "$rule_file" "$binary"; then
quarantine_rule "$rule_file"
fi
# If an interrupted removal was followed by an administrator installing a
# safe replacement, reload that replacement before clearing the old marker.
if [[ -e $reload_needed_marker ]]; then
finish_pending_reload
fi
done