Skip to content

fix(avahi): avoid restart on transient iface loss#2660

Closed
elibosley wants to merge 1 commit into
masterfrom
codex/avahi-update-preserve-running
Closed

fix(avahi): avoid restart on transient iface loss#2660
elibosley wants to merge 1 commit into
masterfrom
codex/avahi-update-preserve-running

Conversation

@elibosley

@elibosley elibosley commented Jun 8, 2026

Copy link
Copy Markdown
Member

Summary

Prevent network-hook service reloads from restarting Avahi when an allowed interface is only temporarily unavailable, while preserving forced restarts for explicit Network Extra configuration changes.

What Changed

  • Added a compact bind-list check in rc.avahidaemon to distinguish meaningful runtime binding changes from transient interface loss.
  • update skips restart when the only runtime difference is a previously allowed interface that is currently inactive.
  • Network Extra settings applies now call update_services 1 force through update.php args.
  • update_services whitelists and carries the optional force mode into the delayed reload_services job.
  • reload_services passes force only to rc.avahidaemon update, leaving other service update calls unchanged.
  • rc.avahidaemon update force preserves the old restart-on-bind-mismatch behavior for explicit user config applies.

Why

Avahi already observes interface loss and recovery while it remains running. Restarting it during a temporary carrier/DHCP outage can start the daemon before any suitable interface is available, causing Avahi to exit and remain stopped.

The earlier heuristic alone was not sufficient because it could not distinguish transient interface loss from a user intentionally removing an interface in Network Extra while that interface was inactive. Passing force from the config-apply path makes that intent explicit.

Relationship To Other PR

Alternative to #2661.

  • This PR tries to prevent the early restart during transient interface loss.
  • fix(avahi): start stopped daemon on update #2661 fixes recovery by starting Avahi if update finds it stopped.
  • This PR is more complete prevention, but it touches more call-chain surface.

Verification

  • bash -n etc/rc.d/rc.avahidaemon emhttp/plugins/dynamix/scripts/update_services emhttp/plugins/dynamix/scripts/reload_services
  • Manual shell harness for forced/unforced bind_changed cases:
    • skips restart for runtime transient wlan0 loss
    • restarts for explicit config removal while wlan0 is inactive
    • restarts for runtime active-interface removal
    • restarts for runtime active-interface addition
    • skips restart when the bind list is unchanged
  • git diff --check

Review Notes

  • No UI behavior changed beyond passing command args with the existing update.php mechanism.
  • The restart decision remains in rc.avahidaemon, which owns Avahi restart behavior.
  • The queued mode is whitelisted to force before being embedded in the delayed service reload command.
  • Residual risk: this is shell-script behavior without an existing repo test harness, so verification is syntax plus targeted decision-logic checks.

@coderabbitai

coderabbitai Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 4ddd2cb0-dbb6-40bc-94f6-eb5111484ed8

📥 Commits

Reviewing files that changed from the base of the PR and between 7b72a8b and 5e750d6.

📒 Files selected for processing (1)
  • etc/rc.d/rc.avahidaemon
🚧 Files skipped from review as they are similar to previous changes (1)
  • etc/rc.d/rc.avahidaemon

Walkthrough

Adds a new helper function bind_changed and updates avahid_update to early-return when this allow-interfaces equals $BIND; otherwise avahid_update calls bind_changed "$CURRENT" "$BIND" and only restarts Avahi when that helper reports a change requiring restart.

Changes

Avahi interface change optimization

Layer / File(s) Summary
bind_changed + conditional restart
etc/rc.d/rc.avahidaemon
Adds bind_changed which compares current vs desired allow-interfaces lists (string containment and checking interface presence). avahid_update now returns immediately when this allow-interfaces equals $BIND and calls avahid_restart only if bind_changed "$CURRENT" "$BIND" indicates a restart is required.

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested labels: 7.3

🐰
I sniffed the binds and took a hop,
Quiet daisy-net, no needless stop,
If lists match true, I'll skip the drum,
Else softly nudge Avahi to hum,
Hooray — fewer jumps, more carrot crumb.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title concisely summarizes the main change: preventing unnecessary Avahi restarts when interfaces are transiently unavailable, which aligns with the changeset's core objective.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/avahi-update-preserve-running

Warning

Review ran into problems

🔥 Problems

