Skip to content

Add support for Darwin / macOS#2

Merged
rsmarples merged 30 commits intomasterfrom
Darwin
Apr 7, 2026
Merged

Add support for Darwin / macOS#2
rsmarples merged 30 commits intomasterfrom
Darwin

Conversation

@rsmarples
Copy link
Copy Markdown
Member

SOCK_SEQPACKET is not supported for socketpair, so use SOCK_STREAM. Rename svc_* to srv_* so we don't conflict with svc_run RPC command. Detect setproctitle and supply compat for reallocarray.

Fixes #1.

SOCK_SEQPACKET is not supported for socketpair, so use SOCK_STREAM.
Rename svc_* to srv_* so we don't conflict with svc_run RPC command.
Detect setproctitle and supply compat for reallocarray.

Fixes #1.
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 6, 2026

Walkthrough

Renamed service API from svc_* to srv_, added xsocketpair/xsetfd socket-flag handling, converted IPC to two-phase recv/send, added reallocarray compatibility, broadened Darwin/macOS support and CI, adjusted plugin build rules, and migrated plugins/unpriv consumers to srv_ APIs.

Changes

Cohort / File(s) Summary
Build & CI
/.gitignore, config-null.mk, configure, .github/workflows/build.yml, src/plugins/Makefile
Updated .gitignore (ignore *.dSYM/**, changed *.So*.soo pattern), removed dhcpcd-embedded.c from null config, expanded configure cleanup globs and added Darwin LDFLAGS_SO, added macOS CI job and updated checkout action, and adjusted plugins Makefile build/link rules.
Socket helpers
src/common.c, src/common.h
Added helper xsetfd() to set FD flags post-creation, refactored xsocket() to use it and fail-clean on error, and added exported xsocketpair(int,int,int,int[2]) that emulates SOCK_CLOEXEC/SOCK_NONBLOCK when unsupported.
Service API & runtime
src/service.h, src/service.c, src/plugin.h, src/dhcpsd.h, src/dhcpsd.c
Renamed public API/types/macros from svc_*srv_* (types, functions, callbacks); switched IPC sockets to `xsocketpair(PF_LOCAL, SOCK_STREAM
Unprivileged helper
src/unpriv.h, src/unpriv.c
Migrated unpriv helper to srv_* API (unpriv_init returns struct srv_ctx *; unpriv_getaddrinfo and dispatch use struct srv_ctx * and srv_run/srv_send); adjusted proctitle guard to HAVE_SETPROCTITLE.
Plugins & consumers
src/plugins/ethers.c, src/plugins/lua.c, src/plugins/Makefile
Updated plugin callbacks and run/send calls from svc_*srv_* and context types to struct srv_ctx *; Lua plugin: length-prefixed hostname parsing, underrun checks, memmove realign, explicit integer casts for DHCP options, and lookup command change; ethers updated dispatch/signature and send call.
Event loop / platform
src/eloop.c, configure
Enabled kqueue backend on Apple (__APPLE__), added macOS-specific kqueue open path and linker flags; switched indefinite wait from ppoll() to poll() and adjusted forked-fd handling.
Network interface handling
src/if.c
Relaxed sockaddr pointer const qualifiers for AF_LINK/AF_PACKET and simplified hardware-address memcpy logic.
Compatibility
compat/reallocarray.c, compat/reallocarray.h
Added reallocarray() implementation and header with overflow detection (returns NULL and sets errno on overflow), delegating to realloc otherwise.
Headers / prototypes
src/dhcpsd.h, src/plugin.h, src/service.h, src/unpriv.h, src/common.h
Updated forward declarations and prototypes to replace svc_ctx with srv_ctx; added prototype for xsocketpair.

Sequence Diagram(s)

sequenceDiagram
    participant Plugin
    participant Service as srv_ctx
    participant Daemon as dhcpsd
    participant Unpriv as unpriv_helper
    participant Kernel

    Note over Daemon,Kernel: IPC socket creation
    Daemon->>Kernel: xsocketpair(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC)
    Kernel-->>Daemon: fd[0], fd[1]
    Daemon->>Service: srv_init(..., fd)

    Note over Plugin,Service: Request handling
    Plugin->>Service: srv_run(request)
    Service->>Kernel: sendmsg(fd, header)  -- (first phase)
    Kernel-->>Service: ack/header consumed
    Service->>Kernel: recvmsg(fd, payload) -- (second phase, if sc_datalen)
    Kernel-->>Service: payload bytes
    Service->>Plugin: dispatch callback (srv_ctx)

    Note over Service,Unpriv: Offload lookup
    Service->>Unpriv: srv_run(U_GETADDRINFO)
    Unpriv->>Kernel: perform lookup
    Unpriv-->>Service: srv_send(response)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.35% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add support for Darwin / macOS' directly and clearly summarizes the main objective of this changeset.
Description check ✅ Passed The description accurately relates to the changeset, mentioning key technical changes like SOCK_STREAM replacement, svc_* to srv_* renaming, setproctitle detection, and reallocarray compatibility.
Linked Issues check ✅ Passed The PR successfully addresses issue #1 by adding macOS/Darwin support through platform-specific changes including socket handling, symbol renaming, and compatibility implementations.
Out of Scope Changes check ✅ Passed All changes are directly related to macOS/Darwin support: platform-specific socket fixes, symbol renaming to avoid RPC conflicts, setproctitle detection, reallocarray compatibility, and updated CI/build configuration.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch Darwin

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.

❤️ Share

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

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/eloop.c (1)

628-639: ⚠️ Potential issue | 🟠 Major

Remove Apple-specific kqueue() branch to ensure FD_CLOEXEC is set on macOS.

The __APPLE__ branch at line 628–629 bypasses the close-on-exec flag setup that other BSD systems apply. Since HAVE_KQUEUE1 is only defined for NetBSD and KQUEUE_CLOEXEC is not used anywhere, macOS will always take the Apple special case and skip the fcntl FD_CLOEXEC handling in the generic branch. This allows the kqueue fd to be inherited across exec* calls.

Removing the Apple special case lets macOS fall through to the standard fcntl-based setup (lines 630–639), which properly sets FD_CLOEXEC on all systems.

🔧 Proposed fix
 `#if` defined(HAVE_KQUEUE1)
 	fd = kqueue1(O_CLOEXEC);
 `#elif` defined(KQUEUE_CLOEXEC)
 	fd = kqueuex(KQUEUE_CLOEXEC);
-#elif defined(USE_KQUEUE) && defined(__APPLE__)
-	fd = kqueue();
 `#elif` defined(USE_KQUEUE)
 	int flags;
 
 	fd = kqueue();
+	if (fd == -1)
+		return -1;
 	flags = fcntl(fd, F_GETFD, 0);
 	if (!(flags != -1 && !(flags & FD_CLOEXEC) &&
 		fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == 0)) {
 		close(fd);
 		return -1;
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/eloop.c` around lines 628 - 639, The Apple-specific kqueue branch
bypasses the FD_CLOEXEC setup so kqueue() on macOS can inherit across exec;
remove the conditional that returns just fd when __APPLE__ is defined so macOS
falls through to the generic USE_KQUEUE path that runs
fcntl(F_GETFD)/fcntl(F_SETFD, flags|FD_CLOEXEC). Edit the src/eloop.c branch
that currently does "elif defined(USE_KQUEUE) && defined(__APPLE__)" to
eliminate that special-case and ensure kqueue(), flags, fcntl, FD_CLOEXEC,
close(fd) and the return -1 logic from the generic USE_KQUEUE block are applied
on macOS (respecting HAVE_KQUEUE1 / KQUEUE_CLOEXEC semantics elsewhere).
src/dhcpsd.h (1)

38-50: ⚠️ Potential issue | 🟠 Major

The old svc_run symbol still leaks through the service layer.

This header switches ctx_unpriv to struct srv_ctx *, but src/service.h Line 67 still declares svc_run(...) and src/service.c Lines 218-226 still define it. The rename is therefore incomplete, and the Darwin/macOS svc_run collision this PR is trying to eliminate is still exposed.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/dhcpsd.h` around lines 38 - 50, The svc_run symbol still leaks despite
switching ctx_unpriv to struct srv_ctx*, so remove or rename the public svc_run
declaration/definition in service.h and service.c to the new internal symbol
(e.g. srv_run) and update all callers to use the new name; ensure the prototype
in src/service.h no longer exposes svc_run and that src/service.c defines the
renamed function (or makes it static) so the OS-level svc_run collision is
eliminated, and verify uses that reference ctx_unpriv/srv_ctx are updated
accordingly.
src/unpriv.c (1)

47-48: ⚠️ Potential issue | 🟡 Minor

Duplicate macro definition.

U_GETADDRINFO is defined twice on consecutive lines. This appears to be a copy-paste error.

🔧 Suggested fix
 `#define` U_GETADDRINFO 1
-#define U_GETADDRINFO 1
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/unpriv.c` around lines 47 - 48, The file defines the macro U_GETADDRINFO
twice consecutively; remove the redundant duplicate so U_GETADDRINFO is only
defined once (i.e., delete one of the repeated "#define U_GETADDRINFO 1" lines)
to eliminate the duplicate macro definition.
🧹 Nitpick comments (1)
src/dhcpsd.c (1)

280-287: MSG_EOR has no effect on SOCK_STREAM sockets.

After switching from SOCK_SEQPACKET to SOCK_STREAM, the MSG_EOR flag in the send() call is meaningless. MSG_EOR is only meaningful for message-oriented protocols (like SOCK_SEQPACKET). While this won't cause failures, it's misleading.

🔧 Suggested fix
-	if (send(ctx->ctx_fork_fd, &exit_code, sizeof(exit_code), MSG_EOR) ==
+	if (send(ctx->ctx_fork_fd, &exit_code, sizeof(exit_code), 0) ==
 	    -1)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/dhcpsd.c` around lines 280 - 287, The send() call that uses MSG_EOR on
ctx->ctx_fork_fd is misleading because MSG_EOR is for message-oriented sockets;
change the flags argument to 0 (or remove MSG_EOR) in the send(ctx->ctx_fork_fd,
&exit_code, sizeof(exit_code), MSG_EOR) invocation so it uses no flags for the
SOCK_STREAM socket, and optionally add a short comment near this block
explaining that MSG_EOR is not applicable to SOCK_STREAM; keep the rest of the
flow (logerr on -1, close and reset ctx->ctx_fork_fd) unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/common.c`:
- Around line 464-490: The xsetfd function mixes descriptor flags and status
flags: keep handling FD_CLOEXEC via fcntl(F_GETFD)/F_SETFD and handle O_NONBLOCK
via a separate fcntl(F_GETFL)/F_SETFL call; specifically, when flags &
SOCK_CLOEXEC modify oflags obtained from F_GETFD and call F_SETFD, and when
flags & SOCK_NONBLOCK get/use a separate var from F_GETFL, OR in O_NONBLOCK and
call F_SETFL if changed (do both operations independently and return error if
either fcntl fails).

In `@src/plugins/lua.c`:
- Line 273: The reply is using the wrong message type constant: change the
srv_send call that currently sends L_LOOKUPADDR to send L_LOOKUPHOSTNAME instead
(the handler is serving hostname lookups); update every occurrence involved (the
srv_send invocation with parameters sctx, p, L_LOOKUPADDR, err, hname, hnamelen
and the corresponding caller sites around the L_LOOKUPHOSTNAME request, e.g.,
the calls near the current caller lines 883-884) so the on-the-wire response is
labeled L_LOOKUPHOSTNAME rather than L_LOOKUPADDR.

In `@src/plugins/Makefile`:
- Around line 28-35: The .SUFFIXES line currently resets suffixes to ".soo .so
.in" but the implicit .c.soo transformation (the .c.soo rule) depends on ".c"
being present, causing plugins like icmp, lua and others (auto, leasefile,
addrinfo, ethers) to lack build recipes; update the .SUFFIXES declaration to
include ".c" (i.e., add ".c" alongside ".soo .so .in") so the .c.soo implicit
rule and .soo.so rule will apply and those plugin targets can be built.

In `@src/service.c`:
- Around line 217-227: Remove the leftover debug log call from svc_run: the
loginfo("SVC RUN") inside svc_run should be deleted (or wrapped behind a
debug/verbose flag if intentional). Locate the svc_run function in service.c and
remove the loginfo line so the function simply prepares the iovec and returns
the srv_runv call; if you prefer conditional logging, guard the loginfo with an
existing debug/log-level check rather than leaving an unconditional info log.

---

Outside diff comments:
In `@src/dhcpsd.h`:
- Around line 38-50: The svc_run symbol still leaks despite switching ctx_unpriv
to struct srv_ctx*, so remove or rename the public svc_run
declaration/definition in service.h and service.c to the new internal symbol
(e.g. srv_run) and update all callers to use the new name; ensure the prototype
in src/service.h no longer exposes svc_run and that src/service.c defines the
renamed function (or makes it static) so the OS-level svc_run collision is
eliminated, and verify uses that reference ctx_unpriv/srv_ctx are updated
accordingly.

In `@src/eloop.c`:
- Around line 628-639: The Apple-specific kqueue branch bypasses the FD_CLOEXEC
setup so kqueue() on macOS can inherit across exec; remove the conditional that
returns just fd when __APPLE__ is defined so macOS falls through to the generic
USE_KQUEUE path that runs fcntl(F_GETFD)/fcntl(F_SETFD, flags|FD_CLOEXEC). Edit
the src/eloop.c branch that currently does "elif defined(USE_KQUEUE) &&
defined(__APPLE__)" to eliminate that special-case and ensure kqueue(), flags,
fcntl, FD_CLOEXEC, close(fd) and the return -1 logic from the generic USE_KQUEUE
block are applied on macOS (respecting HAVE_KQUEUE1 / KQUEUE_CLOEXEC semantics
elsewhere).

In `@src/unpriv.c`:
- Around line 47-48: The file defines the macro U_GETADDRINFO twice
consecutively; remove the redundant duplicate so U_GETADDRINFO is only defined
once (i.e., delete one of the repeated "#define U_GETADDRINFO 1" lines) to
eliminate the duplicate macro definition.

---

Nitpick comments:
In `@src/dhcpsd.c`:
- Around line 280-287: The send() call that uses MSG_EOR on ctx->ctx_fork_fd is
misleading because MSG_EOR is for message-oriented sockets; change the flags
argument to 0 (or remove MSG_EOR) in the send(ctx->ctx_fork_fd, &exit_code,
sizeof(exit_code), MSG_EOR) invocation so it uses no flags for the SOCK_STREAM
socket, and optionally add a short comment near this block explaining that
MSG_EOR is not applicable to SOCK_STREAM; keep the rest of the flow (logerr on
-1, close and reset ctx->ctx_fork_fd) unchanged.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 454d9b32-0630-43b1-b538-087e827325eb

📥 Commits

Reviewing files that changed from the base of the PR and between 810a040 and 4c10010.

📒 Files selected for processing (17)
  • .gitignore
  • config-null.mk
  • configure
  • src/common.c
  • src/common.h
  • src/dhcpsd.c
  • src/dhcpsd.h
  • src/eloop.c
  • src/if.c
  • src/plugin.h
  • src/plugins/Makefile
  • src/plugins/ethers.c
  • src/plugins/lua.c
  • src/service.c
  • src/service.h
  • src/unpriv.c
  • src/unpriv.h
💤 Files with no reviewable changes (1)
  • config-null.mk

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/plugins/lua.c (2)

286-290: ⚠️ Potential issue | 🔴 Critical

Validate payload length before header copy/shift in address lookup.

memcpy and memmove run before any len check. If len < sizeof(hostname), len - sizeof(hostname) underflows and can cause memory corruption.

Proposed fix
 	const char *addr;
 	char hostname[DHCP_HOSTNAME_LEN];
+	if (len < sizeof(hostname) + sizeof(struct bootp)) {
+		errno = EINVAL;
+		goto out;
+	}
 	memcpy(hostname, data, sizeof(hostname));
 	/* Aligns bootp */
 	memmove(UNCONST(data), (const char *)data + sizeof(hostname),
 	    len - sizeof(hostname));
 	len -= sizeof(hostname);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/plugins/lua.c` around lines 286 - 290, The code copies and shifts packet
data using memcpy(hostname, data, sizeof(hostname)) and memmove(UNCONST(data),
(const char *)data + sizeof(hostname), len - sizeof(hostname)) without
validating len, which can underflow when len < sizeof(hostname); before calling
memcpy or memmove in this address-lookup block, add a guard that checks if len
>= sizeof(hostname) and handle the error path (returning or logging) when the
check fails, only then perform memcpy(hostname, data, sizeof(hostname)) and
memmove with the corrected length calculation (len - sizeof(hostname)).

668-678: ⚠️ Potential issue | 🔴 Critical

Fix pointer-size payload validation bug in lease expiry path.

Line 676 validates against sizeof(dl) (pointer size), not the lease struct size. This can let undersized payloads pass and then dereference invalid memory.

Proposed fix
-	if (payloadlen < sizeof(dl)) {
+	if (payloadlen < sizeof(*dl)) {
 		errno = EINVAL;
 		goto out;
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/plugins/lua.c` around lines 668 - 678, The payload length check
incorrectly uses sizeof(dl) (size of a pointer) which allows undersized
payloads; update the validation to compare payloadlen against the actual lease
struct size (e.g., sizeof(*dl) or sizeof(struct dhcp_lease)) and ensure the same
check guards subsequent uses of dl (in the lease expiry path where dl,
dl->dl_clientid, dl->dl_addr are accessed).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@compat/reallocarray.c`:
- Line 56: Change the overflow errno assignment in reallocarray to use the
standard ENOMEM instead of EOVERFLOW: locate the overflow handling in the
reallocarray implementation (the branch that currently does "errno = EOVERFLOW")
and replace that assignment with "errno = ENOMEM" so callers receive the
expected libc errno on multiplication/size overflow; ensure the function name
reallocarray is referenced when making this change.
- Around line 55-56: The overflow guard in reallocarray uses SIZE_MAX / size
without ensuring size is non-zero, causing division-by-zero when size == 0;
update the conditional in the reallocarray implementation to only evaluate the
division when size != 0 (for example, add an explicit size != 0 check before
testing n > SIZE_MAX / size) so the expression becomes ((n | size) >=
SQRT_SIZE_MAX && size != 0 && n > SIZE_MAX / size), and keep the existing errno
= EOVERFLOW path; refer to the function reallocarray and variables n, size,
SQRT_SIZE_MAX, SIZE_MAX, and errno/EOVERFLOW when making the change.

---

Outside diff comments:
In `@src/plugins/lua.c`:
- Around line 286-290: The code copies and shifts packet data using
memcpy(hostname, data, sizeof(hostname)) and memmove(UNCONST(data), (const char
*)data + sizeof(hostname), len - sizeof(hostname)) without validating len, which
can underflow when len < sizeof(hostname); before calling memcpy or memmove in
this address-lookup block, add a guard that checks if len >= sizeof(hostname)
and handle the error path (returning or logging) when the check fails, only then
perform memcpy(hostname, data, sizeof(hostname)) and memmove with the corrected
length calculation (len - sizeof(hostname)).
- Around line 668-678: The payload length check incorrectly uses sizeof(dl)
(size of a pointer) which allows undersized payloads; update the validation to
compare payloadlen against the actual lease struct size (e.g., sizeof(*dl) or
sizeof(struct dhcp_lease)) and ensure the same check guards subsequent uses of
dl (in the lease expiry path where dl, dl->dl_clientid, dl->dl_addr are
accessed).
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: dbdb7018-0246-402a-a9ca-64b43bb8e6ca

📥 Commits

Reviewing files that changed from the base of the PR and between 4c10010 and 441de7a.

📒 Files selected for processing (3)
  • compat/reallocarray.c
  • compat/reallocarray.h
  • src/plugins/lua.c
✅ Files skipped from review due to trivial changes (1)
  • compat/reallocarray.h

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/plugins/lua.c`:
- Around line 298-310: The deserialized hostname is not validated: ensure
hostname_len is > 0 and does not exceed the destination buffer before memcpy
(reject and set errno/EINVAL and goto out if invalid), and after advancing
datap/len and memmoving the remaining data, check that len >= sizeof(struct
bootp) before assigning l->l_req and l->l_reqlen and before reading htype/hlen;
use the existing symbols hostname_len, hostname, datap, len,
memmove(UNCONST(data), ...), and l->l_req/l->l_reqlen to locate where to add
these checks and early returns.
- Around line 290-304: The parse-error branches jump to the out label before
sin, ltime, and the iov array are initialized, causing srv_sendv() to be called
with indeterminate iov contents; update the out-path to detect the parse failure
(check the error variable, e.g., err == -1) and in that case call srv_send()
with a NULL/empty payload instead of srv_sendv(), or only call srv_sendv() when
err != -1 and sin/ltime/iov have been properly initialized; reference the
existing variables and functions in this block (datap, len, hostname_len,
hostname, sin, ltime, iov, srv_send, srv_sendv, and the out label) and ensure
the control flow uses srv_send for error exits and srv_sendv only for the
successful parse path.

In `@src/service.c`:
- Around line 91-120: The code only assigns sr->sr_result and sr->sr_errno
inside the cmd.sc_datalen != 0 branch causing zero-length replies to keep stale
values; move the assignments of sr->sr_result = cmd.sc_result and sr->sr_errno =
cmd.sc_errno out of that conditional so they are always set from the incoming
header (keep the recvmsg and buffer resize logic inside the datalen check and
leave sr->sr_datalen/sr->sr_data handling as-is).
- Around line 163-164: The send path using sendmsg on sctx->srv_fd must handle
short writes: replace the single sendmsg call in service.c (the block that
returns ssize_t err) with a loop that tracks bytes_sent and repeatedly calls
sendmsg until the entire msghdr/iovec payload is transmitted or a fatal error
occurs; on a partial write update the msghdr->msg_iov and msg_iovlen (adjust
iov_base and iov_len) to account for the bytes already sent and continue, and on
-1 handle EINTR by retrying and treat EAGAIN by retrying (or returning a
retryable status if your I/O model requires); ensure srv_runv and srv_recv will
only see a completed message when the full payload has been sent. Refer to
sctx->srv_fd, sendmsg, the local msghdr/iovec usage, srv_runv, and srv_recv to
locate and update the code.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: da42ad20-6dd2-40fc-9682-bc636274dbb7

📥 Commits

Reviewing files that changed from the base of the PR and between 837d25f and dd14a87.

📒 Files selected for processing (4)
  • src/plugins/lua.c
  • src/service.c
  • src/service.h
  • src/unpriv.c
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/unpriv.c
  • src/service.h

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (2)
src/dhcpsd.c (1)

207-208: ⚠️ Potential issue | 🟠 Major

The launcher status channel still assumes packet semantics after the SOCK_STREAM switch.

Lines 207-208 change this channel to a byte stream, but dhcpsd_fork_cb() still does a single recv() of sizeof(exit_code) and Line 290 still does a single send() before closing. A short transfer now turns into a false truncated/hangup path and can report the wrong launcher/main status.

Also applies to: 290-291

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/dhcpsd.c` around lines 207 - 208, The launcher/main status channel was
changed to SOCK_STREAM but the code in dhcpsd_fork_cb() and the send path still
assumes single-packet semantics; update the logic that uses fork_fd and
exit_code so reads and writes loop until all sizeof(exit_code) bytes are
transferred (handle partial reads/writes, EINTR, EAGAIN and EOF) instead of
calling a single recv()/send(); specifically modify the receive side in
dhcpsd_fork_cb() to accumulate bytes into exit_code until complete and detect
EOF as a failure, and modify the send side (the code that writes exit_code to
fork_fd) to loop until all bytes are written before closing the FD. Ensure error
return paths and close behavior remain correct.
src/service.c (1)

170-171: ⚠️ Potential issue | 🔴 Critical

Loop until the full frame is written on SOCK_STREAM.

Line 170 does a single sendmsg(), and Line 192 only treats -1 as failure. On a stream socket, a positive short write is still a valid result, so truncated frames are currently accepted as success and the peer's next srv_recv() can desynchronize or block in MSG_WAITALL.

Also applies to: 192-194

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/service.c` around lines 170 - 171, The send logic currently does a single
sendmsg(sctx->srv_fd, &msg, 0) and treats only -1 as failure, allowing partial
writes on SOCK_STREAM to be accepted; change this to loop until the entire frame
(total_len derived from the iovec/msg) is written: call sendmsg repeatedly,
handle EINTR by retrying, handle EAGAIN/EWOULDBLOCK appropriately, and on a
positive return < remaining_bytes adjust the iovec/offset (or rebuild msg) and
subtract bytes_sent, continuing until remaining_bytes == 0; ensure you
update/inspect the same send path used around sendmsg (use the same msg/iov
references) and return an error only for true failures (<0 after retries), not
for short writes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/service.c`:
- Around line 130-134: srv_recvl currently ignores the new ssize_t return of
srv_recv; capture the return value (ssize_t ret = srv_recv(arg, e)) and if ret <
0 treat it as an error: perform connection cleanup and stop further callbacks
(e.g., call the existing connection teardown routine such as srv_close(arg) or
otherwise close/free the socket and cancel timers) and return; only continue
when ret >= 0. This ensures protocol/I/O/allocation errors in srv_recv do not
leave the peer hanging.
- Line 252: The control socket created by xsocketpair is blocking which allows
srv_recv (which uses recvmsg with MSG_WAITALL) to block the event loop; make the
pair non-blocking by adding SOCK_NONBLOCK to the xsocketpair flags where fdset
is created (the call to xsocketpair(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, ...,
fdset)) or, if you prefer to change state post-creation, set O_NONBLOCK on both
descriptors (fdset[0] and fdset[1]) via fcntl before registering them with
eloop_event_add so srv_recv will never block the reactor.

---

Duplicate comments:
In `@src/dhcpsd.c`:
- Around line 207-208: The launcher/main status channel was changed to
SOCK_STREAM but the code in dhcpsd_fork_cb() and the send path still assumes
single-packet semantics; update the logic that uses fork_fd and exit_code so
reads and writes loop until all sizeof(exit_code) bytes are transferred (handle
partial reads/writes, EINTR, EAGAIN and EOF) instead of calling a single
recv()/send(); specifically modify the receive side in dhcpsd_fork_cb() to
accumulate bytes into exit_code until complete and detect EOF as a failure, and
modify the send side (the code that writes exit_code to fork_fd) to loop until
all bytes are written before closing the FD. Ensure error return paths and close
behavior remain correct.

In `@src/service.c`:
- Around line 170-171: The send logic currently does a single
sendmsg(sctx->srv_fd, &msg, 0) and treats only -1 as failure, allowing partial
writes on SOCK_STREAM to be accepted; change this to loop until the entire frame
(total_len derived from the iovec/msg) is written: call sendmsg repeatedly,
handle EINTR by retrying, handle EAGAIN/EWOULDBLOCK appropriately, and on a
positive return < remaining_bytes adjust the iovec/offset (or rebuild msg) and
subtract bytes_sent, continuing until remaining_bytes == 0; ensure you
update/inspect the same send path used around sendmsg (use the same msg/iov
references) and return an error only for true failures (<0 after retries), not
for short writes.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0e354cb1-b17f-4745-96f9-3ba7237be723

📥 Commits

Reviewing files that changed from the base of the PR and between dd14a87 and e1c159c.

📒 Files selected for processing (3)
  • src/dhcpsd.c
  • src/plugins/lua.c
  • src/service.c
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/plugins/lua.c

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (2)
.github/workflows/build.yml (1)

46-51: Consider explicitly setting PKG_CONFIG to avoid silent Lua detection failures.

The configure script defaults to looking for pkg-config (see configure:122-125). While Homebrew's pkgconf package typically provides a pkg-config symlink, explicitly setting the environment variable ensures Lua detection won't fail silently if the symlink is missing.

Proposed fix
     - name: Install Lua
       run: |
         brew install lua pkgconf

     - name: Configure
-      run: ./configure
+      run: PKG_CONFIG=pkgconf ./configure
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/build.yml around lines 46 - 51, The workflow's Configure
step can miss pkg-config if Homebrew's pkgconf doesn't provide the symlink;
after the Install Lua step, ensure the Configure run exports PKG_CONFIG to a
valid pkg-config binary (e.g., resolve with which pkg-config or which pkgconf)
before invoking ./configure so the configure script's Lua detection uses the
explicit PKG_CONFIG environment variable; update the Configure run (the step
that runs ./configure) to set PKG_CONFIG accordingly.
src/service.c (1)

255-255: This blocking socket design is consistent across the codebase and appears intentional.

Both xsocketpair() calls in the codebase (src/service.c:255 and src/dhcpsd.c:207) are created with SOCK_STREAM | SOCK_CLOEXEC without SOCK_NONBLOCK, despite the codebase having full support for non-blocking sockets (see SOCK_CXNB macro in src/common.h:134). This is a deliberate design choice across the IPC channel creation.

The blocking behavior trades off event loop robustness for code simplicity—MSG_WAITALL guarantees a complete frame or error, eliminating the need for partial read buffering. For local Unix domain sockets between parent and child processes with small control messages, this is generally acceptable, as kernel buffers are sufficient and both peers are trusted.

If this PR's scope includes making the IPC channel non-blocking for improved robustness, add SOCK_NONBLOCK and implement partial read/write handling with buffering. Otherwise, no change is needed.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/service.c` at line 255, The current xsocketpair calls deliberately create
blocking Unix domain sockets (SOCK_STREAM | SOCK_CLOEXEC) so no immediate change
is required; if you want to make IPC non‑blocking instead, change the socket
flags to include the nonblocking macro (use SOCK_CXNB with the existing
xsocketpair call sites in service.c and dhcpsd.c) and then implement proper
partial read/write handling wherever MSG_WAITALL or blocking reads are relied
upon (update the IPC read path to accumulate frames in a buffer and the write
path to handle EAGAIN/EWOULDBLOCK and partial writes). Ensure you touch
xsocketpair call sites, any code paths using MSG_WAITALL, and the IPC
framing/buffering logic to support nonblocking behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/build.yml:
- Line 40: Update the workflow step that currently references
actions/checkout@v2 to use actions/checkout@v4 in the macOS job; locate the step
containing "uses: actions/checkout@v2" and change the version tag to "@v4" so
the checkout action runs on a supported Node runtime. Ensure the rest of the
step remains unchanged and do not modify the separate ubuntu job (which also
uses v2) as noted in the comment.
- Around line 34-37: The workflow uses an invalid runner label in the matrix
(matrix.os / runs-on) — replace "macos-arm64-latest" with a valid GitHub-hosted
macOS runner such as "macos-14" or "macos-15" (or "macos-latest-large" if you
need the large ARM runner) so the strategy.matrix.os array contains only valid
labels; update the matrix.os values accordingly and keep "macos-latest" or a
specific Intel macOS label if you intend to test both Intel and ARM.

In `@src/service.c`:
- Around line 108-111: The error message compares the wrong values: the check
uses nread != cmd.sc_datalen but the log prints sizeof(cmd) + cmd.sc_datalen,
which is misleading; update the logerrx call in the same block (where nread,
cmd.sc_datalen, logerrx and __func__ are used) to print the payload-only
comparison (e.g., logerrx("%s: read datalen mismatch: %zd != %zd", __func__,
nread, cmd.sc_datalen)) or alternatively change the condition to match the
logged total if that was intended—ensure the logged values exactly match the
comparison performed.
- Around line 130-137: srv_recvl currently treats any -1 from srv_recv as a
fatal error and calls eloop_exit(EXIT_FAILURE), which overwrites a prior clean
shutdown; update the contract so srv_recv returns a distinct value for hangup
(e.g., 0) versus error (-1) or exposes a hangup flag, then modify srv_recvl to
only call eloop_exit(EXIT_FAILURE) when srv_recv returns the error sentinel (-1)
and do nothing (or ensure eloop_exit(EXIT_SUCCESS) is preserved) when srv_recv
indicates a hangup; adjust the logic in srv_recv and the check in srv_recvl
(referencing functions srv_recv and srv_recvl and eloop_exit and the srv_ctx
used there) so graceful peer closes do not get translated into EXIT_FAILURE.

---

Nitpick comments:
In @.github/workflows/build.yml:
- Around line 46-51: The workflow's Configure step can miss pkg-config if
Homebrew's pkgconf doesn't provide the symlink; after the Install Lua step,
ensure the Configure run exports PKG_CONFIG to a valid pkg-config binary (e.g.,
resolve with which pkg-config or which pkgconf) before invoking ./configure so
the configure script's Lua detection uses the explicit PKG_CONFIG environment
variable; update the Configure run (the step that runs ./configure) to set
PKG_CONFIG accordingly.

In `@src/service.c`:
- Line 255: The current xsocketpair calls deliberately create blocking Unix
domain sockets (SOCK_STREAM | SOCK_CLOEXEC) so no immediate change is required;
if you want to make IPC non‑blocking instead, change the socket flags to include
the nonblocking macro (use SOCK_CXNB with the existing xsocketpair call sites in
service.c and dhcpsd.c) and then implement proper partial read/write handling
wherever MSG_WAITALL or blocking reads are relied upon (update the IPC read path
to accumulate frames in a buffer and the write path to handle EAGAIN/EWOULDBLOCK
and partial writes). Ensure you touch xsocketpair call sites, any code paths
using MSG_WAITALL, and the IPC framing/buffering logic to support nonblocking
behavior.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c368e44b-5345-46d0-941b-724be710e53c

📥 Commits

Reviewing files that changed from the base of the PR and between e1c159c and 5da02fe.

📒 Files selected for processing (2)
  • .github/workflows/build.yml
  • src/service.c

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
.github/workflows/build.yml (2)

35-37: ⚠️ Potential issue | 🟠 Major

Replace unsupported macOS runner label in the matrix.

Line 36 uses macos-latest-arm64, which is not a valid GitHub-hosted label and will fail job scheduling.

Proposed fix
   strategy:
     matrix:
-      os: [ macos-latest, macos-latest-arm64 ]
+      os: [ macos-15-intel, macos-15 ]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/build.yml around lines 35 - 37, The matrix uses an
unsupported runner label "macos-latest-arm64" which will fail scheduling; update
the matrix configuration (the matrix: os array and the runs-on expression) to
use valid GitHub-hosted labels such as "macos-latest" and "macos-12-arm64" or
remove the invalid entry, ensuring the runs-on value remains ${{ matrix.os }}
and the matrix entries match supported runner labels.

40-40: ⚠️ Potential issue | 🟠 Major

Upgrade checkout action to v4 in the macOS job.

Line 40 still uses actions/checkout@v2, which is too old for current hosted-runner environments.

Proposed fix
-    - uses: actions/checkout@v2
+    - uses: actions/checkout@v4
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/build.yml at line 40, Replace the deprecated
actions/checkout@v2 usage in the macOS job with actions/checkout@v4; locate the
macOS job block that contains the line "uses: actions/checkout@v2" and update it
to "uses: actions/checkout@v4" (keep the existing inputs such as
persist-credentials or fetch-depth if present) so the workflow uses the current
checkout action version.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/build.yml:
- Around line 42-44: The workflow step with id "set-up-homebrew" currently uses
the mutable ref "uses: Homebrew/actions/setup-homebrew@main"; replace the branch
ref with the action's immutable commit SHA (pin the action to a full commit
hash) so the step "uses: Homebrew/actions/setup-homebrew@..." references a
specific commit SHA instead of `@main` to ensure reproducible and secure CI runs.

