diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index f0b8e2d..e87b122 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -19,7 +19,7 @@ body: attributes: label: Fiero-Hotspot version description: Output of `fiero-hotspot version` - placeholder: fiero-hotspot v2.1.2 + placeholder: fiero-hotspot v2.1.3 validations: required: true - type: input diff --git a/99-fiero-hotspot.rules b/99-fiero-hotspot.rules index f6c0479..cbe1b37 100644 --- a/99-fiero-hotspot.rules +++ b/99-fiero-hotspot.rules @@ -1,3 +1,8 @@ # NOTE: @TARGET_USER@ is dynamically replaced by install.sh during installation. -ACTION=="change", SUBSYSTEM=="power_supply", ATTR{type}=="Mains|USB", RUN+="/usr/bin/systemd-run --no-block --collect -- /usr/bin/su @TARGET_USER@ -c /usr/local/bin/fiero-prompt" +# KillMode=process is required: systemd-run creates a transient *service* unit, +# whose default KillMode=control-group SIGTERMs every remaining process in the +# cgroup once `su` exits. `setsid --fork` detaches fiero-prompt watch from the +# session but not from the cgroup, so without this the watcher is killed before +# the daemon emits STARTING/LIVE and lifecycle notifications never appear. +ACTION=="change", SUBSYSTEM=="power_supply", ATTR{type}=="Mains|USB", RUN+="/usr/bin/systemd-run --no-block --collect --property=KillMode=process -- /usr/bin/su @TARGET_USER@ -c /usr/local/bin/fiero-prompt" # END OF FILE diff --git a/CHANGELOG.md b/CHANGELOG.md index 9277695..84e7b3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ All notable changes to this project are documented here. The format is based on ## [Unreleased] +## [2.1.3] - 2026-09-28 + +### Fixed +- Fixed missing `STARTING` and `LIVE` desktop notifications by adding `--property=KillMode=process` to the udev `systemd-run` rule. Prevents systemd control-group teardown from terminating the detached `fiero-prompt watch` process when the prompt script exits. + ## [2.1.2] - 2026-09-28 ### Changed diff --git a/README.md b/README.md index fe6d6f7..5c68c07 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Fiero-Hotspot -![Version](https://img.shields.io/badge/version-v2.1.2-blue) +![Version](https://img.shields.io/badge/version-v2.1.3-blue) ![License](https://img.shields.io/badge/license-GPL--3.0-green) [![CI](https://github.com/Fiero1186/Fiero-Hotspot/actions/workflows/ci.yml/badge.svg)](https://github.com/Fiero1186/Fiero-Hotspot/actions/workflows/ci.yml) @@ -84,7 +84,7 @@ The upstream Wi-Fi connection must be managed by NetworkManager. The daemon uses ``` udev event (AC online/offline) └─> 99-fiero-hotspot.rules - └─> systemd-run --no-block --collect -- su - -c /usr/local/bin/fiero-prompt + └─> systemd-run --no-block --collect --property=KillMode=process -- su -c /usr/local/bin/fiero-prompt ├─> setsid --fork fiero-prompt watch (detached event watcher, one per session) └─> sudo -n systemctl start/stop fiero-hotspot.service └─> /usr/local/bin/fiero-hotspot {start|stop} @@ -95,7 +95,7 @@ udev event (AC online/offline) └─> notify-send (as the logged-in user) ``` -The udev rule fires on any `power_supply` `change` event where `ATTR{type}=="Mains"` and `ATTR{online}` is `1` (plugged) or `0` (unplugged). This invokes `fiero-prompt.sh` as the logged-in user via `systemd-run` + `su` (so udev does not kill the long-running prompt), which presents a desktop notification action prompt and issues `sudo -n systemctl start|stop` accordingly. The first prompt run also spawns a detached, flock-serialized event watcher (`fiero-prompt watch`) that stays alive for the session. +The udev rule fires on any `power_supply` `change` event where `ATTR{type}=="Mains"` and `ATTR{online}` is `1` (plugged) or `0` (unplugged). This invokes `fiero-prompt.sh` as the logged-in user via `systemd-run` + `su` (so udev does not kill the long-running prompt), which presents a desktop notification action prompt and issues `sudo -n systemctl start|stop` accordingly. The first prompt run also spawns a detached, flock-serialized event watcher (`fiero-prompt watch`) that stays alive for the session. That watcher is a child of the transient unit, and `setsid` severs the session but **not** the cgroup, so the rule pins `KillMode=process`: the default `control-group` would SIGTERM the whole cgroup — watcher included — the moment `su` exits, silently dropping every `STARTING`/`LIVE` notification. The transient unit then lingers `inactive` for as long as the watcher runs. ### Signal Handling diff --git a/fiero-hotspot.sh b/fiero-hotspot.sh index 049e52e..518ea43 100644 --- a/fiero-hotspot.sh +++ b/fiero-hotspot.sh @@ -19,7 +19,7 @@ set -u PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" export PATH -VERSION="2.1.2" +VERSION="2.1.3" CONFIG_FILE="/etc/fiero-hotspot.conf" LOCK_FILE="/run/fiero-hotspot.lock" diff --git a/tests/install.bats b/tests/install.bats index 314ab79..9b81033 100644 --- a/tests/install.bats +++ b/tests/install.bats @@ -40,6 +40,16 @@ install_as_tester() { [ -x /usr/local/bin/fiero-hotspot ] } +@test "the udev rule keeps the event watcher alive past the transient unit" { + # systemd-run builds a transient service unit whose default KillMode is + # control-group, so the setsid'd fiero-prompt watch child is SIGTERMed with + # the unit. The rule must pin KillMode=process or STARTING/LIVE + # notifications never arrive. + run install_as_tester "$ANSWERS" + [ "$status" -eq 0 ] + grep -q -- '--property=KillMode=process' /etc/udev/rules.d/99-fiero-hotspot.rules +} + @test "answering no to both timeout questions disables both fallbacks" { run install_as_tester 'My Hotspot\ngoodpass123\ngoodpass123\n\nn\nn\n' [ "$status" -eq 0 ]