diff --git a/README.md b/README.md index 09f42be..037ae79 100644 --- a/README.md +++ b/README.md @@ -7,11 +7,13 @@ update-notify runs on a systemd user timer and checks: - **Repo updates**, using `checkupdates` from pacman-contrib. - **AUR updates** for packages in a local aurutils repository (`aur repo -u`). - **Custom kernel rebuilds**: when a stock kernel (e.g. `linux-lts`) has a newer version than your custom build of it (e.g. `linux-lts-custom`). +- **Maintenance**: `.pacnew`/`.pacsave` files to merge (`pacdiff -o`), and a reboot after the running kernel was upgraded. It sends one notification, and only when its content changes since the last one: - Update counts are shown once the total number of updates reaches a threshold (default 30). Reminders for `git-monitor` and pending firmware updates (`fwupdmgr`) are added when those tools are installed. - Kernel rebuild reminders are always shown, even below the threshold. +- Maintenance reminders are added to every notification. On their own, they notify only when something new appears (a new file to merge, a new kernel to reboot into), so a `.pacnew` you keep isn't repeated. ## Requirements @@ -79,6 +81,8 @@ Settings are read from `$XDG_CONFIG_HOME/update-notify/config` (default `~/.conf - `THRESHOLD`: minimum number of updates before update counts are shown (default 30). - `AUR_REPO`: name of the local aurutils repository (default `aur-local`). - `KERNELS`: maps each stock kernel package to its custom build (default: `linux`, `linux-lts` and `linux-zen` to `-custom`). Kernels where either package isn't installed are skipped. Set `KERNELS=()` to disable the kernel check. +- `PACNEW_CHECK`: set to `0` to disable the `.pacnew`/`.pacsave` reminder (default `1`). +- `REBOOT_CHECK`: set to `0` to disable the reboot reminder (default `1`). A reboot is needed when the running kernel's `/usr/lib/modules//vmlinuz` is gone. A commented sample config is installed with the package. To start from it: @@ -100,9 +104,11 @@ threshold repo aur kernel +pacnew +reboot ``` -The file reflects the last timer or `update-notify.path` run. To keep a bar current in between, e.g. right after an upgrade, drop entries whose installed version (`pacman -Q`) is no longer older than `newver`, or, for `kernel` lines, whose custom build has caught up with `stock-ver` (ignoring `pkgrel`). This only reads local data, so it's cheap to run often. +The file reflects the last timer or `update-notify.path` run. To keep a bar current in between, e.g. right after an upgrade, drop entries whose installed version (`pacman -Q`) is no longer older than `newver`; for `kernel` lines, whose custom build has caught up with `stock-ver` (ignoring `pkgrel`); for `pacnew` lines, whose file is gone; for `reboot` lines, whose version is no longer `uname -r`. This only reads local data, so it's cheap to run often. ## License diff --git a/config.example b/config.example index 0558b4e..f6c3fbc 100644 --- a/config.example +++ b/config.example @@ -17,3 +17,9 @@ # [linux-lts]="linux-lts-custom" # [linux-zen]="linux-zen-custom" #) + +# Remind about .pacnew/.pacsave files to merge with pacdiff (0 to disable). +#PACNEW_CHECK=1 + +# Remind to reboot after the running kernel was upgraded (0 to disable). +#REBOOT_CHECK=1 diff --git a/update-notify b/update-notify index 449f343..a33d32f 100755 --- a/update-notify +++ b/update-notify @@ -12,6 +12,8 @@ declare -A KERNELS=( [linux-lts]="linux-lts-custom" [linux-zen]="linux-zen-custom" ) +PACNEW_CHECK=1 # remind about .pacnew/.pacsave files (0 to disable) +REBOOT_CHECK=1 # remind to reboot after a kernel upgrade (0 to disable) config_file="${XDG_CONFIG_HOME:-$HOME/.config}/update-notify/config" state_dir="${XDG_STATE_HOME:-$HOME/.local/state}/update-notify" @@ -42,27 +44,6 @@ export DBUS_SESSION_BUS_ADDRESS="${DBUS_SESSION_BUS_ADDRESS:-unix:path=/run/user # Helpers # --------------------------------------------------------------------------- -# Send a notification only if its body changed since last time for this key. -notify_once() { - local key=$1 title=$2 body=$3 - local file="$state_dir/$key" - if [[ -f $file && $(<"$file") == "$body" ]]; then - return 0 - fi - if (( quiet )); then - # Track changes to a shown notification, so the timer doesn't re-notify them; - # leave a new one for the timer to show - [[ -f $file ]] && printf '%s' "$body" > "$file" - return 0 - fi - notify-send "$title" "$body" && printf '%s' "$body" > "$file" -} - -# Forget a key so the next occurrence notifies again. -clear_state() { - rm -f "$state_dir/$1" -} - # If $1 has a pending update, print its new version and return 0. # checkupdates lines look like: "pkgname oldver -> newver" pending_version() { @@ -142,6 +123,26 @@ for stock_pkg in "${!KERNELS[@]}"; do fi done +# --------------------------------------------------------------------------- +# Maintenance: config files to merge, reboot after a kernel upgrade. +# maint_items are tracked in the state and the pending file, maint_lines are shown. +# --------------------------------------------------------------------------- +maint_items=() +maint_lines=() +if [[ $PACNEW_CHECK != 0 ]]; then + mapfile -t pacnew_files < <(pacdiff -o 2>/dev/null) + if (( ${#pacnew_files[@]} )); then + maint_items+=("${pacnew_files[@]/#/pacnew }") + maint_lines+=("Config files to merge with pacdiff: ${#pacnew_files[@]}") + fi +fi +running_kernel=$(uname -r) +# vmlinuz belongs to the kernel package, while DKMS files can keep the folder around +if [[ $REBOOT_CHECK != 0 && ! -e /usr/lib/modules/$running_kernel/vmlinuz ]]; then + maint_items+=("reboot $running_kernel") + maint_lines+=("Reboot to load the new kernel (running $running_kernel)") +fi + # --------------------------------------------------------------------------- # Pending list for status bars, one " " per line # --------------------------------------------------------------------------- @@ -158,6 +159,9 @@ done for item in "${kernel_pending[@]}"; do printf 'kernel %s\n' "$item" done + for item in "${maint_items[@]}"; do + printf '%s\n' "$item" + done } > "$state_dir/pending.tmp" && mv "$state_dir/pending.tmp" "$state_dir/pending" # readers never see a partial file # --------------------------------------------------------------------------- @@ -197,11 +201,66 @@ if (( over_threshold )); then fi fi +# --------------------------------------------------------------------------- +# Notify when the update part changed since the last notification (maintenance +# lines included), or for maintenance alone when it has items not shown before +# --------------------------------------------------------------------------- +main_file="$state_dir/updates" +main_body="" +main_changed=0 if (( ${#parts[@]} )); then + printf -v main_body '%s\n' "${parts[@]}" + main_body=${main_body%$'\n'} + [[ -f $main_file && $(<"$main_file") == "$main_body" ]] || main_changed=1 +else + rm -f "$main_file" # so the next occurrence notifies again +fi + +maint_file="$state_dir/maintenance" +declare -A maint_seen=() +if [[ -f $maint_file ]]; then + while IFS= read -r item; do + maint_seen["$item"]=1 + done < "$maint_file" +fi +maint_new=0 +maint_kept=() # current items already shown +for item in "${maint_items[@]}"; do + if [[ -n ${maint_seen["$item"]:-} ]]; then + maint_kept+=("$item") + else + maint_new=1 + fi +done + +lines=() +if (( main_changed )); then + lines=("${parts[@]}" "${maint_lines[@]}") title="Updates available" (( over_threshold )) || title="Kernel update available" - printf -v body '%s\n' "${parts[@]}" - notify_once "updates" "$title" "${body%$'\n'}" -else - clear_state "updates" +elif (( maint_new )); then + lines=("${maint_lines[@]}") + title="System maintenance" +fi + +shown=0 +if (( ${#lines[@]} )); then + printf -v body '%s\n' "${lines[@]}" + if (( quiet )); then + # Track changes to a shown notification, so the timer doesn't re-notify them; + # leave a new one for the timer to show + (( main_changed )) && [[ -f $main_file ]] && printf '%s' "$main_body" > "$main_file" + elif notify-send "$title" "${body%$'\n'}"; then + shown=1 + [[ -n $main_body ]] && printf '%s' "$main_body" > "$main_file" + fi +fi + +# Remember the maintenance items shown: drop resolved ones, keep new ones for the next notification +maint_save=("${maint_kept[@]}") +(( shown )) && maint_save=("${maint_items[@]}") +if (( ${#maint_save[@]} )); then + printf '%s\n' "${maint_save[@]}" > "$maint_file" +else + rm -f "$maint_file" fi