---

Duplicate comments:
In @.github/workflows/build.yml:
- Around line 35-37: The matrix uses an unsupported runner label
"macos-latest-arm64" which will fail scheduling; update the matrix configuration
(the matrix: os array and the runs-on expression) to use valid GitHub-hosted
labels such as "macos-latest" and "macos-12-arm64" or remove the invalid entry,
ensuring the runs-on value remains ${{ matrix.os }} and the matrix entries match
supported runner labels.
- Line 40: Replace the deprecated actions/checkout@v2 usage in the macOS job
with actions/checkout@v4; locate the macOS job block that contains the line
"uses: actions/checkout@v2" and update it to "uses: actions/checkout@v4" (keep
the existing inputs such as persist-credentials or fetch-depth if present) so
the workflow uses the current checkout action version.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ff85f502-0ebd-4bbf-992b-b8ab8c2b3f61

📥 Commits

Reviewing files that changed from the base of the PR and between 5da02fe and 7e6dad8.

📒 Files selected for processing (1)
  • .github/workflows/build.yml

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

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 the current code and only fix it if needed.

Inline comments:
In @.github/workflows/build.yml:
- Around line 42-50: The workflow's Lua build can silently skip the plugin
because Homebrew's pkg-config path isn't exported before running ./configure;
update the CI steps around "Install Lua" and "Configure" to export the Homebrew
pkg-config directory into PKG_CONFIG_PATH (the Homebrew lib/pkgconfig location)
so pkg-config can find Lua .pc files before invoking ./configure, and add a
post-configure verification that checks config.mk for LUA_PLUGIN (e.g., fail the
job if LUA_PLUGIN is not present) to ensure the Lua plugin was detected; target
the existing steps invoking brew install lua, ./configure and make and the
config.mk/LUA_PLUGIN symbols when making these changes.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e2e0576a-c2b7-4c7c-966b-2001f71df1cf

