fix: make the backlight fix reversible and portable across distributions - #159
Conversation
Audit of zephyrus-backlight.py. Nothing in it could make a machine unbootable: it never runs grub-install, never touches the ESP or NVRAM, and grub-mkconfig validates with grub-script-check before installing anything. Five things could still leave a configuration worse than they found it. - The modprobe rule ran /usr/bin/modprobe, which only exists on Fedora 44 and newer, where /usr/sbin became a symlink to bin. On an older base the install rule exits 127 and the module never loads, so brightness stays dead in Hybrid mode with no visible error. /sbin/modprobe resolves everywhere this script supports. - disable removed acpi_backlight=native but never put back the value that enable had replaced, so a machine that started with acpi_backlight=vendor ended up with neither. It now reads the old value back out of the backup that enable writes. - The modprobe rule was overwritten without a backup and deleted outright on disable, under a filename generic enough that somebody may already be using it. A rule this script did not write is now moved aside first and restored on disable, and is never deleted without leaving a copy. The copy lives under /var/lib rather than in /etc/modprobe.d, because older kmod still reads files there that do not end in .conf. - enable refused to run on anything that is not a GA605WV; disable did not, so it would strip acpi_backlight=native from an unrelated laptop where it was somebody else's fix. It now asks first, and refuses under --silent. - The root script read the rule and the GRUB contents from files this unprivileged process could still write, leaving a window to swap them between the polkit prompt and the copy. Root now writes every file it reads. Applying also says up front how long it takes. rpm-ostree kargs writes a whole deployment with no output for 30 to 60 seconds, which reads as a hang, so Ctrl+C is the natural reaction. The GRUB path now traps INT and TERM and puts the old file back, so that reaction costs nothing either. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014EDz8am5VoAy2xbgHunuim
There was a problem hiding this comment.
🟡 Changes recommended
Unresolved rollback, restoration, and partial-application safety issues remain in the backlight script.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
Updates the backlight utility to improve portability, reversibility, rollback handling, and documentation across GRUB and rpm-ostree systems.
Changes:
- Uses portable modprobe paths and safer configuration backups.
- Adds interruption handling, restoration logic, and model confirmation.
- Updates English and Dutch documentation checksums and guidance.
File summaries
| File | Description |
|---|---|
src/static/scripts/zephyrus-backlight.py |
Implements portability, backups, rollback, and safety checks; has unresolved review findings. |
src/content/docs/known-issues.md |
Updates usage guidance and checksum. |
src/content/docs/known-issues.nl.md |
Updates Dutch guidance and checksum. |
Review details
Suppressed comments (4)
src/static/scripts/zephyrus-backlight.py:721
- On a disable, this overwrites the persistent
GRUB_BACKUP, which is the only sourcereplaced_by_enable()uses for restoring a value such asacpi_backlight=vendor, before the new file has been validated. Ifgrub-mkconfigthen fails or is interrupted,put_back()restores the just-copiednativefile and the originalvendorbackup is already lost; a retry removesnativewithout restoringvendor. Keep the enable backup separate from the per-run rollback copy, or update it only after disable succeeds.
cp -p {q(str(GRUB_DEFAULT))} {q(str(GRUB_BACKUP))}
put_back() {{ cp -p {q(str(GRUB_BACKUP))} {q(str(GRUB_DEFAULT))} 2>/dev/null || true; }}
src/static/scripts/zephyrus-backlight.py:752
- When
disableis run before this script has ever enabled (so no backup exists), a pre-existing different rule enters this branch, is copied aside, and then is unconditionally removed. That still deactivates an unrelated user's modprobe configuration; the backup only helps recovery and is not restored. If there is no script-owned backup, leave adifferentrule untouched or require explicit confirmation instead of treating it as this fix's rule.
if [ -e {q(str(MODPROBE_CONF))} ] \\
&& ! cmp -s "$work/rule.conf" {q(str(MODPROBE_CONF))}; then
install -D -m 644 {q(str(MODPROBE_CONF))} {q(str(MODPROBE_BACKUP))}
fi
rm -f {q(str(MODPROBE_CONF))}
src/static/scripts/zephyrus-backlight.py:721
- If the restore copy fails,
2>/dev/null || truehides that failure and the caller still reportsNothing was changedon exit 130 (and the failure path otherwise continues). The new configuration can therefore remain in/etc/default/grub; this safety path should verify the restore and surface an error when it cannot put the old file back.
put_back() {{ cp -p {q(str(GRUB_BACKUP))} {q(str(GRUB_DEFAULT))} 2>/dev/null || true; }}
src/static/scripts/zephyrus-backlight.py:559
restorecan contain multiple originalacpi_backlight=tokens because enable records every matching token. After the first is appended, this key check suppresses all remaining saved values, so duplicate kernel arguments are not restored to their original order/content and the change is not fully reversible.
# Only when nothing else claims that key now, so a value the user
# set by hand after enabling wins over the one from the backup.
if not any(p.startswith(key) for p in params):
params.append(old)
replaced.append(old)
- Files reviewed: 3/3 changed files
- Comments generated: 4
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
GRUB_BACKUP served two conflicting purposes: the transient rollback copy for undoing a single run, and the persistent record disable reads to restore the pre-fix acpi_backlight= value. Every GRUB-touching run overwrote it before validating anything, so a failed or interrupted disable clobbered the persistent record with the fix's own value, and a retry lost the original permanently. The rollback copy now lives under the per-run $work dir instead, and the persistent backup is only written by enable and only cleared by a successful disable. Also, from the same review: - put_back() silently swallowed its own failure, so a restore that didn't actually happen could still be reported as "nothing changed". It now surfaces PUT_BACK_FAILED instead. - disable's modprobe removal only touches /etc/modprobe.d's rule now when the live file still matches the one this script installed, instead of deleting or replacing whatever is there whenever a rule exists at that path. - rewrite_grub() only restores a backed-up acpi_backlight= value when the fix is actually present to remove, so a stale backup left over from a manually-undone enable can't reintroduce it. It also restores every original value when there was more than one, instead of only the first. All eight `_apply` code paths (enable/disable x GRUB/ostree x write/ remove/none) were regenerated and checked with `bash -n`, and rewrite_grub()'s new branches were exercised directly, both outside the actual privileged execution. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019tcTRe2u556WqGciAvDh7G
|
Pushed a549eab addressing the Copilot review. Summary of the review's four collapsed ("suppressed") findings, since there's no inline thread to reply to for those individually:
The four separately-threaded comments are addressed inline below. |
Audits
zephyrus-backlight.pyand fixes five ways it could leave a system's backlight configuration worse off than it found it, without changing what the script is for.Type of change
feat: new page or featurefix: bug fix (broken link, incorrect command, layout issue)content: update or improve existing contentdocs: changes to CONTRIBUTING, README, or meta documentationchore: maintenance (dependencies, config, CI/CD)refactor: restructuring without content changesstyle: formatting, whitespace, typosrevert: reverting a previous commitDetails
Nothing in the script could make a machine unbootable: it never runs
grub-install, never touches the ESP or NVRAM, andgrub-mkconfigvalidates withgrub-script-checkbefore installing anything. Five things could still leave a configuration worse than they found it:/usr/bin/modprobe, which only exists on Fedora 44+ where/usr/sbinbecame a symlink tobin. On an older base the install rule exits 127 and the module never loads, silently./sbin/modproberesolves everywhere this script supports.disableremovedacpi_backlight=nativebut never restored the valueenablehad replaced, so a machine that started withacpi_backlight=vendorended up with neither. It now reads the old value back out of the backupenablewrites.disable, under a filename generic enough that someone else may already be using it. A rule this script didn't write is now moved aside first and restored ondisable, never deleted without leaving a copy (kept under/var/libsince olderkmodstill reads files in/etc/modprobe.dthat don't end in.conf).enablerefused to run on anything that isn't a GA605WV;disabledidn't, so it would stripacpi_backlight=nativefrom an unrelated laptop where it was somebody else's fix. It now asks first, and refuses under--silent.Applying also now says up front how long it takes:
rpm-ostree kargswrites a whole deployment with no output for 30-60s, which reads as a hang. The GRUB path traps INT/TERM and restores the old file, so a Ctrl+C during that window costs nothing.Checklist
fix: correct nmcli command in eduroam guide)/images/*.avifall exist instatic/images/)hugo server