Stopped waiting for pipeline failures after 30000ms. One of your pipelines takes longer than our 30000ms fetch window to run, so review may not consider pipeline-failure results for inline comments if any failures occurred after the fetch window. Increase the timeout if you want to wait longer or run a @coderabbit review after the pipeline has finished.


Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented Jun 8, 2026

Copy link
Copy Markdown

🔧 PR Test Plugin Available

A test plugin has been generated for this PR that includes the modified files.

Version: 2026.06.08.2009
Build: View Workflow Run

📥 Installation Instructions:

Install via Unraid Web UI:

  1. Go to Plugins → Install Plugin
  2. Copy and paste this URL:
https://preview.dl.unraid.net/pr-plugins/pr-2660/webgui-pr-2660.plg
  1. Click Install

Alternative: Direct Download

⚠️ Important Notes:

  • Testing only: This plugin is for testing PR changes
  • Backup included: Original files are automatically backed up
  • Easy removal: Files are restored when plugin is removed
  • Conflicts: Remove this plugin before installing production updates
  • Post-merge behavior: This preview stays available after merge until preview storage expires or it is manually cleaned up

📝 Modified Files:

Click to expand file list
emhttp/plugins/dynamix/NetworkExtra.page
emhttp/plugins/dynamix/scripts/reload_services
emhttp/plugins/dynamix/scripts/update_services
etc/rc.d/rc.avahidaemon

🔄 To Remove:

Navigate to Plugins → Installed Plugins and remove webgui-pr-2660, or run:

plugin remove webgui-pr-2660

🤖 This comment is automatically generated and will be updated with each new push to this PR.

@elibosley elibosley force-pushed the codex/avahi-update-preserve-running branch from 0da49e3 to 7b72a8b Compare June 8, 2026 19:58

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
etc/rc.d/rc.avahidaemon (1)

134-144: ⚡ Quick win

Consider adding an iface_active check for defensive programming.

Currently, bind_adds_active_iface assumes that all interfaces in $NEXT (which is $BIND) are active, relying on the contract that BIND is populated only with active interfaces by rc.library.source.

However, bind_removes_active_iface (lines 146-156) explicitly verifies that removed interfaces are active using iface_active. For consistency and defensive programming, consider adding the same check here:

♻️ Proposed change to verify added interfaces are active
 bind_adds_active_iface(){
   local CURRENT="$1" NEXT="$2" IFACE
   IFS=, read -ra IFACES <<< "$NEXT"
   for IFACE in "${IFACES[@]}"; do
     [[ -n $IFACE ]] || continue
-    if ! csv_has "$CURRENT" "$IFACE"; then
+    if ! csv_has "$CURRENT" "$IFACE" && iface_active "$IFACE"; then
       return 0
     fi
   done
   return 1
 }

This makes the function more robust against potential races or bugs in the BIND population logic, and maintains symmetry with bind_removes_active_iface.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@etc/rc.d/rc.avahidaemon` around lines 134 - 144, The function
bind_adds_active_iface should mirror bind_removes_active_iface by checking that
each candidate interface in NEXT is actually active before treating it as an
added interface; update bind_adds_active_iface to call iface_active "$IFACE"
(and skip non-active interfaces) inside the loop, and only return 0 when an
iface is both not present in CURRENT (csv_has "$CURRENT" "$IFACE" is false) and
iface_active reports true, otherwise continue and return 1 at the end.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@etc/rc.d/rc.avahidaemon`:
- Around line 134-144: The function bind_adds_active_iface should mirror
bind_removes_active_iface by checking that each candidate interface in NEXT is
actually active before treating it as an added interface; update
bind_adds_active_iface to call iface_active "$IFACE" (and skip non-active
interfaces) inside the loop, and only return 0 when an iface is both not present
in CURRENT (csv_has "$CURRENT" "$IFACE" is false) and iface_active reports true,
otherwise continue and return 1 at the end.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: eee1429a-4590-4b35-ad3a-da9244aacc72

📥 Commits

Reviewing files that changed from the base of the PR and between 982b3c2 and 0da49e3.

📒 Files selected for processing (1)
  • etc/rc.d/rc.avahidaemon