📥 Commits

Reviewing files that changed from the base of the PR and between 7e6dad8 and ea815b0.

📒 Files selected for processing (2)
  • .github/workflows/build.yml
  • src/service.c
✅ Files skipped from review due to trivial changes (1)
  • src/service.c

Comment on lines +42 to +50
- name: Install Lua
run: |
brew install lua

- name: Configure
run: ./configure

- name: Build
run: make
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Lua plugin may silently fail to build — pkg-config configuration needed.

The ./configure script detects Lua via pkg-config (see configure:695-720). On macOS, Homebrew-installed Lua's .pc files may not be indexed by pkg-config without additional setup. If detection fails, the build will succeed but skip the Lua plugin entirely, defeating the test purpose.

Consider adding the Homebrew pkg-config path:

Proposed fix
     - name: Install Lua
       run: |
         brew install lua
+
+    - name: Configure
+      run: |
+        export PKG_CONFIG_PATH="$(brew --prefix lua)/lib/pkgconfig:$PKG_CONFIG_PATH"
+        ./configure
 
-    - name: Configure
-      run: ./configure
-
     - name: Build
       run: make

Alternatively, you could add a post-configure check that verifies LUA_PLUGIN was detected:

grep -q 'LUA_PLUGIN' config.mk || { echo "Lua plugin not detected"; exit 1; }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/build.yml around lines 42 - 50, The workflow's Lua build
can silently skip the plugin because Homebrew's pkg-config path isn't exported
before running ./configure; update the CI steps around "Install Lua" and
"Configure" to export the Homebrew pkg-config directory into PKG_CONFIG_PATH
(the Homebrew lib/pkgconfig location) so pkg-config can find Lua .pc files
before invoking ./configure, and add a post-configure verification that checks
config.mk for LUA_PLUGIN (e.g., fail the job if LUA_PLUGIN is not present) to
ensure the Lua plugin was detected; target the existing steps invoking brew
install lua, ./configure and make and the config.mk/LUA_PLUGIN symbols when
making these changes.

