manager: Fix loosing iface options on CARRIER
When an interface (re-)gains carrier dhcpcd_handlecarrier() runs dhcpcd_initstate() to kick off profile re-selection. Previously this used args originally passed when starting the manager (ctx->argv).
However interfaces started via the manager control interface (dhcpcd_initstate1() in dhcpcd_handleargs()) may be started with different args.
For example if we start a manager with
dhcpcd -M --inactive
and then start only IPv4 on an interface with
dhcpcd -4 iface0
a subsequent CARRIER event will reset the interface to what amounts to "default config + -M --inactive" which in this case will enable ipv6 also!
To fix this we keep a copy of the arguments used to start an interface in the manager (dhcpcd_handleargs()) code path around around (ifp->argv).
In the current implementation args passed during for renew following the initial interface start will not be persisted. This causes the interface to reset to a state of "defaults + config + profile + start-cmdline".
For example (continuing the scenario above) after enabling ipv6 with -n:
$ dhcpcd -6 -n iface0
A subsequent CARRIER event will disable ipv6 again as the effective arguments remain -4 iface0 as passed during interface start.
Note the per-interface daemon code path wasn't affected as ctx->args already contains the interface start args.
Walkthrough
Adds per-interface argc/argv to struct interface, implements copy_argv()/free_argv_copy(), propagates control-command argv into discovered/activated interfaces during reconf, prefers per-interface argv during init, and frees copied argv on interface teardown. reconf_reboot signature changed.
Changes
| Cohort / File(s) | Summary |
|---|---|
Interface struct & API src/dhcpcd.h, src/if-options.h |
Added int argc; and char **argv; to struct interface. Declared char **copy_argv(int argc, char **argv) and void free_argv_copy(char **argv). |
Argv copy/free utilities src/if-options.c |
Added copy_argv() to allocate a contiguous argv copy with a magic sentinel and free_argv_copy() to validate and free it; added assert.h and ARGV_COPY_MAGIC. Note: the ARGV_COPY_MAGIC/copy/free logic appears duplicated within the file. |
Init / reconf behavior src/dhcpcd.c |
dhcpcd_initstate now prefers per-interface argv when present. reconf_reboot signature changed to reconf_reboot(struct dhcpcd_ctx *ctx, const int reboot_else_applyaddr, const int argc, char **argv, const int first_iface_arg). New logic detects per-interface argv, checks whether argv references specific interfaces or all, copies control-command argv into newly discovered ifp->argv/ifp->argc when activating interfaces, and either reboots, applies IPv4 addresses, or runs preinit/start using the appropriate argv based on flags and interface state. Calls and comments updated accordingly. |
Interface teardown src/if.c |
if_free() now calls free_argv_copy(ifp->argv) when ifp->argv is non-NULL to release copied per-interface argv before freeing the interface. |
Sequence Diagram(s)
sequenceDiagram
participant Control as Control process
participant Reconf as reconf_reboot
participant Iface as Interface (ifp)
participant Init as preinit/start
Note over Control,Reconf: Control command invokes reconf with argc/argv
Control->>Reconf: reconf_reboot(reboot_else_applyaddr, argc, argv, first_iface_arg)
alt ifp already has per-interface argv
Reconf->>Iface: use existing ifp->argc/ifp->argv
Reconf->>Init: call preinit/start using ifp->argv
else interface referenced but not active
Reconf->>Iface: copy_argv(argc, argv)
Iface-->>Reconf: store copied argv in ifp->argv/ifp->argc
Reconf->>Init: activate interface -> preinit/start using ifp->argv
else interface active but action is apply-address
Reconf->>Init: apply IPv4 address (no reboot) using context/main argv as appropriate
end
Init->>Iface: bring up/apply addresses
Note right of Iface: on teardown -> free_argv_copy(ifp->argv)
Estimated code review effort
🎯 3 (Moderate) | ⏱️ ~25 minutes
- Review focus:
- Consolidate duplicate
ARGV_COPY_MAGIC/copy_argv/free_argv_copyimplementations insrc/if-options.c. - Verify all init/reconf call paths consistently prefer per-interface argv where intended.
- Confirm memory management for copied argv across error paths and during teardown (
if_free()and reconf error paths). - Validate
copy_argv()for allocation size, truncation handling, null-termination, alignment, and portability.
- Consolidate duplicate
Pre-merge checks and finishing touches
❌ Failed checks (1 warning)
| Check name | Status | Explanation | Resolution |
|---|---|---|---|
| Docstring Coverage | ⚠️ Warning | Docstring coverage is 7.69% which is insufficient. The required threshold is 80.00%. | You can run @coderabbitai generate docstrings to improve docstring coverage. |
✅ Passed checks (2 passed)
| Check name | Status | Explanation |
|---|---|---|
| Title check | ✅ Passed | The title accurately summarizes the main change: fixing the loss of interface options when a CARRIER event occurs, which is the core issue addressed throughout the changeset. |
| Description check | ✅ Passed | The description is comprehensive and directly related to the changeset, explaining the problem scenario, the root cause, and how the solution (per-interface argv copying) addresses it. |
✨ Finishing touches
- [ ] 📝 Generate docstrings
🧪 Generate unit tests (beta)
- [ ] Create PR with unit tests
- [ ] Post copyable unit tests in a comment
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
Comment @coderabbitai help to get the list of available commands and usage tips.
@DanielG Just to double-check, is this what you attached to this Debian bug?
@rsmarples, is @DanielG 's response to your change requests adequate?