Commit 93247e9
Chris Rose
·
2026-07-04 16:41:39 -0400 EDT
parent 444fa72
fix: avoid statx syscall so zmx runs on pre-4.11 kernels (#186)
zmx crashed with `error: Unexpected` on every invocation on a Synology
DiskStation (Linux 4.4.302). `error.Unexpected` is what Zig's std returns
for an unrecognized errno; here the errno is ENOSYS (38) from the `statx`
syscall, which only exists on Linux >= 4.11.
Two code paths issued statx via Zig std, which calls the raw statx syscall
directly and does not fall back like libc does:
- log.zig: LogSystem.init() called file.getEndPos() -> File.stat() -> statx.
This runs before command parsing, so it broke *every* subcommand, not
just attach.
- socket.zig: sessionExists() called Dir.statFile() -> statx.
Both now use fd/at-based stat instead:
- LogSystem.init() uses posix.fstat(file.handle).
- sessionExists() uses posix.fstatatZ() + S.ISSOCK().
Because zmx links musl, these resolve to SYS_fstat / SYS_fstatat (musl on
x86_64 skips statx entirely), which are available on 4.4. Verified by
disassembling the release binary: no reachable statx call remains on the
attach path (the only residual references are the DWARF stack-trace
unwinder and sendFile fast-paths, neither of which zmx exercises).
2 files changed,
+9,
-3
+4,
-1
1@@ -1,4 +1,5 @@
2 const std = @import("std");
3+const posix = std.posix;
4
5 pub const LogSystem = struct {
6 file: ?std.fs.File = null,
7@@ -22,7 +23,9 @@ pub const LogSystem = struct {
8 else => return err,
9 };
10
11- const end_pos = try file.getEndPos();
12+ // fstat (not getEndPos) to avoid the statx syscall; see #186.
13+ const st = try posix.fstat(file.handle);
14+ const end_pos: u64 = @intCast(st.size);
15 try file.seekTo(end_pos);
16 self.current_size = end_pos;
17 self.file = file;
+5,
-2
1@@ -44,11 +44,14 @@ pub fn cleanupStaleSocket(dir: std.fs.Dir, session_name: []const u8) void {
2 }
3
4 pub fn sessionExists(dir: std.fs.Dir, name: []const u8) !bool {
5- const stat = dir.statFile(name) catch |err| switch (err) {
6+ // fstatatZ (not statFile) to avoid the statx syscall
7+ // https://github.com/neurosnap/zmx/issues/186
8+ const name_c = try posix.toPosixPath(name);
9+ const stat = posix.fstatatZ(dir.fd, &name_c, posix.AT.SYMLINK_NOFOLLOW) catch |err| switch (err) {
10 error.FileNotFound => return false,
11 else => return err,
12 };
13- if (stat.kind != .unix_domain_socket) {
14+ if (!posix.S.ISSOCK(stat.mode)) {
15 return error.FileNotUnixSocket;
16 }
17 return true;