@rsmarples
Copy link
Copy Markdown
Member Author

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 7, 2026

✅ Actions performed

Full review triggered.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (2)
src/common.c (1)

470-478: ⚠️ Potential issue | 🔴 Critical

Critical: Line 476 uses O_NONBLOCK instead of FD_CLOEXEC with F_SETFD.

The SOCK_CLOEXEC handling is setting the wrong flag. F_SETFD only accepts descriptor flags (FD_CLOEXEC), not status flags (O_NONBLOCK).

🐛 Proposed fix
 `#ifndef` HAVE_SOCK_CLOEXEC
 	if (flags & SOCK_CLOEXEC) {
 		oflags = fcntl(fd, F_GETFD);
 		if (oflags == -1)
 			return -1;
 		if (!(oflags & FD_CLOEXEC) &&
-		    fcntl(fd, F_SETFD, oflags | O_NONBLOCK) == -1)
+		    fcntl(fd, F_SETFD, oflags | FD_CLOEXEC) == -1)
 			return -1;
 	}
 `#endif`
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/common.c` around lines 470 - 478, The SOCK_CLOEXEC handling uses the
wrong flag when calling fcntl(F_SETFD): replace the status flag O_NONBLOCK with
the descriptor flag FD_CLOEXEC so F_SETFD sets the close-on-exec bit; locate the
block guarded by HAVE_SOCK_CLOEXEC that checks (flags & SOCK_CLOEXEC), inspect
variables oflags and the fcntl calls (F_GETFD / F_SETFD) and change the value
passed to F_SETFD from O_NONBLOCK to oflags | FD_CLOEXEC so the descriptor flag
is set correctly.
src/service.c (1)

108-111: ⚠️ Potential issue | 🟡 Minor

Format specifier mismatch for size_t.

cmd.sc_datalen is size_t (unsigned) but uses %zd (signed). Use %zu for unsigned size types.

🔧 Suggested fix
 		if ((size_t)nread != cmd.sc_datalen) {
-			logerrx("%s: read datalen mismatch: %zd != %zd",
+			logerrx("%s: read datalen mismatch: %zd != %zu",
 			    __func__, nread, cmd.sc_datalen);
 			return -1;
 		}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/service.c` around lines 108 - 111, Format specifier mismatch: change the