@elibosley elibosley force-pushed the codex/avahi-update-preserve-running branch from 7b72a8b to 5e750d6 Compare June 8, 2026 20:03

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@etc/rc.d/rc.avahidaemon`:
- Around line 122-131: bind_changed currently treats an empty show dev output as
"inactive" by using -z $(show dev "$IFACE"); update bind_changed to explicitly
interpret the semantics of show dev (as defined in rc.library.source) instead of
relying on emptiness — locate the second for-loop in bind_changed and replace
the -z $(show dev "$IFACE") test with a check that parses show dev "$IFACE"
output for the interface state (e.g., test for "up"/"active" or absence of "up")
so the condition truly reflects an inactive/transiently-down interface according
to show()'s output format.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 68f8aeb7-56e3-45ec-851b-59a727812d83

📥 Commits

Reviewing files that changed from the base of the PR and between 0da49e3 and 7b72a8b.

📒 Files selected for processing (1)
  • etc/rc.d/rc.avahidaemon

Comment thread etc/rc.d/rc.avahidaemon
Comment on lines +122 to +131
bind_changed(){
local CURRENT=",$1," NEXT=",$2," IFACE
for IFACE in ${2//,/ }; do
[[ $CURRENT == *",$IFACE,"* ]] || return 0
done
for IFACE in ${1//,/ }; do
[[ $NEXT == *",$IFACE,"* || -z $(show dev "$IFACE") ]] || return 0
done
return 1
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Verify the 'show' command exists and check its usage with 'dev'

# Check if 'show' is a function or executable
type show 2>/dev/null || echo "show command not found in PATH"

# Search for 'show' function definition in rc scripts
rg -n "^show\s*\(\)" --type=sh --glob='etc/rc.d/*'

# Search for 'show dev' usage patterns
rg -n -C3 "show dev" --type=sh --glob='etc/rc.d/*'

Repository: unraid/webgui

Length of output: 3385


show dev is provided by rc.library.source; align semantics with inactivity checks
etc/rc.d/rc.library.source defines show() (around line 57) and relies on show dev output in -n/-z conditionals (e.g., around line 97 and line 120), so the earlier “command not defined” concern is just an artifact of not sourcing the library in isolation. The key remaining alignment is that show dev <iface> returning empty (which bind_changed tests via -z $(show dev "$IFACE")) truly means the interface is inactive/transiently down—not merely that it currently has no addresses.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@etc/rc.d/rc.avahidaemon` around lines 122 - 131, bind_changed currently
treats an empty show dev output as "inactive" by using -z $(show dev "$IFACE");
update bind_changed to explicitly interpret the semantics of show dev (as
defined in rc.library.source) instead of relying on emptiness — locate the
second for-loop in bind_changed and replace the -z $(show dev "$IFACE") test
with a check that parses show dev "$IFACE" output for the interface state (e.g.,
test for "up"/"active" or absence of "up") so the condition truly reflects an
inactive/transiently-down interface according to show()'s output format.

Purpose of the change:
- Keep Avahi running when an allowed interface temporarily loses carrier or its address.
- Preserve explicit user configuration applies as forced Avahi binding updates.

Before this change:
- rc.avahidaemon update recomputed active bindings and restarted Avahi whenever the active bind list differed from allow-interfaces.
- DHCP/network hooks could therefore restart Avahi while a wireless interface was still down.
- A purely heuristic fix could not distinguish transient interface loss from a user applying Network Extra changes.

Why that was a problem:
- Avahi already observes interface loss and recovery while running.
- Restarting during a temporary outage can start Avahi with no suitable network protocol, causing it to exit and remain stopped.
- Explicit Network Extra changes still need to force the legacy restart path when the Avahi bind list changes.

What the new change accomplishes:
- Avoids restarting Avahi from ordinary network-hook updates when the only bind difference is an allowed interface that is currently inactive.
- Passes a force mode from Network Extra applies so intentional include/exclude changes retain the old restart behavior.
- Whitelists the queued service reload mode before passing it through the delayed at job.

How it works:
- Network Extra calls update_services with a force argument.
- update_services carries the whitelisted mode into reload_services.
- reload_services passes force only to rc.avahidaemon update.
- rc.avahidaemon update restarts immediately for forced config applies, and otherwise uses bind_changed to ignore transient inactive-interface shrinkage.
@elibosley elibosley force-pushed the codex/avahi-update-preserve-running branch from 5e750d6 to 81847c1 Compare June 8, 2026 20:09
@elibosley elibosley closed this Jun 9, 2026
@github-actions

github-actions Bot commented Jun 9, 2026

Copy link
Copy Markdown

🧹 PR Test Plugin Cleaned Up

The test plugin and associated files for this PR have been removed from the preview environment.


🤖 This comment is automatically generated when a PR is closed without merging.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant