fix: support cross-platform status and down - #129
Conversation
There was a problem hiding this comment.
Pull request overview
This PR wires meshguard status and meshguard down through the cross-platform userspace control socket first (Unix socket / Windows named pipe), with Linux netlink/kernel fallbacks when the control socket is unavailable, and updates docs to reflect the new behavior across platforms.
Changes:
- Add
STOPcontrol command and client-sideControl.request()helper for Unix/Windows IPC. - Update CLI
status/downcommands to prefer control socket responses, falling back to Linux RTNETLINK/kernel status/teardown. - Refresh CLI + platform docs to remove stale “Linux-only” caveats for userspace-daemon status/down.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| src/services/control.zig | Adds STOP, shared response formatter, Windows nonblocking named-pipe polling tweaks, and a cross-platform request() client helper with Unix timeout. |
| src/main.zig | Routes status/down through Control.request() first; adds control-socket status printing; wires daemon stop flag. |
| README.md | Removes Linux-only notes for status/down examples and checklist item. |
| docs/reference/cli.md | Updates down/status docs to describe control-socket behavior + Linux fallback. |
| docs/guide/windows-support.md | Updates Windows support matrix/docs to reflect named-pipe-backed status/down. |
| docs/guide/macos-support.md | Notes control-socket-backed status/down for userspace daemon. |
| docs/guide/getting-started.md | Updates getting-started steps to describe control-socket shutdown/status + Linux fallback. |
| docs/guide/freebsd-support.md | Notes control-socket-backed status/down for userspace daemon. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| } else |_| { | ||
| if (comptime @import("builtin").os.tag != .linux) { | ||
| try stderr.writeStreamingAll(zio(), "meshguard is not running (control socket unavailable).\n"); | ||
| std.process.exit(1); | ||
| }; | ||
| } |
| var response_buf: [4096]u8 = undefined; | ||
| if (lib.services.Control.request(allocator, "STATUS", &response_buf)) |n| { | ||
| try printControlStatus(allocator, stdout, response_buf[0..n]); | ||
| return; | ||
| } else |_| { | ||
| if (comptime @import("builtin").os.tag != .linux) { | ||
| try stderr.writeStreamingAll(zio(), "meshguard is not running (control socket unavailable).\n"); | ||
| std.process.exit(1); | ||
| } | ||
| } |
| { | ||
| return false; | ||
| } | ||
| if (err == @as(win.windows.Win32Error, @enumFromInt(536))) return false; |
| var parsed = std.json.parseFromSlice(std.json.Value, allocator, response, .{}) catch { | ||
| try stdout.writeStreamingAll(zio(), "meshguard is running.\n"); | ||
| try writeFormatted(stdout, " control response: {s}", .{response}); | ||
| return; |
| else => { | ||
| try stdout.writeStreamingAll(zio(), "meshguard is running.\n"); | ||
| try writeFormatted(stdout, " control response: {s}", .{response}); | ||
| return; | ||
| }, |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 778cfb0ab8
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (std.mem.eql(u8, cmd, "STOP")) { | ||
| if (self.stop_flag) |flag| { | ||
| flag.store(false, .release); |
There was a problem hiding this comment.
Require authorization before honoring STOP
When the daemon is using the default Unix control socket, listenUnix chmods the socket to 0666, so any local user can connect to /run/meshguard/meshguard.sock. Adding this unauthenticated STOP handler lets an unprivileged local account terminate the meshguard daemon with meshguard down or a raw socket write, which is a local DoS against the VPN service; the stop path should be restricted to an authorized user/group or otherwise authenticated before flipping the daemon's running flag.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b10e60cdcf
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // SWIM-driven handshake retransmit is only needed for the userspace WG data | ||
| // plane; kernel WireGuard retransmits its own, and gossip-only has none. | ||
| swim.retransmit_handshakes = !use_kernel_wg and !gossip_only; | ||
| control.setStopFlag(&swim.running); |
There was a problem hiding this comment.
Poll the control socket in kernel mode
For meshguard up --kernel, this wires STOP to swim.running, but the later kernel branch enters swim.run() and never calls control.poll(). In that mode the Unix socket can accept a meshguard down connection but no handler ever reads STOP, so the client times out and falls back to deleting mg0 while the daemon process keeps running instead of shutting down.
Useful? React with 👍 / 👎.
| if (win.ReadFile(handle, @ptrCast(out.ptr), @intCast(out.len), &bytes_read, null) == @as(win.BOOL, @enumFromInt(0))) { | ||
| return error.ReadFailed; |
There was a problem hiding this comment.
Add a timeout to Windows pipe reads
This synchronous ReadFile has no timeout, so on Windows a meshguard status or meshguard down can hang indefinitely once it opens the pipe but the daemon does not reply. I checked windowsEventLoop: it only reaches control_socket.poll() after draining UDP, and the Windows UDP socket is created as a blocking socket in UdpSocket.bindAddr, so an idle Wintun daemon with no UDP traffic can leave this client blocked forever; the Unix request path avoids this with a 1s poll.
Useful? React with 👍 / 👎.
Summary
meshguard statusthrough the userspace control socket before falling back to Linux netlink/kernel WireGuard status.meshguard downthrough a newSTOPcontrol command before falling back to Linux RTNETLINK teardown.Validation
zig build test --summary allpassed, 90/90 tests.zig build --summary allpassed, 7/7 steps.git diff --checkpassed.meshguard status, then stopped viameshguard downwith daemon exit code 0.meshguard statusreports control socket unavailable.Closes #125