logerrx call that prints nread and cmd.sc_datalen so the signed nread uses %zd
and the unsigned cmd.sc_datalen uses %zu; update the format string in the
logerrx invocation (referencing __func__, nread, cmd.sc_datalen) to "%s: read
datalen mismatch: %zd != %zu" so the types match their specifiers.
🧹 Nitpick comments (3)
compat/reallocarray.h (1)

1-1: Incorrect CVS tag in header file.

The CVS ID tag references reallocarr.c but this is reallocarray.h. This appears to be a copy-paste artifact.

📝 Proposed fix
-/* $NetBSD: reallocarr.c,v 1.4 2015/08/20 20:08:04 joerg Exp $ */
+/* $NetBSD: reallocarray.h */
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@compat/reallocarray.h` at line 1, The CVS ID header in compat/reallocarray.h
incorrectly references "reallocarr.c"; update the header comment to refer to the
correct filename (reallocarray.h) so the CVS tag matches the file, e.g. change
the string "reallocarr.c" to "reallocarray.h" in the top comment; ensure you
only modify the comment and preserve the existing timestamp/format.
src/if.c (1)

67-67: Unnecessary removal of const qualifiers.

The change from const struct sockaddr_dl *sdl to struct sockaddr_dl *sdl (and similarly for sll) removes const-correctness without apparent necessity. The pointers are only read, not written to.

♻️ Preserve const-correctness
 `#ifdef` AF_LINK
