Merge pull request #10425 from acrogenesis/security/root-owned-sleep-hooks
Harden ownership of installed sleep hooks
This commit is contained in:
8 files changed
+1189
-21
No files matched your search
@@ -30,6 +30,35 @@ MKINITCPIO_CONF="/etc/mkinitcpio.conf.d/omarchy_resume.conf"
|
||||
SWAP_FILE="/swap/swapfile"
|
||||
RESUME_DROP_IN="/etc/limine-entry-tool.d/resume.conf"
|
||||
|
||||
install_root_file() {
|
||||
local source="$1"
|
||||
local destination="$2"
|
||||
local mode="$3"
|
||||
local stage
|
||||
|
||||
stage=$(sudo /usr/bin/mktemp -- "${destination%/*}/.${destination##*/}.omarchy.XXXXXX") || return 1
|
||||
safe_stage_path "$stage" "$destination" || return 1
|
||||
|
||||
if sudo /usr/bin/install -m "$mode" -o root -g root -T "$source" "$stage" &&
|
||||
sudo /usr/bin/mv -Tf -- "$stage" "$destination"; then
|
||||
return 0
|
||||
else
|
||||
safe_stage_path "$stage" "$destination" && sudo /usr/bin/rm -f -- "$stage"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
safe_stage_path() {
|
||||
local stage="$1"
|
||||
local destination="$2"
|
||||
local prefix suffix
|
||||
|
||||
prefix="${destination%/*}/.${destination##*/}.omarchy."
|
||||
[[ $stage == "$prefix"* ]] || return 1
|
||||
suffix=${stage#"$prefix"}
|
||||
[[ $suffix =~ ^[[:alnum:]]{6}$ ]]
|
||||
}
|
||||
|
||||
# Check if hibernation is already configured
|
||||
if [[ -f $MKINITCPIO_CONF ]] && grep -q "^HOOKS+=(resume)$" "$MKINITCPIO_CONF"; then
|
||||
# Fix empty resume_offset if btrfs map-swapfile failed during initial setup
|
||||
@@ -83,14 +112,20 @@ if ! swapon --show | grep -q "$SWAP_FILE"; then
|
||||
sudo swapon -p 0 "$SWAP_FILE"
|
||||
fi
|
||||
|
||||
# Ensure keyboard backlight doesn't prevent sleep
|
||||
# Install this before writing the resume marker so a failed install remains
|
||||
# retryable through the normal setup command.
|
||||
if ! install_root_file "$OMARCHY_PATH/default/systemd/system-sleep/keyboard-backlight" \
|
||||
/usr/lib/systemd/system-sleep/keyboard-backlight 0755; then
|
||||
echo "Could not install the keyboard-backlight system-sleep hook" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Add resume hook to mkinitcpio
|
||||
sudo mkdir -p /etc/mkinitcpio.conf.d
|
||||
echo "Adding resume hook to $MKINITCPIO_CONF"
|
||||
echo "HOOKS+=(resume)" | sudo tee "$MKINITCPIO_CONF" >/dev/null
|
||||
|
||||
# Ensure keyboard backlight doesn't prevent sleep
|
||||
sudo cp -p "$OMARCHY_PATH/default/systemd/system-sleep/keyboard-backlight" /usr/lib/systemd/system-sleep/
|
||||
|
||||
# Add resume= kernel parameters so the initramfs resume hook knows where to find the
|
||||
# hibernation image. Without these, resume happens late (after GPU drivers load) and fails.
|
||||
if [[ ! -f $RESUME_DROP_IN ]]; then
|
||||
|
||||
@@ -3,6 +3,35 @@
|
||||
# omarchy:summary=Toggle dedicated vs integrated GPU mode via supergfxd (for hybrid gpu laptops, like Asus G14).
|
||||
# omarchy:requires-sudo=true
|
||||
|
||||
install_root_file() {
|
||||
local source="$1"
|
||||
local destination="$2"
|
||||
local mode="$3"
|
||||
local stage
|
||||
|
||||
stage=$(sudo /usr/bin/mktemp -- "${destination%/*}/.${destination##*/}.omarchy.XXXXXX") || return 1
|
||||
safe_stage_path "$stage" "$destination" || return 1
|
||||
|
||||
if sudo /usr/bin/install -m "$mode" -o root -g root -T "$source" "$stage" &&
|
||||
sudo /usr/bin/mv -Tf -- "$stage" "$destination"; then
|
||||
return 0
|
||||
else
|
||||
safe_stage_path "$stage" "$destination" && sudo /usr/bin/rm -f -- "$stage"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
safe_stage_path() {
|
||||
local stage="$1"
|
||||
local destination="$2"
|
||||
local prefix suffix
|
||||
|
||||
prefix="${destination%/*}/.${destination##*/}.omarchy."
|
||||
[[ $stage == "$prefix"* ]] || return 1
|
||||
suffix=${stage#"$prefix"}
|
||||
[[ $suffix =~ ^[[:alnum:]]{6}$ ]]
|
||||
}
|
||||
|
||||
if omarchy-cmd-missing supergfxctl; then
|
||||
omarchy-pkg-add supergfxctl
|
||||
|
||||
@@ -54,18 +83,31 @@ case "$gpu_mode" in
|
||||
;;
|
||||
"Hybrid")
|
||||
if gum confirm "Use only integrated GPU and reboot?"; then
|
||||
# Switch to integrated mode and ensure vfio is enabled (needed for sleep/wake trick)
|
||||
sudo sed -i "s/\"mode\": \".*\"/\"mode\": \"Integrated\"/" /etc/supergfxd.conf
|
||||
sudo sed -i 's/"vfio_enable": false/"vfio_enable": true/' /etc/supergfxd.conf
|
||||
|
||||
# Force igpu mode after system sleep (or dgpu could get activated)
|
||||
sudo mkdir -p /usr/lib/systemd/system-sleep
|
||||
sudo cp -p "$OMARCHY_PATH/default/systemd/system-sleep/force-igpu" /usr/lib/systemd/system-sleep/
|
||||
|
||||
# Delay supergfxd startup to avoid race condition with display manager
|
||||
# that can cause system freeze when booting in Integrated mode
|
||||
sudo mkdir -p /etc/systemd/system/supergfxd.service.d
|
||||
sudo cp -p "$OMARCHY_PATH/default/systemd/system/supergfxd.service.d/delay-start.conf" /etc/systemd/system/supergfxd.service.d/
|
||||
if ! install_root_file "$OMARCHY_PATH/default/systemd/system/supergfxd.service.d/delay-start.conf" \
|
||||
/etc/systemd/system/supergfxd.service.d/delay-start.conf 0644; then
|
||||
echo "Could not install the supergfxd startup-delay override" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Publish the self-guarding sleep hook before enabling Integrated mode. It
|
||||
# remains inert while the config says Hybrid, so any failed step is safe to
|
||||
# retry without leaving the GPU config partially switched.
|
||||
sudo mkdir -p /usr/lib/systemd/system-sleep
|
||||
if ! install_root_file "$OMARCHY_PATH/default/systemd/system-sleep/force-igpu" \
|
||||
/usr/lib/systemd/system-sleep/force-igpu 0755; then
|
||||
echo "Could not install the force-igpu system-sleep hook" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Switch both settings in one atomic config rewrite only after every
|
||||
# supporting file has been installed successfully.
|
||||
sudo sed -i \
|
||||
-e 's/"mode": ".*"/"mode": "Integrated"/' \
|
||||
-e 's/"vfio_enable": false/"vfio_enable": true/' \
|
||||
/etc/supergfxd.conf
|
||||
|
||||
omarchy-system-reboot
|
||||
fi
|
||||
|
||||
@@ -1,29 +1,65 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
# Use the Vfio to Integrated trick to turn off NVIDIA dgpu when in integrated mode
|
||||
# without needing to restart the computer. This is needed because computers like the Asus G14
|
||||
# will wake after suspend in Hybrid mode, even if the system was in Integrated mode before
|
||||
# suspending.
|
||||
|
||||
restore_marker=/run/omarchy-force-igpu-integrated
|
||||
sleep_action=${SYSTEMD_SLEEP_ACTION:-$2}
|
||||
[[ -x /usr/bin/supergfxctl ]] || exit 0
|
||||
|
||||
switch_mode() {
|
||||
local expected="$1" current
|
||||
|
||||
if ! /usr/bin/timeout --kill-after=1s 3s /usr/bin/supergfxctl -m "$expected"; then
|
||||
echo "Could not request the GPU transition to $expected mode" >&2
|
||||
return 1
|
||||
fi
|
||||
for _ in {1..10}; do
|
||||
if current=$(/usr/bin/timeout --kill-after=1s 2s /usr/bin/supergfxctl -g 2>/dev/null) &&
|
||||
[[ $current == "$expected" ]]; then
|
||||
return 0
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
echo "Could not confirm the GPU transition to $expected mode" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
pre)
|
||||
# Remember the mode this sleep cycle started in. supergfxctl persists the
|
||||
# temporary hibernate switch to Vfio, so post must not consult that mutable
|
||||
# value when deciding whether to restore Integrated mode.
|
||||
if [[ -L $restore_marker ]]; then
|
||||
exit 1
|
||||
elif [[ ! -f $restore_marker ]]; then
|
||||
/usr/bin/grep -Eq '"mode"[[:space:]]*:[[:space:]]*"Integrated"' /etc/supergfxd.conf 2>/dev/null || exit 0
|
||||
/usr/bin/install -m 0600 -o root -g root -T /dev/null "$restore_marker"
|
||||
fi
|
||||
|
||||
# Before hibernating, switch to Vfio so the nvidia driver is detached from the dGPU.
|
||||
# Without this, hibernate resume fails because the nvidia driver can't freeze a
|
||||
# powered-off dGPU (returns -EIO), which aborts the entire resume.
|
||||
if [[ $2 == "hibernate" ]]; then
|
||||
/usr/bin/supergfxctl -m Vfio
|
||||
sleep 1
|
||||
if [[ $sleep_action == "hibernate" ]]; then
|
||||
switch_mode Vfio
|
||||
fi
|
||||
;;
|
||||
post)
|
||||
[[ -f $restore_marker && ! -L $restore_marker ]] || exit 0
|
||||
|
||||
# small delay so the device is fully re-enumerated
|
||||
sleep 4
|
||||
|
||||
# force-bind dGPU to vfio (fully detached from nvidia)
|
||||
/usr/bin/supergfxctl -m Vfio
|
||||
sleep 1
|
||||
switch_mode Vfio
|
||||
|
||||
# then go back to Integrated, which powers it off again
|
||||
/usr/bin/supergfxctl -m Integrated
|
||||
switch_mode Integrated
|
||||
/usr/bin/rm -f -- "$restore_marker"
|
||||
;;
|
||||
esac
|
||||
@@ -3,7 +3,9 @@
|
||||
# Turn off keyboard backlight before hibernate to prevent hang on power-off.
|
||||
# The ASUS keyboard controller can block S4 shutdown if LEDs are active.
|
||||
|
||||
if [[ $1 == "pre" && $2 == "hibernate" ]]; then
|
||||
sleep_action=${SYSTEMD_SLEEP_ACTION:-$2}
|
||||
|
||||
if [[ $1 == "pre" && $sleep_action == "hibernate" ]]; then
|
||||
device=""
|
||||
for candidate in /sys/class/leds/*kbd_backlight*; do
|
||||
if [[ -e "$candidate" ]]; then
|
||||
|
||||
+3
-2
@@ -124,8 +124,7 @@ default/** ──► omarchy-settings /usr/share/omarchy
|
||||
├─ applications/mimeapps.list /usr/share/applications/mimeapps.list
|
||||
├─ systemd/user/*.service /usr/lib/systemd/user/
|
||||
├─ systemd/user/app.slice.d/10-oomd.conf /usr/lib/systemd/user/app.slice.d/
|
||||
├─ systemd/system-sleep/{force-igpu,
|
||||
│ keyboard-backlight,unmount-fuse} /usr/lib/systemd/system-sleep/
|
||||
├─ systemd/system-sleep/unmount-fuse /usr/lib/systemd/system-sleep/
|
||||
├─ systemd/zram-generator.conf.d/90-omarchy.conf /usr/lib/systemd/zram-generator.conf.d/
|
||||
├─ fonts/omarchy/omarchy.ttf /usr/share/fonts/omarchy/
|
||||
├─ sddm/omarchy/ /usr/share/sddm/themes/omarchy/
|
||||
@@ -139,6 +138,8 @@ logo.{txt,svg}, icon.{txt,png} ──► omarchy-settings /usr/share/omarchy
|
||||
/etc/skel/.config/omarchy/branding/{about,screensaver}.txt
|
||||
```
|
||||
|
||||
The hardware-conditional `force-igpu` and `keyboard-backlight` sources also live under `default/systemd/system-sleep/`, but their setup commands publish root-owned copies only on machines that need them; they are not installed by `omarchy-settings`.
|
||||
|
||||
### Why `etc-overrides/` exists
|
||||
|
||||
Some files under `/etc/` (`.bashrc` in `/etc/skel`, `nsswitch.conf`,
|
||||
|
||||
@@ -0,0 +1,303 @@
|
||||
echo "Repair user-owned system-sleep hooks and hybrid GPU service configuration"
|
||||
|
||||
system_sleep_dir=/usr/lib/systemd/system-sleep
|
||||
supergfxd_drop_in=/etc/systemd/system/supergfxd.service.d/delay-start.conf
|
||||
quarantine_root=/var/lib/omarchy/migrations/1788662350-system-sleep
|
||||
reload_needed_marker=/var/lib/omarchy/migrations/1788662350-systemd-reload-needed
|
||||
keyboard_source="$OMARCHY_PATH/default/systemd/system-sleep/keyboard-backlight"
|
||||
force_igpu_source="$OMARCHY_PATH/default/systemd/system-sleep/force-igpu"
|
||||
supergfxd_source="$OMARCHY_PATH/default/systemd/system/supergfxd.service.d/delay-start.conf"
|
||||
legacy_keyboard_sha256=f313a81e47401f0d38b8602e5997f52c5286d5e97f74027564ddd515b3d16511
|
||||
legacy_force_igpu_sha256=d604e7c4903829563e45fc52188fc5602c3f1bc66e247f0a2cc0a974ed6e57db
|
||||
|
||||
as_root() {
|
||||
if (( EUID == 0 )); then
|
||||
"$@"
|
||||
else
|
||||
sudo "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
path_is_root_controlled() {
|
||||
local path="$1"
|
||||
local current=/ component candidate file_mode link metadata part status uid gid mode
|
||||
local missing_depth=0 symlink_count=0
|
||||
local -a pending resolved link_components
|
||||
|
||||
[[ $path == /* ]] || return 1
|
||||
IFS=/ read -r -a pending <<<"$path"
|
||||
# A non-root group is harmless when neither it nor everyone else can write.
|
||||
# Resolve symlinks component by component so an indirect link cannot hide an
|
||||
# intermediate directory controlled by an unprivileged user.
|
||||
metadata=$(path_metadata /) || return 1
|
||||
read -r file_mode uid gid mode <<<"$metadata"
|
||||
(( uid == 0 && (8#$mode & 8#022) == 0 )) || return 1
|
||||
|
||||
while ((${#pending[@]})); do
|
||||
component=${pending[0]}
|
||||
pending=("${pending[@]:1}")
|
||||
[[ -n $component ]] || continue
|
||||
[[ $component == "." ]] && continue
|
||||
|
||||
if [[ $component == ".." ]]; then
|
||||
if ((${#resolved[@]})); then
|
||||
unset 'resolved[-1]'
|
||||
fi
|
||||
|
||||
current=/
|
||||
for part in "${resolved[@]}"; do
|
||||
if [[ $current == "/" ]]; then
|
||||
current="/$part"
|
||||
else
|
||||
current="$current/$part"
|
||||
fi
|
||||
done
|
||||
if (( missing_depth > 0 && ${#resolved[@]} < missing_depth )); then
|
||||
missing_depth=0
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
|
||||
if [[ $current == "/" ]]; then
|
||||
candidate="/$component"
|
||||
else
|
||||
candidate="$current/$component"
|
||||
fi
|
||||
|
||||
if (( missing_depth > 0 )); then
|
||||
# The first missing component makes descendants inactive today, but keep
|
||||
# consuming the lexical suffix. A later .. can escape back into an
|
||||
# existing user-controlled path that would become active if an
|
||||
# administrator creates the missing directory.
|
||||
resolved+=("$component")
|
||||
current=$candidate
|
||||
continue
|
||||
elif metadata=$(path_metadata "$candidate"); then
|
||||
read -r file_mode uid gid mode <<<"$metadata"
|
||||
else
|
||||
status=$?
|
||||
if (( status == 2 )); then
|
||||
resolved+=("$component")
|
||||
current=$candidate
|
||||
missing_depth=${#resolved[@]}
|
||||
continue
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
if (( (16#$file_mode & 16#f000) == 16#a000 )); then
|
||||
((++symlink_count <= 40)) || return 1
|
||||
link=$(readlink_with_privilege "$candidate") || return 1
|
||||
IFS=/ read -r -a link_components <<<"$link"
|
||||
pending=("${link_components[@]}" "${pending[@]}")
|
||||
if [[ $link == /* ]]; then
|
||||
resolved=()
|
||||
current=/
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
|
||||
(( uid == 0 && (8#$mode & 8#022) == 0 )) || return 1
|
||||
resolved+=("$component")
|
||||
current=$candidate
|
||||
done
|
||||
}
|
||||
|
||||
path_metadata() {
|
||||
local path="$1"
|
||||
local metadata parent
|
||||
|
||||
if /usr/bin/stat -c '%f %u %g %a' -- "$path" 2>/dev/null; then
|
||||
return 0
|
||||
elif [[ ! -e $path && ! -L $path ]]; then
|
||||
parent=${path%/*}
|
||||
[[ -n $parent ]] || parent=/
|
||||
# Avoid asking for sudo for ordinary ENOENT. If the parent is searchable,
|
||||
# the absence is conclusive; an inaccessible root-only chain still needs a
|
||||
# privileged metadata check so safe administrator symlinks are preserved.
|
||||
[[ -x $parent ]] && return 2
|
||||
if metadata=$(as_root /usr/bin/stat -c '%f %u %g %a' -- "$path" 2>/dev/null); then
|
||||
printf '%s\n' "$metadata"
|
||||
return 0
|
||||
elif as_root /usr/bin/test -x "$parent"; then
|
||||
# The privileged probe could search the protected parent, so stat's
|
||||
# failure identifies a target that does not exist yet.
|
||||
return 2
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
as_root /usr/bin/stat -c '%f %u %g %a' -- "$path"
|
||||
fi
|
||||
}
|
||||
|
||||
readlink_with_privilege() {
|
||||
local path="$1"
|
||||
|
||||
if /usr/bin/readlink -- "$path" 2>/dev/null; then
|
||||
return 0
|
||||
else
|
||||
as_root /usr/bin/readlink -- "$path"
|
||||
fi
|
||||
}
|
||||
|
||||
privileged_entry_is_safe() {
|
||||
local path="$1"
|
||||
|
||||
path_is_root_controlled "$path"
|
||||
}
|
||||
|
||||
file_matches_source() {
|
||||
local source="$1"
|
||||
local destination="$2"
|
||||
|
||||
[[ -f $destination && ! -L $destination ]] || return 1
|
||||
|
||||
if [[ -r $destination ]]; then
|
||||
/usr/bin/cmp -s -- "$source" "$destination"
|
||||
else
|
||||
as_root /usr/bin/cmp -s -- "$source" "$destination"
|
||||
fi
|
||||
}
|
||||
|
||||
file_matches_sha256() {
|
||||
local destination="$1"
|
||||
local expected="$2"
|
||||
local digest
|
||||
|
||||
[[ -f $destination && ! -L $destination ]] || return 1
|
||||
if [[ -r $destination ]]; then
|
||||
digest=$(/usr/bin/sha256sum -- "$destination") || return 1
|
||||
else
|
||||
digest=$(as_root /usr/bin/sha256sum -- "$destination") || return 1
|
||||
fi
|
||||
[[ ${digest%% *} == "$expected" ]]
|
||||
}
|
||||
|
||||
safe_stage_path() {
|
||||
local stage="$1"
|
||||
local destination="$2"
|
||||
local prefix suffix
|
||||
|
||||
prefix="${destination%/*}/.${destination##*/}.omarchy."
|
||||
[[ $stage == "$prefix"* ]] || return 1
|
||||
suffix=${stage#"$prefix"}
|
||||
[[ $suffix =~ ^[[:alnum:]]{6}$ ]]
|
||||
}
|
||||
|
||||
install_root_file() {
|
||||
local source="$1"
|
||||
local destination="$2"
|
||||
local mode="$3"
|
||||
local stage
|
||||
|
||||
stage=$(as_root /usr/bin/mktemp -- "${destination%/*}/.${destination##*/}.omarchy.XXXXXX") || return 1
|
||||
safe_stage_path "$stage" "$destination" || return 1
|
||||
|
||||
if as_root /usr/bin/install -m "$mode" -o root -g root -T "$source" "$stage" &&
|
||||
as_root /usr/bin/mv -Tf -- "$stage" "$destination"; then
|
||||
return 0
|
||||
else
|
||||
safe_stage_path "$stage" "$destination" && as_root /usr/bin/rm -f -- "$stage"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
preserve_unsafe_customization() {
|
||||
local path="$1"
|
||||
local label="$2"
|
||||
local backup_dir backup
|
||||
|
||||
if ! as_root /usr/bin/install -d -m 0700 -o root -g root "$quarantine_root"; then
|
||||
echo "Could not create the root-only system-sleep quarantine at $quarantine_root" >&2
|
||||
return 1
|
||||
fi
|
||||
if ! backup_dir=$(as_root /usr/bin/mktemp -d -- "$quarantine_root/${label}.XXXXXX"); then
|
||||
echo "Could not reserve a quarantine path for $path" >&2
|
||||
return 1
|
||||
fi
|
||||
backup="$backup_dir/original"
|
||||
|
||||
if as_root /usr/bin/cp -a --no-dereference -T -- "$path" "$backup"; then
|
||||
printf '%s\n' "$backup"
|
||||
else
|
||||
as_root /usr/bin/rm -rf -- "$backup_dir"
|
||||
echo "Could not preserve unsafe custom content from $path before repairing it" >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
repair_unsafe_privileged_entry() {
|
||||
local source="$1"
|
||||
local destination="$2"
|
||||
local mode="$3"
|
||||
local label="$4"
|
||||
local legacy_sha256="${5:-}"
|
||||
local backup current_mode
|
||||
|
||||
[[ -e $destination || -L $destination ]] || return 0
|
||||
[[ -f $destination || -L $destination ]] || return 0
|
||||
|
||||
if file_matches_source "$source" "$destination"; then
|
||||
current_mode=$(/usr/bin/stat -c '%a' -- "$destination" 2>/dev/null) ||
|
||||
current_mode=$(as_root /usr/bin/stat -c '%a' -- "$destination") || return 1
|
||||
if privileged_entry_is_safe "$destination" && [[ $current_mode == "${mode#0}" ]]; then
|
||||
return 0
|
||||
fi
|
||||
elif [[ -n $legacy_sha256 ]] && file_matches_sha256 "$destination" "$legacy_sha256"; then
|
||||
:
|
||||
else
|
||||
privileged_entry_is_safe "$destination" && return 0
|
||||
backup=$(preserve_unsafe_customization "$destination" "$label") || return 1
|
||||
fi
|
||||
|
||||
if install_root_file "$source" "$destination" "$mode"; then
|
||||
if [[ -n ${backup:-} ]]; then
|
||||
echo "Preserved unsafe custom content from $destination at $backup for administrator review" >&2
|
||||
fi
|
||||
else
|
||||
if [[ -n ${backup:-} ]]; then
|
||||
echo "Preserved unsafe custom content from $destination at $backup, but could not repair the active path" >&2
|
||||
fi
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Replace rather than chown an unsafe destination: its current owner may have
|
||||
# already changed the contents or kept a writable file descriptor open. The
|
||||
# root-owned staging inode makes the final rename an atomic trust transition.
|
||||
repair_unsafe_privileged_entry "$keyboard_source" \
|
||||
"$system_sleep_dir/keyboard-backlight" 0755 keyboard-backlight "$legacy_keyboard_sha256"
|
||||
|
||||
force_igpu="$system_sleep_dir/force-igpu"
|
||||
repair_unsafe_privileged_entry "$force_igpu_source" "$force_igpu" 0755 force-igpu "$legacy_force_igpu_sha256"
|
||||
|
||||
systemd_reload_needed=false
|
||||
if [[ -e $reload_needed_marker || -L $reload_needed_marker ]]; then
|
||||
systemd_reload_needed=true
|
||||
fi
|
||||
|
||||
if [[ -e $supergfxd_drop_in || -L $supergfxd_drop_in ]]; then
|
||||
if ! privileged_entry_is_safe "$supergfxd_drop_in"; then
|
||||
# Replacing the drop-in and reloading systemd are one repair. Record the
|
||||
# second half before changing the file so failure or interruption cannot
|
||||
# be forgotten when a retry sees only the trusted replacement on disk.
|
||||
if ! as_root /usr/bin/install -Dm0644 -o root -g root /dev/null "$reload_needed_marker"; then
|
||||
echo "Could not persist the pending systemd reload for the repaired supergfxd configuration" >&2
|
||||
exit 1
|
||||
fi
|
||||
systemd_reload_needed=true
|
||||
repair_unsafe_privileged_entry "$supergfxd_source" "$supergfxd_drop_in" 0644 delay-start.conf
|
||||
fi
|
||||
fi
|
||||
|
||||
if $systemd_reload_needed; then
|
||||
if ! as_root /usr/bin/systemctl daemon-reload; then
|
||||
echo "Could not reload systemd after repairing the supergfxd configuration; the migration will retry" >&2
|
||||
exit 1
|
||||
fi
|
||||
if ! as_root /usr/bin/rm -f -- "$reload_needed_marker"; then
|
||||
echo "Could not clear the pending systemd reload marker; the migration will retry" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
@@ -0,0 +1,705 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
source "$(dirname "$0")/base-test.sh"
|
||||
|
||||
migration="$ROOT/migrations/1788662350.sh"
|
||||
test_tmp=$(mktemp -d -p /tmp)
|
||||
trap 'rm -rf "$test_tmp"' EXIT
|
||||
|
||||
mock_omarchy="$test_tmp/omarchy"
|
||||
sleep_dir="$test_tmp/system-sleep"
|
||||
systemd_dir="$test_tmp/systemd"
|
||||
drop_in="$systemd_dir/supergfxd.service.d/delay-start.conf"
|
||||
quarantine="$test_tmp/quarantine"
|
||||
reload_needed_marker="$test_tmp/reload-needed"
|
||||
migration_copy="$test_tmp/migration.sh"
|
||||
stub_bin="$test_tmp/bin"
|
||||
calls="$test_tmp/calls"
|
||||
|
||||
mkdir -p "$mock_omarchy/default/systemd/system-sleep" \
|
||||
"$mock_omarchy/default/systemd/system/supergfxd.service.d" \
|
||||
"$sleep_dir" "${drop_in%/*}" "$stub_bin"
|
||||
cp "$ROOT/default/systemd/system-sleep/keyboard-backlight" \
|
||||
"$mock_omarchy/default/systemd/system-sleep/keyboard-backlight"
|
||||
cp "$ROOT/default/systemd/system-sleep/force-igpu" \
|
||||
"$mock_omarchy/default/systemd/system-sleep/force-igpu"
|
||||
cp "$ROOT/default/systemd/system/supergfxd.service.d/delay-start.conf" \
|
||||
"$mock_omarchy/default/systemd/system/supergfxd.service.d/delay-start.conf"
|
||||
|
||||
[[ $(grep -Fxc 'system_sleep_dir=/usr/lib/systemd/system-sleep' "$migration") == 1 ]] ||
|
||||
fail "migration fixes one literal system-sleep directory"
|
||||
[[ $(grep -Fxc 'supergfxd_drop_in=/etc/systemd/system/supergfxd.service.d/delay-start.conf' "$migration") == 1 ]] ||
|
||||
fail "migration fixes one literal supergfxd drop-in"
|
||||
|
||||
sed \
|
||||
-e "s|system_sleep_dir=/usr/lib/systemd/system-sleep|system_sleep_dir=$sleep_dir|" \
|
||||
-e "s|supergfxd_drop_in=/etc/systemd/system/supergfxd.service.d/delay-start.conf|supergfxd_drop_in=$drop_in|" \
|
||||
-e "s|quarantine_root=/var/lib/omarchy/migrations/1788662350-system-sleep|quarantine_root=$quarantine|" \
|
||||
-e "s|/var/lib/omarchy/migrations/1788662350-systemd-reload-needed|$reload_needed_marker|" \
|
||||
-e "s|/usr/bin/stat|$stub_bin/stat|g" \
|
||||
-e "s|/usr/bin/readlink|$stub_bin/readlink|g" \
|
||||
"$migration" >"$migration_copy"
|
||||
|
||||
cat >"$stub_bin/stat" <<'SH'
|
||||
#!/bin/bash
|
||||
|
||||
path=${!#}
|
||||
if [[ :${INACCESSIBLE_AS_USER:-}: == *":$path:"* && ${FAKE_SUDO:-0} == 0 ]]; then
|
||||
exit 13
|
||||
fi
|
||||
|
||||
actual_file_mode=$(/usr/bin/stat -c '%f' -- "$path") || exit 1
|
||||
actual_mode=$(/usr/bin/stat -c '%a' -- "$path") || exit 1
|
||||
|
||||
if [[ :${FAKE_ROOT_DIRS:-}: == *":$path:"* ]]; then
|
||||
uid=0
|
||||
gid=0
|
||||
mode=$(printf '%o' "$((8#$actual_mode & ~8#022))")
|
||||
elif [[ :${FAKE_ROOT_FILES:-}: == *":$path:"* ]]; then
|
||||
uid=0
|
||||
gid=${FAKE_ROOT_GID:-0}
|
||||
mode=${FAKE_ROOT_MODE:-$actual_mode}
|
||||
else
|
||||
exec /usr/bin/stat "$@"
|
||||
fi
|
||||
|
||||
file_type=$((16#$actual_file_mode & 16#f000))
|
||||
file_mode=$(printf '%x' "$((file_type | 8#$mode))")
|
||||
|
||||
case "$*" in
|
||||
*"%f %u %g %a"*) printf '%s %s %s %s\n' "$file_mode" "$uid" "$gid" "$mode" ;;
|
||||
*"%u %g %a"*) printf '%s %s %s\n' "$uid" "$gid" "$mode" ;;
|
||||
*"%a"*) printf '%s\n' "$mode" ;;
|
||||
*) exec /usr/bin/stat "$@" ;;
|
||||
esac
|
||||
SH
|
||||
|
||||
cat >"$stub_bin/readlink" <<'SH'
|
||||
#!/bin/bash
|
||||
|
||||
path=${!#}
|
||||
if [[ :${INACCESSIBLE_AS_USER:-}: == *":$path:"* && ${FAKE_SUDO:-0} == 0 ]]; then
|
||||
exit 13
|
||||
fi
|
||||
|
||||
exec /usr/bin/readlink "$@"
|
||||
SH
|
||||
chmod +x "$stub_bin/stat" "$stub_bin/readlink"
|
||||
|
||||
cat >"$stub_bin/sudo" <<'SH'
|
||||
#!/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
printf 'sudo' >>"$CALLS"
|
||||
printf '\t%s' "$@" >>"$CALLS"
|
||||
printf '\n' >>"$CALLS"
|
||||
|
||||
case "$1" in
|
||||
*/stat | */readlink)
|
||||
FAKE_SUDO=1 exec "$@"
|
||||
;;
|
||||
/usr/bin/test)
|
||||
shift
|
||||
if [[ $1 == "-x" && :${FAKE_ROOT_DIRS:-}: == *":$2:"* ]]; then
|
||||
exit 0
|
||||
else
|
||||
exec /usr/bin/test "$@"
|
||||
fi
|
||||
;;
|
||||
/usr/bin/mktemp | /usr/bin/mv | /usr/bin/chmod | /usr/bin/cp | /usr/bin/rm)
|
||||
exec "$@"
|
||||
;;
|
||||
/usr/bin/systemctl)
|
||||
if [[ -n ${SYSTEMCTL_FAIL_ONCE_FILE:-} && -e $SYSTEMCTL_FAIL_ONCE_FILE ]]; then
|
||||
/usr/bin/rm -f -- "$SYSTEMCTL_FAIL_ONCE_FILE"
|
||||
exit 1
|
||||
fi
|
||||
exit 0
|
||||
;;
|
||||
/usr/bin/install)
|
||||
shift
|
||||
args=()
|
||||
while (($#)); do
|
||||
case "$1" in
|
||||
-o | -g)
|
||||
shift 2
|
||||
;;
|
||||
*)
|
||||
args+=("$1")
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
exec /usr/bin/install "${args[@]}"
|
||||
;;
|
||||
*)
|
||||
printf 'unexpected sudo command: %s\n' "$*" >&2
|
||||
exit 97
|
||||
;;
|
||||
esac
|
||||
SH
|
||||
chmod +x "$stub_bin/sudo"
|
||||
|
||||
run_migration() {
|
||||
local fake_root_dirs
|
||||
|
||||
: >"$calls"
|
||||
fake_root_dirs="/:/tmp:$test_tmp:$sleep_dir:$systemd_dir:${drop_in%/*}"
|
||||
[[ -z ${EXTRA_FAKE_ROOT_DIRS:-} ]] || fake_root_dirs+=":$EXTRA_FAKE_ROOT_DIRS"
|
||||
|
||||
CALLS="$calls" \
|
||||
FAKE_ROOT_DIRS="$fake_root_dirs" \
|
||||
FAKE_ROOT_FILES="${FAKE_ROOT_FILES:-${2:-}}" \
|
||||
FAKE_ROOT_MODE="${FAKE_ROOT_MODE:-${3:-}}" \
|
||||
FAKE_ROOT_GID="${FAKE_ROOT_GID:-0}" \
|
||||
INACCESSIBLE_AS_USER="${INACCESSIBLE_AS_USER:-}" \
|
||||
SYSTEMCTL_FAIL_ONCE_FILE="${SYSTEMCTL_FAIL_ONCE_FILE:-}" \
|
||||
OMARCHY_PATH="$mock_omarchy" \
|
||||
PATH="$stub_bin:$PATH" bash -euo pipefail "$migration_copy" >/dev/null
|
||||
}
|
||||
|
||||
printf 'attacker keyboard\n' >"$sleep_dir/keyboard-backlight"
|
||||
printf 'attacker gpu\n' >"$sleep_dir/force-igpu"
|
||||
printf 'attacker drop-in\n' >"$drop_in"
|
||||
chmod 0777 "$sleep_dir/keyboard-backlight" "$sleep_dir/force-igpu"
|
||||
chmod 0666 "$drop_in"
|
||||
exec 9>>"$sleep_dir/keyboard-backlight"
|
||||
|
||||
run_migration Integrated
|
||||
printf 'write through stale attacker descriptor\n' >&9
|
||||
exec 9>&-
|
||||
|
||||
cmp -s "$mock_omarchy/default/systemd/system-sleep/keyboard-backlight" "$sleep_dir/keyboard-backlight" ||
|
||||
fail "migration replaces the user-owned keyboard hook with trusted content"
|
||||
cmp -s "$mock_omarchy/default/systemd/system-sleep/force-igpu" "$sleep_dir/force-igpu" ||
|
||||
fail "migration replaces the user-owned GPU hook with trusted content"
|
||||
cmp -s "$mock_omarchy/default/systemd/system/supergfxd.service.d/delay-start.conf" "$drop_in" ||
|
||||
fail "migration replaces the user-owned root service drop-in with trusted content"
|
||||
[[ $(stat -c '%a' "$sleep_dir/keyboard-backlight") == 755 ]] ||
|
||||
fail "migration activates the repaired keyboard hook"
|
||||
[[ $(stat -c '%a' "$sleep_dir/force-igpu") == 755 ]] ||
|
||||
fail "migration activates force-igpu only in Integrated mode"
|
||||
[[ $(stat -c '%a' "$drop_in") == 644 ]] ||
|
||||
fail "migration installs the service drop-in as configuration"
|
||||
grep -Fx $'sudo\t/usr/bin/systemctl\tdaemon-reload' "$calls" >/dev/null ||
|
||||
fail "migration reloads systemd after repairing its root service drop-in"
|
||||
[[ ! -e $reload_needed_marker ]] ||
|
||||
fail "migration leaves a reload marker after systemd accepted the repaired drop-in"
|
||||
[[ $(stat -c '%a' "$quarantine") == 700 ]] ||
|
||||
fail "migration keeps preserved unsafe custom content in a root-only directory"
|
||||
keyboard_backup=$(find "$quarantine" -path '*/keyboard-backlight.*/original' -type f -print -quit)
|
||||
force_backup=$(find "$quarantine" -path '*/force-igpu.*/original' -type f -print -quit)
|
||||
drop_in_backup=$(find "$quarantine" -path '*/delay-start.conf.*/original' -type f -print -quit)
|
||||
grep -Fxq 'attacker keyboard' "$keyboard_backup" ||
|
||||
fail "migration preserves unknown keyboard-hook content before replacing it"
|
||||
grep -Fxq 'attacker gpu' "$force_backup" ||
|
||||
fail "migration preserves unknown force-iGPU content before replacing it"
|
||||
grep -Fxq 'attacker drop-in' "$drop_in_backup" ||
|
||||
fail "migration preserves unknown service-drop-in content before replacing it"
|
||||
pass "migration replaces writable privileged files with trusted root-owned copies"
|
||||
|
||||
backup_count=$(find "$quarantine" -mindepth 2 -maxdepth 2 -name original | wc -l)
|
||||
FAKE_ROOT_FILES="$sleep_dir/keyboard-backlight:$sleep_dir/force-igpu:$drop_in" \
|
||||
run_migration Integrated
|
||||
[[ ! -s $calls ]] ||
|
||||
fail "migration changes already-repaired privileged files on a second run" "$(<"$calls")"
|
||||
[[ $(find "$quarantine" -mindepth 2 -maxdepth 2 -name original | wc -l) == "$backup_count" ]] ||
|
||||
fail "migration creates duplicate quarantines on a second run"
|
||||
pass "migration is idempotent after repairing unsafe privileged files"
|
||||
|
||||
printf 'attacker drop-in\n' >"$drop_in"
|
||||
chmod 0666 "$drop_in"
|
||||
reload_failure="$test_tmp/fail-systemd-reload-once"
|
||||
touch "$reload_failure"
|
||||
set +e
|
||||
SYSTEMCTL_FAIL_ONCE_FILE="$reload_failure" \
|
||||
FAKE_ROOT_FILES="$sleep_dir/keyboard-backlight:$sleep_dir/force-igpu" \
|
||||
run_migration Integrated
|
||||
reload_status=$?
|
||||
set -e
|
||||
(( reload_status != 0 )) ||
|
||||
fail "migration reports success after systemd rejects the repaired drop-in"
|
||||
cmp -s "$mock_omarchy/default/systemd/system/supergfxd.service.d/delay-start.conf" "$drop_in" ||
|
||||
fail "migration does not repair the drop-in before the simulated reload failure"
|
||||
[[ -e $reload_needed_marker && $(stat -c '%a' "$reload_needed_marker") == 644 ]] ||
|
||||
fail "migration does not persist the reload requirement before replacing the drop-in"
|
||||
|
||||
FAKE_ROOT_FILES="$sleep_dir/keyboard-backlight:$sleep_dir/force-igpu:$drop_in" \
|
||||
run_migration Integrated
|
||||
grep -Fx $'sudo\t/usr/bin/systemctl\tdaemon-reload' "$calls" >/dev/null ||
|
||||
fail "migration does not retry a failed reload after the drop-in is already safe"
|
||||
[[ ! -e $reload_needed_marker ]] ||
|
||||
fail "migration does not clear the reload requirement after a successful retry"
|
||||
|
||||
FAKE_ROOT_FILES="$sleep_dir/keyboard-backlight:$sleep_dir/force-igpu:$drop_in" \
|
||||
run_migration Integrated
|
||||
[[ ! -s $calls ]] ||
|
||||
fail "migration repeats a successfully completed reload repair" "$(<"$calls")"
|
||||
pass "migration persists and retries systemd reload after failure or interruption"
|
||||
|
||||
keyboard_backup_count=$(find "$quarantine" -path '*/keyboard-backlight.*/original' | wc -l)
|
||||
cp "$mock_omarchy/default/systemd/system-sleep/keyboard-backlight" \
|
||||
"$sleep_dir/keyboard-backlight"
|
||||
chmod 0644 "$sleep_dir/keyboard-backlight"
|
||||
FAKE_ROOT_FILES="$sleep_dir/force-igpu:$drop_in" run_migration Integrated
|
||||
[[ $(stat -c '%a' "$sleep_dir/keyboard-backlight") == 755 ]] ||
|
||||
fail "migration does not safely activate a user-owned canonical hook"
|
||||
[[ $(find "$quarantine" -path '*/keyboard-backlight.*/original' | wc -l) == "$keyboard_backup_count" ]] ||
|
||||
fail "migration quarantines an exact legacy artifact as administrator content"
|
||||
pass "migration replaces exact vulnerable installer artifacts without inventing backups"
|
||||
|
||||
legacy_keyboard="$test_tmp/legacy-keyboard-backlight"
|
||||
cat >"$legacy_keyboard" <<'SH'
|
||||
#!/bin/bash
|
||||
|
||||
# Turn off keyboard backlight before hibernate to prevent hang on power-off.
|
||||
# The ASUS keyboard controller can block S4 shutdown if LEDs are active.
|
||||
|
||||
if [[ $1 == "pre" && $2 == "hibernate" ]]; then
|
||||
device=""
|
||||
for candidate in /sys/class/leds/*kbd_backlight*; do
|
||||
if [[ -e "$candidate" ]]; then
|
||||
device="$(basename "$candidate")"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ -n "$device" ]]; then
|
||||
brightnessctl -d "$device" set 0 >/dev/null 2>&1
|
||||
fi
|
||||
fi
|
||||
SH
|
||||
[[ $(sha256sum "$legacy_keyboard" | cut -d' ' -f1) == f313a81e47401f0d38b8602e5997f52c5286d5e97f74027564ddd515b3d16511 ]] ||
|
||||
fail "keyboard-backlight legacy fixture no longer matches the migration fingerprint"
|
||||
keyboard_backup_count=$(find "$quarantine" -path '*/keyboard-backlight.*/original' | wc -l)
|
||||
cp "$legacy_keyboard" "$sleep_dir/keyboard-backlight"
|
||||
chmod 0644 "$sleep_dir/keyboard-backlight"
|
||||
FAKE_ROOT_FILES="$sleep_dir/keyboard-backlight:$sleep_dir/force-igpu:$drop_in" \
|
||||
run_migration Integrated
|
||||
cmp -s "$mock_omarchy/default/systemd/system-sleep/keyboard-backlight" "$sleep_dir/keyboard-backlight" ||
|
||||
fail "migration does not upgrade the released keyboard-backlight hook"
|
||||
[[ $(stat -c '%a' "$sleep_dir/keyboard-backlight") == 755 ]] ||
|
||||
fail "migration leaves the released keyboard-backlight hook non-executable"
|
||||
[[ $(find "$quarantine" -path '*/keyboard-backlight.*/original' | wc -l) == "$keyboard_backup_count" ]] ||
|
||||
fail "migration quarantines the released keyboard hook as administrator content"
|
||||
pass "migration activates the released root-owned keyboard-backlight hook"
|
||||
|
||||
cp "$legacy_keyboard" "$sleep_dir/keyboard-backlight"
|
||||
chmod 0755 "$sleep_dir/keyboard-backlight"
|
||||
FAKE_ROOT_FILES="$sleep_dir/keyboard-backlight:$sleep_dir/force-igpu:$drop_in" \
|
||||
run_migration Integrated
|
||||
cmp -s "$mock_omarchy/default/systemd/system-sleep/keyboard-backlight" "$sleep_dir/keyboard-backlight" ||
|
||||
fail "migration mistakes executable released hook bytes for a current artifact"
|
||||
pass "migration refreshes recognized legacy hook contents at the final mode"
|
||||
|
||||
printf 'attacker gpu\n' >"$sleep_dir/force-igpu"
|
||||
chmod 0777 "$sleep_dir/force-igpu"
|
||||
run_migration Hybrid
|
||||
[[ $(stat -c '%a' "$sleep_dir/force-igpu") == 755 ]] ||
|
||||
fail "migration does not activate the trusted self-guarding force-igpu hook"
|
||||
pass "migration repairs force-igpu without depending on a live GPU-mode query"
|
||||
|
||||
printf 'administrator customization\n' >"$sleep_dir/keyboard-backlight"
|
||||
chmod 0755 "$sleep_dir/keyboard-backlight"
|
||||
run_migration Integrated "$sleep_dir/keyboard-backlight" 755
|
||||
grep -Fxq 'administrator customization' "$sleep_dir/keyboard-backlight" ||
|
||||
fail "migration preserves a secure administrator-owned custom hook"
|
||||
|
||||
cp "$mock_omarchy/default/systemd/system-sleep/keyboard-backlight" "$sleep_dir/keyboard-backlight"
|
||||
chmod 0644 "$sleep_dir/keyboard-backlight"
|
||||
run_migration Integrated "$sleep_dir/keyboard-backlight" 644
|
||||
[[ $(stat -c '%a' "$sleep_dir/keyboard-backlight") == 755 ]] ||
|
||||
fail "migration leaves an exact packaged keyboard hook non-executable"
|
||||
|
||||
printf 'administrator customization\n' >"$sleep_dir/keyboard-backlight"
|
||||
chmod 0644 "$sleep_dir/keyboard-backlight"
|
||||
run_migration Integrated "$sleep_dir/keyboard-backlight" 644
|
||||
[[ $(stat -c '%a' "$sleep_dir/keyboard-backlight") == 644 ]] ||
|
||||
fail "migration changes the mode of a safe noncanonical administrator hook"
|
||||
grep -Fxq 'administrator customization' "$sleep_dir/keyboard-backlight" ||
|
||||
fail "migration replaces a safe noncanonical administrator hook"
|
||||
pass "migration activates only exact packaged hooks while preserving safe custom files"
|
||||
|
||||
legacy_force_igpu="$test_tmp/legacy-force-igpu"
|
||||
cat >"$legacy_force_igpu" <<'SH'
|
||||
#!/bin/bash
|
||||
|
||||
# Use the Vfio to Integrated trick to turn off NVIDIA dgpu when in integrated mode
|
||||
# without needing to restart the computer. This is needed because computers like the Asus G14
|
||||
# will wake after suspend in Hybrid mode, even if the system was in Integrated mode before
|
||||
# suspending.
|
||||
|
||||
case "$1" in
|
||||
pre)
|
||||
# Before hibernating, switch to Vfio so the nvidia driver is detached from the dGPU.
|
||||
# Without this, hibernate resume fails because the nvidia driver can't freeze a
|
||||
# powered-off dGPU (returns -EIO), which aborts the entire resume.
|
||||
if [[ $2 == "hibernate" ]]; then
|
||||
/usr/bin/supergfxctl -m Vfio
|
||||
sleep 1
|
||||
fi
|
||||
;;
|
||||
post)
|
||||
# small delay so the device is fully re-enumerated
|
||||
sleep 4
|
||||
|
||||
# force-bind dGPU to vfio (fully detached from nvidia)
|
||||
/usr/bin/supergfxctl -m Vfio
|
||||
sleep 1
|
||||
|
||||
# then go back to Integrated, which powers it off again
|
||||
/usr/bin/supergfxctl -m Integrated
|
||||
;;
|
||||
esac
|
||||
SH
|
||||
[[ $(sha256sum "$legacy_force_igpu" | cut -d' ' -f1) == d604e7c4903829563e45fc52188fc5602c3f1bc66e247f0a2cc0a974ed6e57db ]] ||
|
||||
fail "force-igpu legacy fixture no longer matches the migration fingerprint"
|
||||
cp "$legacy_force_igpu" "$sleep_dir/force-igpu"
|
||||
chmod 0644 "$sleep_dir/force-igpu"
|
||||
FAKE_ROOT_FILES="$sleep_dir/keyboard-backlight:$sleep_dir/force-igpu:$drop_in" \
|
||||
run_migration Integrated
|
||||
cmp -s "$mock_omarchy/default/systemd/system-sleep/force-igpu" "$sleep_dir/force-igpu" ||
|
||||
fail "migration does not upgrade the exact legacy force-igpu hook"
|
||||
[[ $(stat -c '%a' "$sleep_dir/force-igpu") == 755 ]] ||
|
||||
fail "migration leaves the exact legacy force-igpu hook non-executable"
|
||||
pass "migration activates the exact legacy force-igpu artifact with its new guard"
|
||||
|
||||
printf 'wheel-managed customization\n' >"$sleep_dir/keyboard-backlight"
|
||||
chmod 0755 "$sleep_dir/keyboard-backlight"
|
||||
FAKE_ROOT_FILES="$sleep_dir/keyboard-backlight:$sleep_dir/force-igpu:$drop_in" \
|
||||
FAKE_ROOT_GID=10 run_migration Integrated
|
||||
grep -Fxq 'wheel-managed customization' "$sleep_dir/keyboard-backlight" ||
|
||||
fail "migration replaces a safe root:wheel administrator hook"
|
||||
[[ ! -s $calls ]] ||
|
||||
fail "migration escalates while preserving safe root:wheel entries"
|
||||
pass "migration treats non-writable root-owned files as safe regardless of group"
|
||||
|
||||
admin_dir="$test_tmp/admin-hooks"
|
||||
admin_keyboard="$admin_dir/keyboard"
|
||||
admin_delay="$admin_dir/delay.conf"
|
||||
mkdir -p "$admin_dir"
|
||||
printf 'protected keyboard customization\n' >"$admin_keyboard"
|
||||
printf 'protected delay customization\n' >"$admin_delay"
|
||||
chmod 0755 "$admin_keyboard"
|
||||
chmod 0644 "$admin_delay"
|
||||
rm -f "$sleep_dir/keyboard-backlight" "$drop_in"
|
||||
ln -s "$admin_keyboard" "$sleep_dir/keyboard-backlight"
|
||||
ln -s "$admin_delay" "$drop_in"
|
||||
|
||||
EXTRA_FAKE_ROOT_DIRS="$admin_dir" \
|
||||
FAKE_ROOT_FILES="$admin_keyboard:$admin_delay:$sleep_dir/force-igpu" \
|
||||
FAKE_ROOT_GID=10 run_migration Integrated
|
||||
[[ -L $sleep_dir/keyboard-backlight && $(readlink "$sleep_dir/keyboard-backlight") == "$admin_keyboard" ]] ||
|
||||
fail "migration replaces a safe administrator-managed keyboard-hook symlink"
|
||||
[[ -L $drop_in && $(readlink "$drop_in") == "$admin_delay" ]] ||
|
||||
fail "migration replaces a safe administrator-managed service-drop-in symlink"
|
||||
[[ ! -s $calls ]] ||
|
||||
fail "migration escalates while preserving safe administrator symlinks"
|
||||
pass "migration preserves symlinks whose full target paths are root-controlled"
|
||||
|
||||
dangling_target="$admin_dir/future-keyboard"
|
||||
rm -f "$sleep_dir/keyboard-backlight" "$dangling_target"
|
||||
ln -s "$dangling_target" "$sleep_dir/keyboard-backlight"
|
||||
|
||||
EXTRA_FAKE_ROOT_DIRS="$admin_dir" \
|
||||
FAKE_ROOT_FILES="$admin_delay:$sleep_dir/force-igpu:$drop_in" \
|
||||
run_migration Integrated
|
||||
[[ -L $sleep_dir/keyboard-backlight && $(readlink "$sleep_dir/keyboard-backlight") == "$dangling_target" ]] ||
|
||||
fail "migration replaces a safe dangling administrator symlink"
|
||||
[[ ! -s $calls ]] ||
|
||||
fail "migration asks for sudo to verify an absent target below a searchable root-controlled directory"
|
||||
pass "migration handles safe dangling administrator symlinks without sudo"
|
||||
|
||||
escaping_user_dir="$test_tmp/escaping-user-hooks"
|
||||
escaping_user_hook="$escaping_user_dir/keyboard"
|
||||
escaping_missing_dir="$admin_dir/future"
|
||||
escaping_target="$escaping_missing_dir/../../escaping-user-hooks/keyboard"
|
||||
mkdir -p "$escaping_user_dir"
|
||||
printf 'future unsafe keyboard customization\n' >"$escaping_user_hook"
|
||||
chmod 0755 "$escaping_user_hook"
|
||||
rm -f "$sleep_dir/keyboard-backlight"
|
||||
ln -s "$escaping_target" "$sleep_dir/keyboard-backlight"
|
||||
|
||||
EXTRA_FAKE_ROOT_DIRS="$admin_dir" \
|
||||
FAKE_ROOT_FILES="$admin_delay:$sleep_dir/force-igpu:$drop_in" \
|
||||
run_migration Integrated
|
||||
[[ ! -L $sleep_dir/keyboard-backlight ]] ||
|
||||
fail "migration trusts a dangling symlink whose unresolved suffix escapes to a user-controlled path"
|
||||
cmp -s "$mock_omarchy/default/systemd/system-sleep/keyboard-backlight" \
|
||||
"$sleep_dir/keyboard-backlight" ||
|
||||
fail "migration does not replace a future user-controlled dangling symlink"
|
||||
pass "migration resolves the full dangling-symlink suffix before trusting it"
|
||||
|
||||
protected_dir="$test_tmp/root-only-hooks"
|
||||
protected_target="$protected_dir/target"
|
||||
protected_bridge="$protected_dir/bridge"
|
||||
mkdir -p "$protected_dir"
|
||||
printf 'root-only administrator customization\n' >"$protected_target"
|
||||
ln -s "$protected_target" "$protected_bridge"
|
||||
chmod 0700 "$protected_dir"
|
||||
rm -f "$sleep_dir/keyboard-backlight"
|
||||
ln -s "$protected_bridge" "$sleep_dir/keyboard-backlight"
|
||||
|
||||
EXTRA_FAKE_ROOT_DIRS="$admin_dir:$protected_dir" \
|
||||
FAKE_ROOT_FILES="$protected_target:$admin_delay:$sleep_dir/force-igpu:$drop_in" \
|
||||
INACCESSIBLE_AS_USER="$protected_bridge:$protected_target" \
|
||||
run_migration Integrated
|
||||
[[ -L $sleep_dir/keyboard-backlight && $(readlink "$sleep_dir/keyboard-backlight") == "$protected_bridge" ]] ||
|
||||
fail "migration replaces a safe symlink whose target is hidden by a root-only directory"
|
||||
grep -q $'^sudo\t.*/stat\t-c\t%f %u %g %a\t--\t.*/root-only-hooks/bridge$' "$calls" ||
|
||||
fail "migration does not inspect inaccessible symlink metadata with privilege"
|
||||
grep -q $'^sudo\t.*/readlink\t--\t.*/root-only-hooks/bridge$' "$calls" ||
|
||||
fail "migration does not resolve an inaccessible administrator symlink with privilege"
|
||||
grep -q $'^sudo\t.*/stat\t-c\t%f %u %g %a\t--\t.*/root-only-hooks/target$' "$calls" ||
|
||||
fail "migration does not inspect an inaccessible administrator target with privilege"
|
||||
pass "migration preserves root-controlled symlink chains hidden from the invoking user"
|
||||
|
||||
protected_dangling_dir="$test_tmp/root-only-dangling"
|
||||
protected_dangling_target="$protected_dangling_dir/future-keyboard"
|
||||
mkdir -p "$protected_dangling_dir"
|
||||
chmod 0000 "$protected_dangling_dir"
|
||||
rm -f "$sleep_dir/keyboard-backlight"
|
||||
ln -s "$protected_dangling_target" "$sleep_dir/keyboard-backlight"
|
||||
|
||||
EXTRA_FAKE_ROOT_DIRS="$admin_dir:$protected_dangling_dir" \
|
||||
FAKE_ROOT_FILES="$admin_delay:$sleep_dir/force-igpu:$drop_in" \
|
||||
INACCESSIBLE_AS_USER="$protected_dangling_target" \
|
||||
run_migration Integrated
|
||||
[[ -L $sleep_dir/keyboard-backlight && $(readlink "$sleep_dir/keyboard-backlight") == "$protected_dangling_target" ]] ||
|
||||
fail "migration replaces a safe dangling symlink below a root-only directory"
|
||||
grep -q $'^sudo\t.*/stat\t-c\t%f %u %g %a\t--\t.*/root-only-dangling/future-keyboard$' "$calls" ||
|
||||
fail "migration does not inspect a protected dangling target with privilege"
|
||||
grep -q $'^sudo\t/usr/bin/test\t-x\t.*/root-only-dangling$' "$calls" ||
|
||||
fail "migration does not distinguish a protected missing target from an inaccessible parent"
|
||||
pass "migration preserves dangling administrator symlinks below root-only directories"
|
||||
|
||||
user_dir="$test_tmp/user-hooks"
|
||||
user_keyboard="$user_dir/keyboard"
|
||||
mkdir -p "$user_dir"
|
||||
printf 'unsafe symlink customization\n' >"$user_keyboard"
|
||||
chmod 0755 "$user_keyboard"
|
||||
rm -f "$sleep_dir/keyboard-backlight"
|
||||
ln -s "$user_keyboard" "$sleep_dir/keyboard-backlight"
|
||||
|
||||
EXTRA_FAKE_ROOT_DIRS="$admin_dir" \
|
||||
FAKE_ROOT_FILES="$admin_delay:$sleep_dir/force-igpu" run_migration Integrated
|
||||
[[ ! -L $sleep_dir/keyboard-backlight ]] ||
|
||||
fail "migration leaves a user-controlled keyboard-hook symlink active"
|
||||
cmp -s "$mock_omarchy/default/systemd/system-sleep/keyboard-backlight" \
|
||||
"$sleep_dir/keyboard-backlight" ||
|
||||
fail "migration does not replace an unsafe symlink with trusted hook content"
|
||||
symlink_backup=$(find "$quarantine" -path '*/keyboard-backlight.*/original' -type l -print -quit)
|
||||
[[ -n $symlink_backup && $(readlink "$symlink_backup") == "$user_keyboard" ]] ||
|
||||
fail "migration discards an unsafe custom symlink instead of preserving it"
|
||||
pass "migration quarantines unsafe symlinks outside the active systemd directory"
|
||||
|
||||
bridge="$user_dir/bridge"
|
||||
ln -s "$admin_keyboard" "$bridge"
|
||||
rm -f "$sleep_dir/keyboard-backlight"
|
||||
ln -s "$bridge" "$sleep_dir/keyboard-backlight"
|
||||
|
||||
EXTRA_FAKE_ROOT_DIRS="$admin_dir" \
|
||||
FAKE_ROOT_FILES="$admin_keyboard:$admin_delay:$sleep_dir/force-igpu" \
|
||||
run_migration Integrated
|
||||
[[ ! -L $sleep_dir/keyboard-backlight ]] ||
|
||||
fail "migration trusts a symlink chain routed through a user-controlled directory"
|
||||
cmp -s "$mock_omarchy/default/systemd/system-sleep/keyboard-backlight" \
|
||||
"$sleep_dir/keyboard-backlight" ||
|
||||
fail "migration does not repair an indirectly user-controlled symlink"
|
||||
pass "migration checks every intermediate component in a symlink chain"
|
||||
|
||||
hook_copy="$test_tmp/force-igpu-hook"
|
||||
hook_calls="$test_tmp/force-igpu-calls"
|
||||
hook_queries="$test_tmp/force-igpu-queries"
|
||||
hook_config="$test_tmp/supergfxd.conf"
|
||||
hook_marker="$test_tmp/force-igpu-restore"
|
||||
hook_pending="$test_tmp/force-igpu-pending"
|
||||
sed \
|
||||
-e "s|/usr/bin/supergfxctl|$stub_bin/hook-supergfxctl|g" \
|
||||
-e "s|/usr/bin/install|$stub_bin/hook-install|g" \
|
||||
-e "s|/etc/supergfxd.conf|$hook_config|g" \
|
||||
-e "s|/run/omarchy-force-igpu-integrated|$hook_marker|g" \
|
||||
"$ROOT/default/systemd/system-sleep/force-igpu" >"$hook_copy"
|
||||
cat >"$stub_bin/hook-supergfxctl" <<'SH'
|
||||
#!/bin/bash
|
||||
|
||||
case "$1" in
|
||||
-m)
|
||||
printf '%s\n' "$*" >>"$HOOK_CALLS"
|
||||
if [[ ${HOOK_BLOCK_MODE:-} == "$2" ]]; then
|
||||
trap '' TERM
|
||||
/usr/bin/sleep 30
|
||||
fi
|
||||
current=$(sed -n 's/.*"mode"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$HOOK_CONFIG")
|
||||
if [[ $current != "$2" ]]; then
|
||||
printf '%s %s\n' "$2" "${HOOK_CONFIRM_AFTER:-1}" >"$HOOK_PENDING"
|
||||
fi
|
||||
;;
|
||||
-g)
|
||||
printf '%s\n' "$*" >>"$HOOK_QUERIES"
|
||||
if [[ -f $HOOK_PENDING ]]; then
|
||||
read -r pending remaining <"$HOOK_PENDING"
|
||||
if [[ ${HOOK_FAIL_MODE:-} != "$pending" ]]; then
|
||||
remaining=$((remaining - 1))
|
||||
if (( remaining <= 0 )); then
|
||||
sed -i "s/\"mode\"[[:space:]]*:[[:space:]]*\"[^\"]*\"/\"mode\": \"$pending\"/" "$HOOK_CONFIG"
|
||||
rm -f -- "$HOOK_PENDING"
|
||||
else
|
||||
printf '%s %s\n' "$pending" "$remaining" >"$HOOK_PENDING"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
sed -n 's/.*"mode"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$HOOK_CONFIG"
|
||||
;;
|
||||
esac
|
||||
SH
|
||||
cat >"$stub_bin/hook-install" <<'SH'
|
||||
#!/bin/bash
|
||||
args=()
|
||||
while (($#)); do
|
||||
case "$1" in
|
||||
-o | -g)
|
||||
shift 2
|
||||
;;
|
||||
*)
|
||||
args+=("$1")
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
exec /usr/bin/install "${args[@]}"
|
||||
SH
|
||||
cat >"$stub_bin/sleep" <<'SH'
|
||||
#!/bin/bash
|
||||
:
|
||||
SH
|
||||
chmod +x "$stub_bin/hook-supergfxctl" "$stub_bin/hook-install" "$stub_bin/sleep"
|
||||
|
||||
hook_env=(
|
||||
"HOOK_CALLS=$hook_calls"
|
||||
"HOOK_QUERIES=$hook_queries"
|
||||
"HOOK_CONFIG=$hook_config"
|
||||
"HOOK_PENDING=$hook_pending"
|
||||
"PATH=$stub_bin:$PATH"
|
||||
)
|
||||
|
||||
printf '{ "mode": "Hybrid" }\n' >"$hook_config"
|
||||
env "${hook_env[@]}" bash "$hook_copy" pre suspend
|
||||
env "${hook_env[@]}" bash "$hook_copy" post suspend
|
||||
[[ ! -e $hook_calls ]] || fail "force-igpu runs while the root-owned config says Hybrid"
|
||||
[[ ! -e $hook_marker ]] || fail "force-igpu records restore intent while configured for Hybrid mode"
|
||||
|
||||
rm -f "$hook_config"
|
||||
env "${hook_env[@]}" bash "$hook_copy" pre suspend
|
||||
env "${hook_env[@]}" bash "$hook_copy" post suspend
|
||||
[[ ! -e $hook_calls ]] || fail "force-igpu runs when its mode config is unavailable"
|
||||
[[ ! -e $hook_marker ]] || fail "force-igpu records restore intent without a mode config"
|
||||
|
||||
printf '{ "mode": "Integrated" }\n' >"$hook_config"
|
||||
env "${hook_env[@]}" bash "$hook_copy" pre suspend
|
||||
[[ -f $hook_marker && $(stat -c '%a' "$hook_marker") == 600 ]] ||
|
||||
fail "force-igpu does not securely record Integrated restore intent during pre-suspend"
|
||||
HOOK_CONFIRM_AFTER=2 env "${hook_env[@]}" bash "$hook_copy" post suspend
|
||||
[[ $(wc -l <"$hook_calls") == 2 ]] ||
|
||||
fail "force-igpu does not run both GPU transitions in Integrated mode"
|
||||
grep -Fqx -- '-m Integrated' "$hook_calls" ||
|
||||
fail "force-igpu does not restore Integrated mode after suspend"
|
||||
[[ ! -e $hook_marker ]] || fail "force-igpu leaves stale restore intent after suspend"
|
||||
(( $(wc -l <"$hook_queries") >= 4 )) ||
|
||||
fail "force-igpu does not wait for asynchronous GPU transitions"
|
||||
pass "force-igpu confirms asynchronous transitions for Integrated sleep cycles"
|
||||
|
||||
: >"$hook_calls"
|
||||
: >"$hook_queries"
|
||||
printf '{ "mode": "Integrated" }\n' >"$hook_config"
|
||||
env "${hook_env[@]}" bash "$hook_copy" pre suspend
|
||||
set +e
|
||||
HOOK_CONFIRM_AFTER=2 HOOK_FAIL_MODE=Integrated env "${hook_env[@]}" \
|
||||
bash "$hook_copy" post suspend >/dev/null 2>&1
|
||||
restore_status=$?
|
||||
set -e
|
||||
(( restore_status != 0 )) || fail "force-igpu reports success without confirming Integrated mode"
|
||||
grep -Fq '"mode": "Vfio"' "$hook_config" ||
|
||||
fail "force-igpu failure test does not leave the transition in Vfio mode"
|
||||
[[ -f $hook_marker ]] || fail "force-igpu discards restore intent after an asynchronous transition failure"
|
||||
HOOK_CONFIRM_AFTER=2 env "${hook_env[@]}" bash "$hook_copy" post suspend
|
||||
grep -Fq '"mode": "Integrated"' "$hook_config" ||
|
||||
fail "force-igpu does not recover the Integrated transition on the next sleep cycle"
|
||||
[[ ! -e $hook_marker ]] || fail "force-igpu leaves restore intent after a confirmed retry"
|
||||
pass "force-igpu retains restore intent until Integrated mode is confirmed"
|
||||
|
||||
: >"$hook_calls"
|
||||
: >"$hook_queries"
|
||||
printf '{ "mode": "Integrated" }\n' >"$hook_config"
|
||||
env "${hook_env[@]}" bash "$hook_copy" pre suspend
|
||||
set +e
|
||||
HOOK_BLOCK_MODE=Vfio env "${hook_env[@]}" \
|
||||
bash "$hook_copy" post suspend >/dev/null 2>&1
|
||||
blocked_request_status=$?
|
||||
set -e
|
||||
(( blocked_request_status != 0 )) || fail "force-igpu waits forever for a blocked GPU transition request"
|
||||
[[ -f $hook_marker ]] || fail "force-igpu discards restore intent after a blocked transition request"
|
||||
[[ ! -s $hook_queries ]] || fail "force-igpu polls before a blocked transition request returns"
|
||||
env "${hook_env[@]}" bash "$hook_copy" post suspend
|
||||
[[ ! -e $hook_marker ]] || fail "force-igpu cannot retry after a blocked transition request"
|
||||
pass "force-igpu bounds blocked transition requests and retains retry intent"
|
||||
|
||||
: >"$hook_calls"
|
||||
printf '{ "mode": "Integrated" }\n' >"$hook_config"
|
||||
env "${hook_env[@]}" bash "$hook_copy" pre hibernate
|
||||
grep -Fq '"mode": "Vfio"' "$hook_config" ||
|
||||
fail "force-igpu test double does not model the pre-hibernate Vfio persistence"
|
||||
[[ -f $hook_marker ]] || fail "force-igpu loses restore intent during the Vfio transition"
|
||||
env "${hook_env[@]}" bash "$hook_copy" post hibernate
|
||||
[[ $(wc -l <"$hook_calls") == 3 ]] ||
|
||||
fail "force-igpu skips the post-hibernate transitions after Vfio changes the config"
|
||||
[[ $(tail -1 "$hook_calls") == "-m Integrated" ]] ||
|
||||
fail "force-igpu does not finish post-hibernate restoration in Integrated mode"
|
||||
grep -Fq '"mode": "Integrated"' "$hook_config" ||
|
||||
fail "force-igpu leaves supergfxd configured for Vfio after hibernation"
|
||||
[[ ! -e $hook_marker ]] || fail "force-igpu leaves stale restore intent after hibernation"
|
||||
pass "force-igpu restores Integrated mode after pre-hibernate persists Vfio"
|
||||
|
||||
: >"$hook_calls"
|
||||
printf '{ "mode": "Integrated" }\n' >"$hook_config"
|
||||
SYSTEMD_SLEEP_ACTION=suspend env "${hook_env[@]}" bash "$hook_copy" pre suspend-then-hibernate
|
||||
SYSTEMD_SLEEP_ACTION=suspend env "${hook_env[@]}" bash "$hook_copy" post suspend-then-hibernate
|
||||
[[ $(wc -l <"$hook_calls") == 2 ]] ||
|
||||
fail "force-igpu does not complete the initial suspend phase of suspend-then-hibernate"
|
||||
SYSTEMD_SLEEP_ACTION=hibernate env "${hook_env[@]}" bash "$hook_copy" pre suspend-then-hibernate
|
||||
[[ $(wc -l <"$hook_calls") == 3 && $(tail -1 "$hook_calls") == "-m Vfio" ]] ||
|
||||
fail "force-igpu skips the Vfio transition before compound hibernation"
|
||||
grep -Fq '"mode": "Vfio"' "$hook_config" ||
|
||||
fail "force-igpu does not detach the dGPU during the hibernate phase"
|
||||
[[ -f $hook_marker ]] || fail "force-igpu loses restore intent during compound hibernation"
|
||||
SYSTEMD_SLEEP_ACTION=hibernate env "${hook_env[@]}" bash "$hook_copy" post suspend-then-hibernate
|
||||
[[ $(wc -l <"$hook_calls") == 5 && $(tail -1 "$hook_calls") == "-m Integrated" ]] ||
|
||||
fail "force-igpu does not restore Integrated mode after compound hibernation"
|
||||
grep -Fq '"mode": "Integrated"' "$hook_config" ||
|
||||
fail "force-igpu leaves supergfxd configured for Vfio after compound hibernation"
|
||||
[[ ! -e $hook_marker ]] || fail "force-igpu leaves stale restore intent after compound hibernation"
|
||||
pass "force-igpu handles both phases of suspend-then-hibernate"
|
||||
|
||||
keyboard_hook_copy="$test_tmp/keyboard-backlight-hook"
|
||||
keyboard_calls="$test_tmp/keyboard-backlight-calls"
|
||||
keyboard_led_dir="$test_tmp/leds"
|
||||
mkdir -p "$keyboard_led_dir/asus::kbd_backlight"
|
||||
sed "s|/sys/class/leds/\*kbd_backlight\*|$keyboard_led_dir/*kbd_backlight*|" \
|
||||
"$ROOT/default/systemd/system-sleep/keyboard-backlight" >"$keyboard_hook_copy"
|
||||
cat >"$stub_bin/brightnessctl" <<'SH'
|
||||
#!/bin/bash
|
||||
printf '%s\n' "$*" >>"$KEYBOARD_CALLS"
|
||||
SH
|
||||
chmod +x "$stub_bin/brightnessctl"
|
||||
|
||||
SYSTEMD_SLEEP_ACTION=suspend KEYBOARD_CALLS="$keyboard_calls" PATH="$stub_bin:$PATH" \
|
||||
bash "$keyboard_hook_copy" pre suspend-then-hibernate
|
||||
[[ ! -e $keyboard_calls ]] || fail "keyboard-backlight runs during the suspend phase of compound sleep"
|
||||
SYSTEMD_SLEEP_ACTION=hibernate KEYBOARD_CALLS="$keyboard_calls" PATH="$stub_bin:$PATH" \
|
||||
bash "$keyboard_hook_copy" pre suspend-then-hibernate
|
||||
grep -Fqx -- '-d asus::kbd_backlight set 0' "$keyboard_calls" ||
|
||||
fail "keyboard-backlight skips the hibernate phase of compound sleep"
|
||||
pass "keyboard-backlight handles the hibernate phase of suspend-then-hibernate"
|
||||
@@ -132,3 +132,47 @@ if problems:
|
||||
PYTHON
|
||||
|
||||
pass "no Omarchy script writes a path under /usr that no package owns"
|
||||
|
||||
for script in bin/omarchy-hibernation-setup bin/omarchy-toggle-hybrid-gpu; do
|
||||
grep -F '"${destination%/*}/.${destination##*/}.omarchy.XXXXXX"' "$ROOT/$script" >/dev/null ||
|
||||
fail "$script reserves a hidden sibling for the privileged replacement"
|
||||
grep -F 'sudo /usr/bin/install -m "$mode" -o root -g root -T "$source" "$stage"' "$ROOT/$script" >/dev/null ||
|
||||
fail "$script prepares privileged files with final root ownership and mode"
|
||||
grep -F 'sudo /usr/bin/mv -Tf -- "$stage" "$destination"' "$ROOT/$script" >/dev/null ||
|
||||
fail "$script atomically replaces the privileged destination"
|
||||
if grep -F 'sudo /usr/bin/chmod "$mode" "$destination"' "$ROOT/$script" >/dev/null; then
|
||||
fail "$script changes mode after publishing the privileged destination"
|
||||
fi
|
||||
done
|
||||
|
||||
grep -F ' /usr/lib/systemd/system-sleep/keyboard-backlight 0755' "$ROOT/bin/omarchy-hibernation-setup" >/dev/null ||
|
||||
fail "hibernation setup installs keyboard-backlight as a root-owned executable"
|
||||
|
||||
hook_install_line=$(rg -n '^if ! install_root_file .*keyboard-backlight' "$ROOT/bin/omarchy-hibernation-setup" | cut -d: -f1)
|
||||
resume_marker_line=$(rg -n '^echo "HOOKS\+=\(resume\)"' "$ROOT/bin/omarchy-hibernation-setup" | cut -d: -f1)
|
||||
[[ -n $hook_install_line && -n $resume_marker_line ]] ||
|
||||
fail "hibernation setup keeps recognizable hook-install and resume-marker steps"
|
||||
(( hook_install_line < resume_marker_line )) ||
|
||||
fail "hibernation setup marks completion before a failed hook install can be retried"
|
||||
|
||||
grep -F ' /usr/lib/systemd/system-sleep/force-igpu 0755' "$ROOT/bin/omarchy-toggle-hybrid-gpu" >/dev/null ||
|
||||
fail "hybrid GPU setup installs force-igpu as a root-owned executable"
|
||||
grep -F ' /etc/systemd/system/supergfxd.service.d/delay-start.conf 0644' "$ROOT/bin/omarchy-toggle-hybrid-gpu" >/dev/null ||
|
||||
fail "hybrid GPU setup installs its root service drop-in as root-owned configuration"
|
||||
|
||||
delay_install_line=$(rg -n '^ if ! install_root_file .*delay-start\.conf' "$ROOT/bin/omarchy-toggle-hybrid-gpu" | cut -d: -f1)
|
||||
force_install_line=$(rg -n '^ if ! install_root_file .*force-igpu' "$ROOT/bin/omarchy-toggle-hybrid-gpu" | cut -d: -f1)
|
||||
config_switch_line=$(rg -n '^ sudo sed -i \\' "$ROOT/bin/omarchy-toggle-hybrid-gpu" | tail -1 | cut -d: -f1)
|
||||
[[ -n $delay_install_line && -n $force_install_line && -n $config_switch_line ]] ||
|
||||
fail "hybrid GPU setup keeps recognizable support-file and config-switch steps"
|
||||
(( delay_install_line < config_switch_line && force_install_line < config_switch_line )) ||
|
||||
fail "hybrid GPU setup switches config before every required file is installed"
|
||||
|
||||
grep -Fq '/usr/bin/grep -Eq' "$ROOT/default/systemd/system-sleep/force-igpu" ||
|
||||
fail "force-igpu does not guard execution with the configured GPU mode"
|
||||
|
||||
if rg -n 'cp -p.*(system-sleep|supergfxd\.service\.d)' "$ROOT/bin/omarchy-hibernation-setup" "$ROOT/bin/omarchy-toggle-hybrid-gpu"; then
|
||||
fail "privileged sleep and hybrid GPU files are never copied with source ownership"
|
||||
fi
|
||||
|
||||
pass "system-sleep hooks and the hybrid GPU drop-in enforce root ownership"
|
||||
Reference in new issue
Block a user