-	struct sockaddr_dl *sdl;
+	const struct sockaddr_dl *sdl;
 `#ifdef` IFLR_ACTIVE
 	struct if_laddrreq iflr = { .flags = IFLR_PREFIX };
 `#endif`
 `#elif` defined(AF_PACKET)
-	struct sockaddr_ll *sll;
+	const struct sockaddr_ll *sll;
 `#endif`

The casts on lines 87, 110, and 156 should then be (const void *) to maintain type safety.

Also applies to: 72-72

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/if.c` at line 67, Restore const-correctness by changing the local pointer
declarations back to const: use "const struct sockaddr_dl *sdl" and "const
struct sockaddr_ll *sll" (or whatever sll identifier exists) since the buffers
are only read. Update the corresponding casts where those pointers are passed to
functions so they cast to "(const void *)" instead of "(void *)" to match the
const types; ensure any other uses that expect non-const remain unchanged or
perform safe, explicit copies if mutation is required.
configure (1)

612-614: Inverted condition for HAVE_SETPROCTITLE.

The condition [ "$SETPROCTITLE" != no ] will define HAVE_SETPROCTITLE even when SETPROCTITLE is explicitly set to something other than "yes" or "no" (e.g., empty). However, since lines 605-608 explicitly set it to "yes" or "no", this should work correctly in practice.

For consistency with other feature checks (e.g., STRLCPY, RBTREE), consider using the positive check:

📝 Suggested improvement for consistency
-if [ "$SETPROCTITLE" != no ]; then
+if [ "$SETPROCTITLE" = yes ]; then
 	echo "#define HAVE_SETPROCTITLE" >>$CONFIG_H
 fi
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@configure` around lines 612 - 614, The conditional that defines
HAVE_SETPROCTITLE should only trigger when SETPROCTITLE is explicitly "yes";
change the test from the negative form ([ "$SETPROCTITLE" != no ]) to a positive
equality check ([ "$SETPROCTITLE" = yes ]) so HAVE_SETPROCTITLE is defined only
when SETPROCTITLE is "yes", matching the style used for other feature checks
like STRLCPY and RBTREE and referencing the SETPROCTITLE variable and
HAVE_SETPROCTITLE macro.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/eloop.c`:
- Around line 628-633: The current comment and behavior around kqueue() are
incorrect: kqueue FDs are inherited across fork() and not automatically closed,
and macOS does allow setting FD_CLOEXEC; update the code that calls kqueue()
(the fd = kqueue() site) to immediately set the close-on-exec flag using
fcntl(fd, F_SETFD, FD_CLOEXEC) and check for errors, and replace the comment to
state that we set FD_CLOEXEC to prevent the descriptor from leaking into
children (and note that fork-only children inherit FDs unless FD_CLOEXEC is
set).

In `@src/service.c`:
- Around line 104-106: The error message incorrectly states "recvmsg cmd" when
the recvmsg call is actually reading the data payload; update the log in the
nread == -1 branch inside the function shown (uses __func__) to accurately
reflect that this failure occurred while receiving the payload/data (e.g.,
"recvmsg payload" or "recvmsg data"), leaving the error handling and return -1
behavior unchanged so callers still get the same failure path.

---

Duplicate comments:
In `@src/common.c`:
- Around line 470-478: The SOCK_CLOEXEC handling uses the wrong flag when
calling fcntl(F_SETFD): replace the status flag O_NONBLOCK with the descriptor
flag FD_CLOEXEC so F_SETFD sets the close-on-exec bit; locate the block guarded
by HAVE_SOCK_CLOEXEC that checks (flags & SOCK_CLOEXEC), inspect variables
oflags and the fcntl calls (F_GETFD / F_SETFD) and change the value passed to
F_SETFD from O_NONBLOCK to oflags | FD_CLOEXEC so the descriptor flag is set
correctly.

In `@src/service.c`:
- Around line 108-111: Format specifier mismatch: change the logerrx call that
prints nread and cmd.sc_datalen so the signed nread uses %zd and the unsigned
cmd.sc_datalen uses %zu; update the format string in the logerrx invocation
(referencing __func__, nread, cmd.sc_datalen) to "%s: read datalen mismatch: %zd
!= %zu" so the types match their specifiers.

---

Nitpick comments:
In `@compat/reallocarray.h`:
- Line 1: The CVS ID header in compat/reallocarray.h incorrectly references
"reallocarr.c"; update the header comment to refer to the correct filename
(reallocarray.h) so the CVS tag matches the file, e.g. change the string
"reallocarr.c" to "reallocarray.h" in the top comment; ensure you only modify
the comment and preserve the existing timestamp/format.

In `@configure`:
- Around line 612-614: The conditional that defines HAVE_SETPROCTITLE should
only trigger when SETPROCTITLE is explicitly "yes"; change the test from the
negative form ([ "$SETPROCTITLE" != no ]) to a positive equality check ([
"$SETPROCTITLE" = yes ]) so HAVE_SETPROCTITLE is defined only when SETPROCTITLE
is "yes", matching the style used for other feature checks like STRLCPY and
RBTREE and referencing the SETPROCTITLE variable and HAVE_SETPROCTITLE macro.

In `@src/if.c`:
- Line 67: Restore const-correctness by changing the local pointer declarations
back to const: use "const struct sockaddr_dl *sdl" and "const struct sockaddr_ll
*sll" (or whatever sll identifier exists) since the buffers are only read.
Update the corresponding casts where those pointers are passed to functions so
they cast to "(const void *)" instead of "(void *)" to match the const types;
ensure any other uses that expect non-const remain unchanged or perform safe,
explicit copies if mutation is required.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5ca7e466-7c4f-4bf6-ab6e-04fcf8ce61c3

📥 Commits

Reviewing files that changed from the base of the PR and between 810a040 and f18a138.

📒 Files selected for processing (20)
  • .github/workflows/build.yml
  • .gitignore
  • compat/reallocarray.c
  • compat/reallocarray.h
  • config-null.mk
  • configure
  • src/common.c
  • src/common.h
  • src/dhcpsd.c
  • src/dhcpsd.h
  • src/eloop.c
  • src/if.c
  • src/plugin.h
  • src/plugins/Makefile
  • src/plugins/ethers.c
  • src/plugins/lua.c
  • src/service.c
  • src/service.h
  • src/unpriv.c
  • src/unpriv.h
💤 Files with no reviewable changes (1)
  • config-null.mk

Comment on lines +628 to +633
#elif defined(USE_KQUEUE) && defined(__APPLE__)
/* macOS does not allow setting CLOEXEC on kqueue.
* This should not be a problem because eloop consumers
* fork and exec rather than just exec and kqueue is
* automatically closed on fork. */
fd = kqueue();
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify that all fork() calls in the codebase are followed by exec() variants
# to confirm the CLOEXEC omission is safe

echo "=== Searching for fork() calls and their context ==="
rg -n -A10 '\bfork\s*\(\s*\)' --type=c

echo ""
echo "=== Checking for exec family calls ==="
rg -n '\b(execl|execle|execlp|execv|execve|execvp|execvpe)\s*\(' --type=c

Repository: NetworkConfiguration/dhcpsd

Length of output: 1449


🏁 Script executed:

# Check if eloop.c exists and examine the file
find . -name "eloop.c" -type f

Repository: NetworkConfiguration/dhcpsd

Length of output: 84


🏁 Script executed:

# Get more context around the fork() calls to see if exec is called
# Let's examine src/dhcpsd.c more closely around the fork calls
head -n 280 src/dhcpsd.c | tail -n 70

Repository: NetworkConfiguration/dhcpsd

Length of output: 1725


🏁 Script executed:

# Search for exec calls more broadly (might be escaped or formatted differently)
rg -n 'exec' --type=c | head -20

Repository: NetworkConfiguration/dhcpsd

Length of output: 140


🏁 Script executed:

# Read src/eloop.c lines 620-640 to see the full context
sed -n '620,640p' src/eloop.c

Repository: NetworkConfiguration/dhcpsd

Length of output: 675


macOS kqueue FD without CLOEXEC — unsafe assumption about fork/exec pattern.

The comment contains two issues:

  1. It states kqueue is "automatically closed on fork," which is backwards — kqueue FDs are inherited across fork() and only closed on exec() when CLOEXEC is set.
  2. The claim that "eloop consumers fork and exec" does not match actual usage: the codebase uses fork() without exec() (child processes continue as daemons via setsid() and eloop_forked()).

Since child processes inherit the kqueue FD without CLOEXEC, and no exec() call closes it, the FD remains open in forked children, potentially causing resource leaks or unexpected behavior.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/eloop.c` around lines 628 - 633, The current comment and behavior around
kqueue() are incorrect: kqueue FDs are inherited across fork() and not
automatically closed, and macOS does allow setting FD_CLOEXEC; update the code
that calls kqueue() (the fd = kqueue() site) to immediately set the
close-on-exec flag using fcntl(fd, F_SETFD, FD_CLOEXEC) and check for errors,
and replace the comment to state that we set FD_CLOEXEC to prevent the
descriptor from leaking into children (and note that fork-only children inherit
FDs unless FD_CLOEXEC is set).

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/service.c (1)

255-264: ⚠️ Potential issue | 🟠 Major

Close both socketpair() fds if fork() fails.

On the fork() error path, sctx->srv_fd is still -1, so srv_free() does not close either entry in fdset. That leaks both descriptors exactly when the process is already under resource pressure.

🧹 Minimal cleanup fix
 	case -1:
 		logerr("%s: fork", __func__);
+		close(fdset[0]);
+		close(fdset[1]);
 		goto error;

Also applies to: 316-318

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/service.c` around lines 255 - 264, The fork() error path leaks both
socketpair fds because sctx->srv_fd remains -1 and srv_free() won't close fdset;
in the case -1 branch after xsocketpair(...) and before goto error, explicitly
close fdset[0] and fdset[1] (or call a small cleanup helper) and/or set
sctx->srv_fd to a valid value so srv_free() will close them; apply the same
cleanup to the other similar fork-failure location (the second occurrence around
where pid is checked again) to ensure both descriptors are closed on fork
failure.
♻️ Duplicate comments (2)
src/service.c (2)

173-174: ⚠️ Potential issue | 🔴 Critical

Write the entire frame before reporting success.

On a SOCK_STREAM socket, sendmsg() can legally return a short positive byte count. Returning that as success leaves the header/payload half-written, and the next srv_recv() can lose framing.

On POSIX/Unix stream sockets, can `sendmsg()` legally return a short positive byte count, and should callers loop until the full `iovec` payload is transmitted?
🔁 Sketch of the required send loop
-	ssize_t err = sendmsg(sctx->srv_fd, &msg, 0);
-	return err;
+	for (;;) {
+		ssize_t n = sendmsg(sctx->srv_fd, &msg, 0);
+		size_t off;
+
+		if (n == -1) {
+			if (errno == EINTR)
+				continue;
+			return -1;
+		}
+		if (n == 0) {
+			errno = EPIPE;
+			return -1;
+		}
+
+		off = (size_t)n;
+		while (off != 0 && msg.msg_iovlen != 0) {
+			if (off >= msg.msg_iov[0].iov_len) {
+				off -= msg.msg_iov[0].iov_len;
+				msg.msg_iov++;
+				msg.msg_iovlen--;
+			} else {
+				msg.msg_iov[0].iov_base =
+				    (char *)msg.msg_iov[0].iov_base + off;
+				msg.msg_iov[0].iov_len -= off;
+				off = 0;
+			}
+		}
+
+		if (msg.msg_iovlen == 0)
+			return (ssize_t)(sizeof(sc) + sc.sc_datalen);
+	}

Also applies to: 195-197

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/service.c` around lines 173 - 174, The code currently calls sendmsg once
(ssize_t err = sendmsg(sctx->srv_fd, &msg, 0); return err;) which can return a
short positive byte count on SOCK_STREAM; change to a loop that repeatedly calls
sendmsg (or send) until the full frame bytes from the iovec(s) are written,
tracking bytes_sent vs total_len and advancing/adjusting the iovec base/length
accordingly, while properly handling EINTR/EAGAIN and returning on fatal errors;
apply the same fix to the other send site mentioned (lines ~195-197) and ensure
srv_recv() sees complete frames only after the loop finishes successfully.

68-71: ⚠️ Potential issue | 🟠 Major

Keep clean EOF distinct from reply and transport error states.

A zero-byte read on a plain ELE_READ event still jumps to hangup: and returns -1, so srv_recvl() overwrites the earlier EXIT_SUCCESS with EXIT_FAILURE. Zero-length replies are valid now, so EOF needs its own negative sentinel (or a separate status flag) rather than reusing the generic error path.

🧭 One way to preserve the clean-shutdown path
+enum { SRV_RECV_HANGUP = -2 };
+
 static ssize_t
 srv_recv(struct srv_ctx *sctx, unsigned short e)
 {
@@
 	if (e & ELE_HANGUP) {
 	hangup:
 		eloop_exit(sctx->srv_ctx->ctx_eloop, EXIT_SUCCESS);
-		return -1;
+		return SRV_RECV_HANGUP;
 	}
@@
 static void
 srv_recvl(void *arg, unsigned short e)
 {
 	struct srv_ctx *sctx = arg;
+	ssize_t ret;
 
-	if (srv_recv(sctx, e) == -1 && !(e & ELE_HANGUP))
+	ret = srv_recv(sctx, e);
+	if (ret == -1)
 		eloop_exit(sctx->srv_ctx->ctx_eloop, EXIT_FAILURE);
 }
@@
-	if (srv_recv(sctx, (unsigned short)events) == -1)
+	if (srv_recv(sctx, (unsigned short)events) < 0)
 		return -1;

Also applies to: 135-136, 200-204

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/service.c` around lines 68 - 71, The current handler jumps to the hangup
path (label hangup) for ELE_HANGUP and also for zero-byte reads on ELE_READ,
causing eloop_exit(sctx->srv_ctx->ctx_eloop, EXIT_SUCCESS) to be overwritten by
srv_recvl() returning -1; change the logic so EOF (zero-length read) is treated
distinctly from transport/reply errors by returning a separate negative sentinel
(e.g., ERR_EOF) or setting a status flag instead of jumping to hangup, and
update srv_recvl(), the ELE_READ handling, and any other uses of the hangup
label (including the contexts around ELE_HANGUP and the places noted near the
other occurrences) to check for that EOF sentinel/flag and preserve the
EXIT_SUCCESS clean-shutdown call invoked at hangup.
🧹 Nitpick comments (2)
.github/workflows/build.yml (1)

52-55: Consider removing disabled BSD jobs instead of using if: false.

Using if: false to disable jobs is a code smell flagged by actionlint. These jobs still appear in the workflow file and contribute to maintenance burden. Consider either:

  1. Removing the jobs entirely and tracking re-enablement in an issue
  2. Using a repository variable or secret to control enablement (e.g., if: vars.ENABLE_BSD_CI == 'true')
  3. Moving to a separate workflow file that can be enabled later

If keeping them disabled temporarily is intentional, adding a comment explaining when/why they'll be re-enabled would help maintainability. The current comment "The BSDs are currently broken on github" is helpful but could include an issue reference for tracking.

Also applies to: 74-75, 94-95

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/build.yml around lines 52 - 55, The workflow currently
keeps disabled BSD jobs (e.g., the "openbsd" job and other BSD blocks at the
referenced sections) using "if: false", which actionlint flags; remove these
disabled job blocks entirely or replace the conditional with a configurable
toggle (for example change to use a repository variable like "if:
vars.ENABLE_BSD_CI == 'true'") or move them to a separate workflow file, and if
you choose to keep a temporary disablement add a clear comment referencing an
issue/plan for re-enablement so maintainers can track it.
src/plugins/Makefile (1)

37-37: Consider declaring all and other non-file targets as .PHONY.

The all target (and likely lint, install, clean, proginstall, maninstall) should be declared as .PHONY to ensure they always run even if a file with that name exists, and to improve make's performance by not checking for file timestamps.

Proposed addition near the top of the file
 CLEANFILES+=	${OBJS} ${PLUGS}
 CLEANFILES+=	dhcpsd_leasefile.8 ${LUA_MAN8}

+.PHONY: all lint install proginstall maninstall clean
+
 .SUFFIXES:	.c .o .so .in
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/plugins/Makefile` at line 37, Declare the non-file make targets as phony
so make won't treat them as files: add a .PHONY line that lists targets like
all, lint, install, clean, proginstall, maninstall (and any other meta-targets)
near the top of the Makefile; update the Makefile so the .PHONY declaration
references the unique targets "all", "lint", "install", "clean", "proginstall",
and "maninstall" to ensure they always execute regardless of existing filenames.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/build.yml:
- Around line 33-50: The macOS build step must export Homebrew's pkg-config path
before running ./configure so pkg-config can find Lua; update the workflow so
the "Configure" step (or immediately before it, e.g., after "Install Lua") sets
PKG_CONFIG_PATH to include `$(brew --prefix)/opt/lua/lib/pkgconfig` (and/or
`$(brew --prefix)/lib/pkgconfig`) so configure detects the Lua plugin, then run
./configure; also add a post-configure step named "Verify Lua plugin detected"
that greps for 'LUA_PLUGIN' in config.mk and fails the job if not found to
ensure the plugin was detected.

In `@src/plugins/Makefile`:
- Around line 26-32: The .SUFFIXES line is missing the .c and .o suffixes so the
implicit suffix rules .c.o and .o.so aren't registered; update the .SUFFIXES
declaration to include .c and .o (so the declared .c.o and .o.so rules become
active and targets like auto.o, leasefile.o, addrinfo.o, ethers.o and their .so
counterparts can be built). Ensure you modify the existing .SUFFIXES entry (the
one currently listing .in .so) to also contain .c and .o so the .c.o and .o.so
suffix transformation rules are recognized.

---

Outside diff comments:
In `@src/service.c`:
- Around line 255-264: The fork() error path leaks both socketpair fds because
sctx->srv_fd remains -1 and srv_free() won't close fdset; in the case -1 branch
after xsocketpair(...) and before goto error, explicitly close fdset[0] and
fdset[1] (or call a small cleanup helper) and/or set sctx->srv_fd to a valid
value so srv_free() will close them; apply the same cleanup to the other similar
fork-failure location (the second occurrence around where pid is checked again)
to ensure both descriptors are closed on fork failure.

---

Duplicate comments:
In `@src/service.c`:
- Around line 173-174: The code currently calls sendmsg once (ssize_t err =
sendmsg(sctx->srv_fd, &msg, 0); return err;) which can return a short positive
byte count on SOCK_STREAM; change to a loop that repeatedly calls sendmsg (or
send) until the full frame bytes from the iovec(s) are written, tracking
bytes_sent vs total_len and advancing/adjusting the iovec base/length
accordingly, while properly handling EINTR/EAGAIN and returning on fatal errors;
apply the same fix to the other send site mentioned (lines ~195-197) and ensure
srv_recv() sees complete frames only after the loop finishes successfully.
- Around line 68-71: The current handler jumps to the hangup path (label hangup)
for ELE_HANGUP and also for zero-byte reads on ELE_READ, causing
eloop_exit(sctx->srv_ctx->ctx_eloop, EXIT_SUCCESS) to be overwritten by
srv_recvl() returning -1; change the logic so EOF (zero-length read) is treated
distinctly from transport/reply errors by returning a separate negative sentinel
(e.g., ERR_EOF) or setting a status flag instead of jumping to hangup, and
update srv_recvl(), the ELE_READ handling, and any other uses of the hangup
label (including the contexts around ELE_HANGUP and the places noted near the
other occurrences) to check for that EOF sentinel/flag and preserve the
EXIT_SUCCESS clean-shutdown call invoked at hangup.

---

Nitpick comments:
In @.github/workflows/build.yml:
- Around line 52-55: The workflow currently keeps disabled BSD jobs (e.g., the
"openbsd" job and other BSD blocks at the referenced sections) using "if:
false", which actionlint flags; remove these disabled job blocks entirely or
replace the conditional with a configurable toggle (for example change to use a
repository variable like "if: vars.ENABLE_BSD_CI == 'true'") or move them to a
separate workflow file, and if you choose to keep a temporary disablement add a
clear comment referencing an issue/plan for re-enablement so maintainers can
track it.

In `@src/plugins/Makefile`:
- Line 37: Declare the non-file make targets as phony so make won't treat them
as files: add a .PHONY line that lists targets like all, lint, install, clean,
proginstall, maninstall (and any other meta-targets) near the top of the
Makefile; update the Makefile so the .PHONY declaration references the unique
targets "all", "lint", "install", "clean", "proginstall", and "maninstall" to
ensure they always execute regardless of existing filenames.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0c51af25-074b-4146-8153-15f4d7507ae6

📥 Commits

Reviewing files that changed from the base of the PR and between f18a138 and be6a688.

📒 Files selected for processing (6)
  • .github/workflows/build.yml
  • compat/reallocarray.c
  • src/common.c
  • src/eloop.c
  • src/plugins/Makefile
  • src/service.c
🚧 Files skipped from review as they are similar to previous changes (2)
  • compat/reallocarray.c
  • src/eloop.c

Comment on lines +33 to +50
macos:
strategy:
matrix:
os: [ macos-15, macos-26 ]
runs-on: ${{ matrix.os }}

steps:
- uses: actions/checkout@v6

- name: Install Lua
run: |
brew install lua

- name: Configure
run: ./configure

- name: Build
run: make
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Lua plugin may silently fail to build on macOS — pkg-config path still not configured.

The ./configure script detects Lua via pkg-config (see configure:695-720). On macOS, Homebrew-installed packages are not in pkg-config's default search path. Without setting PKG_CONFIG_PATH, the Lua detection will fail silently and the plugin will be skipped, defeating the test purpose.

Proposed fix to export Homebrew's pkg-config path
     - name: Install Lua
       run: |
         brew install lua

     - name: Configure
-      run: ./configure
+      run: |
+        export PKG_CONFIG_PATH="$(brew --prefix lua)/lib/pkgconfig:$PKG_CONFIG_PATH"
+        ./configure

     - name: Build
       run: make

Optionally, add a post-configure verification step:

    - name: Verify Lua plugin detected
      run: grep -q 'LUA_PLUGIN' config.mk || { echo "Lua plugin not detected"; exit 1; }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/build.yml around lines 33 - 50, The macOS build step must
export Homebrew's pkg-config path before running ./configure so pkg-config can
find Lua; update the workflow so the "Configure" step (or immediately before it,
e.g., after "Install Lua") sets PKG_CONFIG_PATH to include `$(brew
--prefix)/opt/lua/lib/pkgconfig` (and/or `$(brew --prefix)/lib/pkgconfig`) so
configure detects the Lua plugin, then run ./configure; also add a
post-configure step named "Verify Lua plugin detected" that greps for
'LUA_PLUGIN' in config.mk and fails the job if not found to ensure the plugin
was detected.

Comment on lines +26 to +32
.SUFFIXES: .in .so

.c.So:
.c.o:
${CC} ${PICFLAG} -DPIC ${CFLAGS} ${CDBGFLAGS} ${CPPFLAGS} -c $< -o $@

.So.so:
${CC} ${LDFLAGS} -shared -Wl,-x -o $@ -Wl,-soname,$@ $< ${LIBS}
.o.so:
${CC} ${LDFLAGS} ${LDFLAGS_SO} -shared -Wl,-x -o $@ $< ${LIBS}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical: Add .c and .o to .SUFFIXES for suffix rules to work.

The .SUFFIXES declaration on line 26 only includes .in .so, but the implicit suffix rules .c.o (line 28) and .o.so (line 31) require both their source and target suffixes to be declared in .SUFFIXES for make to recognize them as suffix transformation rules.

Without .c and .o in the suffix list:

  • icmp.o and lua.o will build (they have explicit rules on lines 40-44)
  • lua.so will build (explicit rule on line 46)
  • auto.o, leasefile.o, addrinfo.o, ethers.o will fail — no build recipe
  • auto.so, leasefile.so, icmp.so, addrinfo.so, ethers.so will fail — the .o.so rule won't be recognized
Proposed fix
-.SUFFIXES:	.in .so
+.SUFFIXES:	.c .o .so .in
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
.SUFFIXES: .in .so
.c.So:
.c.o:
${CC} ${PICFLAG} -DPIC ${CFLAGS} ${CDBGFLAGS} ${CPPFLAGS} -c $< -o $@
.So.so:
${CC} ${LDFLAGS} -shared -Wl,-x -o $@ -Wl,-soname,$@ $< ${LIBS}
.o.so:
${CC} ${LDFLAGS} ${LDFLAGS_SO} -shared -Wl,-x -o $@ $< ${LIBS}
.SUFFIXES: .c .o .so .in
.c.o:
${CC} ${PICFLAG} -DPIC ${CFLAGS} ${CDBGFLAGS} ${CPPFLAGS} -c $< -o $@
.o.so:
${CC} ${LDFLAGS} ${LDFLAGS_SO} -shared -Wl,-x -o $@ $< ${LIBS}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/plugins/Makefile` around lines 26 - 32, The .SUFFIXES line is missing the
.c and .o suffixes so the implicit suffix rules .c.o and .o.so aren't
registered; update the .SUFFIXES declaration to include .c and .o (so the
declared .c.o and .o.so rules become active and targets like auto.o,
leasefile.o, addrinfo.o, ethers.o and their .so counterparts can be built).
Ensure you modify the existing .SUFFIXES entry (the one currently listing .in
.so) to also contain .c and .o so the .c.o and .o.so suffix transformation rules
are recognized.

@rsmarples rsmarples merged commit 9330ef4 into master Apr 7, 2026
7 checks passed
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.

Support Darwin (macOS)

1 participant