Commit 9e5eb55

Eric Bower  ·  2026-07-26 14:13:24 -0400 EDT
parent 4232d74
refactor: use posix.zig for all posix fns

The zig team is planning to completely kill std.posix so we might as well
get ahead of it and bring everything we need into zmx.
6 files changed,  +251, -113
+0, -1
......@@ -1,6 +1,5 @@
11 const builtin = @import("builtin");
22 const std = @import("std");
3-const posix = std.posix;
43
54 pub const c = switch (builtin.os.tag) {
65 .macos => @cImport({
+6, -7
......@@ -1,5 +1,4 @@
11 const std = @import("std");
2-const posix = std.posix;
32 const cross = @import("cross.zig");
43 const socket = @import("socket.zig");
54 const lib_posix = @import("posix.zig");
......@@ -172,7 +171,7 @@ pub const SocketBuffer = struct {
172171 }
173172
174173 var tmp: [4096]u8 = undefined;
175- const n = try posix.read(fd, &tmp);
174+ const n = try lib_posix.read(fd, &tmp);
176175 if (n > 0) {
177176 try self.buf.appendSlice(self.alloc, tmp[0..n]);
178177 }
......@@ -239,8 +238,8 @@ pub fn probeSession(
239238 send(fd, .Info, "") catch return error.Unexpected;
240239 send(fd, .LabelGet, "") catch {};
241240
242- var poll_fds = [_]posix.pollfd{.{ .fd = fd, .events = posix.POLL.IN, .revents = 0 }};
243- const poll_result = posix.poll(&poll_fds, timeout_ms) catch return error.Unexpected;
241+ var poll_fds = [_]lib_posix.pollfd{.{ .fd = fd, .events = lib_posix.POLL.IN, .revents = 0 }};
242+ const poll_result = lib_posix.poll(&poll_fds, timeout_ms) catch return error.Unexpected;
244243 if (poll_result == 0) {
245244 return error.Timeout;
246245 }
......@@ -270,7 +269,7 @@ pub fn probeSession(
270269 }
271270
272271 // No complete message available, wait for more data
273- const more = posix.poll(&poll_fds, 50) catch break;
272+ const more = lib_posix.poll(&poll_fds, 50) catch break;
274273 if (more == 0) break;
275274 const n_read = sb.read(fd) catch break;
276275 if (n_read == 0) break;
......@@ -325,8 +324,8 @@ pub fn roundTripForTag(
325324
326325 send(fd, request_tag, payload) catch return error.Unexpected;
327326
328- var poll_fds = [_]posix.pollfd{.{ .fd = fd, .events = posix.POLL.IN, .revents = 0 }};
329- const poll_result = posix.poll(&poll_fds, timeout_ms) catch return error.Unexpected;
327+ var poll_fds = [_]lib_posix.pollfd{.{ .fd = fd, .events = lib_posix.POLL.IN, .revents = 0 }};
328+ const poll_result = lib_posix.poll(&poll_fds, timeout_ms) catch return error.Unexpected;
330329 if (poll_result == 0) return error.Timeout;
331330
332331 var sb = SocketBuffer.init(alloc) catch return error.Unexpected;
+89, -90
......@@ -1,5 +1,4 @@
11 const std = @import("std");
2-const posix = std.posix;
32 const build_options = @import("build_options");
43 const ghostty_vt = @import("ghostty-vt");
54 const ipc = @import("ipc.zig");
......@@ -33,10 +32,10 @@ fn zmxLogFn(
3332 /// Self-pipe woken by signal handlers. std.posix.poll loops on .INTR internally
3433 /// (PollError has no Interrupted member), so a signal that lands during poll()
3534 /// never surfaces; the handler writes a byte here and poll() wakes on POLLIN.
36-var sig_pipe: [2]posix.fd_t = .{ -1, -1 };
35+var sig_pipe: [2]lib_posix.fd_t = .{ -1, -1 };
3736
3837 // https://github.com/ziglang/zig/blob/738d2be9d6b6ef3ff3559130c05159ef53336224/lib/std/posix.zig#L3505
39-const O_NONBLOCK: usize = 1 << @bitOffsetOf(posix.O, "NONBLOCK");
38+const O_NONBLOCK: usize = 1 << @bitOffsetOf(lib_posix.O, "NONBLOCK");
4039
4140 const SessionMatch = struct {
4241 name: []const u8,
......@@ -83,7 +82,7 @@ fn openSignalPipe() !void {
8382 fn drainSignalPipe() void {
8483 var b: [16]u8 = undefined;
8584 while (true) {
86- const n = posix.read(sig_pipe[0], &b) catch return;
85+ const n = lib_posix.read(sig_pipe[0], &b) catch return;
8786 if (n == 0) return;
8887 }
8988 }
......@@ -805,12 +804,12 @@ const Daemon = struct {
805804 // main() set SIGPIPE to SIG_IGN, which (unlike handlers) survives
806805 // exec. Restore the default so the shell and its children behave
807806 // normally (e.g. `yes | head` should exit 141 via SIGPIPE).
808- const dfl: posix.Sigaction = .{
809- .handler = .{ .handler = posix.SIG.DFL },
810- .mask = posix.sigemptyset(),
807+ const dfl: lib_posix.Sigaction = .{
808+ .handler = .{ .handler = lib_posix.SIG.DFL },
809+ .mask = lib_posix.sigemptyset(),
811810 .flags = 0,
812811 };
813- posix.sigaction(posix.SIG.PIPE, &dfl, null);
812+ lib_posix.sigaction(lib_posix.SIG.PIPE, &dfl, null);
814813
815814 const session_env = try std.fmt.allocPrintSentinel(
816815 alloc,
......@@ -848,7 +847,7 @@ const Daemon = struct {
848847
849848 /// spawnPty runs forkpty() and executes the shell or shell command the user provides.
850849 fn spawnPty(self: *Daemon) !c_int {
851- const size = ipc.getTerminalSize(posix.STDOUT_FILENO);
850+ const size = ipc.getTerminalSize(lib_posix.STDOUT_FILENO);
852851 var ws: cross.c.struct_winsize = .{
853852 .ws_row = size.rows,
854853 .ws_col = size.cols,
......@@ -878,8 +877,8 @@ const Daemon = struct {
878877 std.log.info("pty spawned session={s} pid={d}", .{ self.session_name, pid });
879878
880879 // make pty non-blocking
881- const flags = try lib_posix.fcntl(master_fd, posix.F.GETFL, 0);
882- _ = try lib_posix.fcntl(master_fd, posix.F.SETFL, flags | O_NONBLOCK);
880+ const flags = try lib_posix.fcntl(master_fd, lib_posix.F.GETFL, 0);
881+ _ = try lib_posix.fcntl(master_fd, lib_posix.F.SETFL, flags | O_NONBLOCK);
883882 return master_fd;
884883 }
885884
......@@ -946,7 +945,7 @@ const Daemon = struct {
946945 std.log.warn("failed to open /dev/null: {s}", .{@errorName(err)});
947946 return err;
948947 };
949- inline for (.{ posix.STDIN_FILENO, posix.STDOUT_FILENO, posix.STDERR_FILENO }) |fd| {
948+ inline for (.{ lib_posix.STDIN_FILENO, lib_posix.STDOUT_FILENO, lib_posix.STDERR_FILENO }) |fd| {
950949 _ = lib_posix.dup2(devnull, fd) catch |err| {
951950 std.log.warn("dup2 /dev/null -> {d}: {s}", .{ fd, @errorName(err) });
952951 return err;
......@@ -1221,11 +1220,11 @@ const Daemon = struct {
12211220 // https://www.gnu.org/software/bash/manual/html_node/Signals.html
12221221 // negative pid means kill process and children
12231222 std.log.info("sending SIGHUP session={s} pid={d}", .{ self.session_name, self.pid });
1224- posix.kill(-self.pid, posix.SIG.HUP) catch |err| {
1223+ lib_posix.kill(-self.pid, lib_posix.SIG.HUP) catch |err| {
12251224 std.log.warn("failed to send SIGHUP to pty child err={s}", .{@errorName(err)});
12261225 };
12271226 std.Io.sleep(self.io, std.Io.Duration.fromMilliseconds(500), .real) catch unreachable;
1228- posix.kill(-self.pid, posix.SIG.KILL) catch |err| {
1227+ lib_posix.kill(-self.pid, lib_posix.SIG.KILL) catch |err| {
12291228 std.log.warn("failed to send SIGKILL to pty child err={s}", .{@errorName(err)});
12301229 };
12311230 }
......@@ -1339,7 +1338,7 @@ const Daemon = struct {
13391338 client.has_pending_output = true;
13401339 }
13411340 if (self.clients.items.len > 0) {
1342- posix.kill(self.pid, posix.SIG.WINCH) catch |err| {
1341+ lib_posix.kill(self.pid, lib_posix.SIG.WINCH) catch |err| {
13431342 std.log.warn("failed to send SIGWINCH err={s}", .{@errorName(err)});
13441343 };
13451344 }
......@@ -1580,7 +1579,7 @@ fn help(io: std.Io) !void {
15801579 }
15811580
15821581 fn tail(alloc: std.mem.Allocator, client_socket_fds: std.ArrayList(i32), detached: bool, is_run_cmd: bool) !u8 {
1583- var poll_fds = try std.ArrayList(posix.pollfd).initCapacity(alloc, 4);
1582+ var poll_fds = try std.ArrayList(lib_posix.pollfd).initCapacity(alloc, 4);
15841583 defer poll_fds.deinit(alloc);
15851584
15861585 var read_buf = try ipc.SocketBuffer.init(alloc);
......@@ -1599,7 +1598,7 @@ fn tail(alloc: std.mem.Allocator, client_socket_fds: std.ArrayList(i32), detache
15991598 for (client_socket_fds.items) |client_sock_fd| {
16001599 try poll_fds.append(alloc, .{
16011600 .fd = client_sock_fd,
1602- .events = posix.POLL.IN,
1601+ .events = lib_posix.POLL.IN,
16031602 .revents = 0,
16041603 });
16051604 }
......@@ -1607,20 +1606,20 @@ fn tail(alloc: std.mem.Allocator, client_socket_fds: std.ArrayList(i32), detache
16071606 // Poll for write if we have pending data
16081607 if (stdout_buf.items.len > 0) {
16091608 try poll_fds.append(alloc, .{
1610- .fd = posix.STDOUT_FILENO,
1611- .events = posix.POLL.OUT,
1609+ .fd = lib_posix.STDOUT_FILENO,
1610+ .events = lib_posix.POLL.OUT,
16121611 .revents = 0,
16131612 });
16141613 }
16151614
1616- _ = posix.poll(poll_fds.items, -1) catch |err| {
1615+ _ = lib_posix.poll(poll_fds.items, -1) catch |err| {
16171616 if (err == error.Interrupted) continue; // EINTR from signal, loop again
16181617 return err;
16191618 };
16201619
16211620 // Handle socket read (incoming Output messages from daemon)
16221621 for (poll_fds.items) |*poll_fd| {
1623- if (poll_fd.revents & posix.POLL.IN != 0) {
1622+ if (poll_fd.revents & lib_posix.POLL.IN != 0) {
16241623 const n = read_buf.read(poll_fd.fd) catch |err| {
16251624 if (err == error.WouldBlock) continue;
16261625 if (err == error.ConnectionResetByPeer or err == error.BrokenPipe) {
......@@ -1642,7 +1641,7 @@ fn tail(alloc: std.mem.Allocator, client_socket_fds: std.ArrayList(i32), detache
16421641 switch (msg.header.tag) {
16431642 .Ack => {
16441643 if (detached) {
1645- _ = lib_posix.write(posix.STDOUT_FILENO, "command sent!\n") catch |err| blk: {
1644+ _ = lib_posix.write(lib_posix.STDOUT_FILENO, "command sent!\n") catch |err| blk: {
16461645 if (err == error.WouldBlock) break :blk 0;
16471646 return err;
16481647 };
......@@ -1704,7 +1703,7 @@ fn tail(alloc: std.mem.Allocator, client_socket_fds: std.ArrayList(i32), detache
17041703 if (task_complete_code) |exit_code| {
17051704 // Flush any remaining output before returning
17061705 flush_loop: while (stdout_buf.items.len > 0) {
1707- const n = lib_posix.write(posix.STDOUT_FILENO, stdout_buf.items) catch |err| {
1706+ const n = lib_posix.write(lib_posix.STDOUT_FILENO, stdout_buf.items) catch |err| {
17081707 if (err == error.WouldBlock) break :flush_loop;
17091708 return err;
17101709 };
......@@ -1714,7 +1713,7 @@ fn tail(alloc: std.mem.Allocator, client_socket_fds: std.ArrayList(i32), detache
17141713 }
17151714
17161715 if (stdout_buf.items.len > 0) {
1717- const n = lib_posix.write(posix.STDOUT_FILENO, stdout_buf.items) catch |err| blk: {
1716+ const n = lib_posix.write(lib_posix.STDOUT_FILENO, stdout_buf.items) catch |err| blk: {
17181717 if (err == error.WouldBlock) break :blk 0;
17191718 return err;
17201719 };
......@@ -1725,7 +1724,7 @@ fn tail(alloc: std.mem.Allocator, client_socket_fds: std.ArrayList(i32), detache
17251724
17261725 // Check for HUP/ERR on any socket
17271726 for (poll_fds.items) |poll_fd| {
1728- if (poll_fd.revents & (posix.POLL.HUP | posix.POLL.ERR | posix.POLL.NVAL) != 0) {
1727+ if (poll_fd.revents & (lib_posix.POLL.HUP | lib_posix.POLL.ERR | lib_posix.POLL.NVAL) != 0) {
17291728 return 0;
17301729 }
17311730 }
......@@ -2022,7 +2021,7 @@ fn kill(alloc: std.mem.Allocator, io: std.Io, cfg: *Cfg, session_name: []const u
20222021 // accept backlog.
20232022 var drain: [256]u8 = undefined;
20242023 while (true) {
2025- const n = posix.read(fd, &drain) catch break;
2024+ const n = lib_posix.read(fd, &drain) catch break;
20262025 if (n == 0) break;
20272026 }
20282027
......@@ -2179,8 +2178,8 @@ fn fetchHistory(
21792178 errdefer result.deinit(alloc);
21802179
21812180 while (true) {
2182- var poll_fds = [_]posix.pollfd{.{ .fd = fd, .events = posix.POLL.IN, .revents = 0 }};
2183- const poll_result = posix.poll(&poll_fds, 5000) catch return error.Timeout;
2181+ var poll_fds = [_]lib_posix.pollfd{.{ .fd = fd, .events = lib_posix.POLL.IN, .revents = 0 }};
2182+ const poll_result = lib_posix.poll(&poll_fds, 5000) catch return error.Timeout;
21842183 if (poll_result == 0) {
21852184 return error.Timeout;
21862185 }
......@@ -2236,8 +2235,8 @@ fn history(alloc: std.mem.Allocator, io: std.Io, cfg: *Cfg, session_name: []cons
22362235 defer sb.deinit();
22372236
22382237 while (true) {
2239- var poll_fds = [_]posix.pollfd{.{ .fd = fd, .events = posix.POLL.IN, .revents = 0 }};
2240- const poll_result = posix.poll(&poll_fds, 5000) catch return;
2238+ var poll_fds = [_]lib_posix.pollfd{.{ .fd = fd, .events = lib_posix.POLL.IN, .revents = 0 }};
2239+ const poll_result = lib_posix.poll(&poll_fds, 5000) catch return;
22412240 if (poll_result == 0) {
22422241 std.log.err("timeout waiting for history response", .{});
22432242 return;
......@@ -2248,7 +2247,7 @@ fn history(alloc: std.mem.Allocator, io: std.Io, cfg: *Cfg, session_name: []cons
22482247
22492248 while (sb.next()) |msg| {
22502249 if (msg.header.tag == .History) {
2251- _ = lib_posix.write(posix.STDOUT_FILENO, msg.payload) catch return;
2250+ _ = lib_posix.write(lib_posix.STDOUT_FILENO, msg.payload) catch return;
22522251 return;
22532252 }
22542253 }
......@@ -2314,15 +2313,15 @@ fn attach(daemon: *Daemon) !void {
23142313 // skip terminal setup entirely rather than applying undefined stack bytes
23152314 // via tcsetattr.
23162315 var orig_termios: cross.c.termios = undefined;
2317- const stdin_is_tty = cross.c.tcgetattr(posix.STDIN_FILENO, &orig_termios) == 0;
2316+ const stdin_is_tty = cross.c.tcgetattr(lib_posix.STDIN_FILENO, &orig_termios) == 0;
23182317
23192318 defer {
23202319 if (stdin_is_tty) {
2321- _ = cross.c.tcsetattr(posix.STDIN_FILENO, cross.c.TCSAFLUSH, &orig_termios);
2320+ _ = cross.c.tcsetattr(lib_posix.STDIN_FILENO, cross.c.TCSAFLUSH, &orig_termios);
23222321 }
23232322 // Reset terminal modes on detach
23242323 const restore_seq = "\x1bc";
2325- _ = lib_posix.write(posix.STDOUT_FILENO, restore_seq) catch {};
2324+ _ = lib_posix.write(lib_posix.STDOUT_FILENO, restore_seq) catch {};
23262325 }
23272326
23282327 if (stdin_is_tty) {
......@@ -2340,13 +2339,13 @@ fn attach(daemon: *Daemon) !void {
23402339 raw_termios.c_cc[cross.c.VMIN] = 1; // Minimum chars to read: return after 1 byte
23412340 raw_termios.c_cc[cross.c.VTIME] = 0; // Read timeout: no timeout, return immediately
23422341
2343- _ = cross.c.tcsetattr(posix.STDIN_FILENO, cross.c.TCSANOW, &raw_termios);
2342+ _ = cross.c.tcsetattr(lib_posix.STDIN_FILENO, cross.c.TCSANOW, &raw_termios);
23442343 }
23452344
23462345 // Clear screen before attaching. This provides a clean slate before
23472346 // the session restore.
23482347 const clear_seq = "\x1b[2J\x1b[H";
2349- _ = try lib_posix.write(posix.STDOUT_FILENO, clear_seq);
2348+ _ = try lib_posix.write(lib_posix.STDOUT_FILENO, clear_seq);
23502349
23512350 const looper = try clientLoop(client_sock);
23522351 switch (looper.kind) {
......@@ -2399,13 +2398,13 @@ fn writeFile(daemon: *Daemon, file_path: []const u8) !void {
23992398 try w.interface.print("session \"{s}\" created\n", .{daemon.session_name});
24002399 try w.interface.flush();
24012400 }
2402- const stdin_fd = posix.STDIN_FILENO;
2401+ const stdin_fd = lib_posix.STDIN_FILENO;
24032402 var stdin_buf = try std.ArrayList(u8).initCapacity(daemon.alloc, 4096);
24042403 defer stdin_buf.deinit(daemon.alloc);
24052404
24062405 while (true) {
24072406 var tmp: [4096]u8 = undefined;
2408- const n = posix.read(stdin_fd, &tmp) catch |err| {
2407+ const n = lib_posix.read(stdin_fd, &tmp) catch |err| {
24092408 if (err == error.WouldBlock) break;
24102409 return err;
24112410 };
......@@ -2680,22 +2679,22 @@ fn clientLoop(client_sock_fd: i32) !ClientResult {
26802679 defer lib_posix.close(client_sock_fd);
26812680
26822681 try openSignalPipe();
2683- installWakeHandler(@intFromEnum(posix.SIG.WINCH));
2682+ installWakeHandler(@intFromEnum(lib_posix.SIG.WINCH));
26842683
26852684 // Make socket non-blocking to avoid blocking on writes
2686- var sock_flags = try lib_posix.fcntl(client_sock_fd, posix.F.GETFL, 0);
2685+ var sock_flags = try lib_posix.fcntl(client_sock_fd, lib_posix.F.GETFL, 0);
26872686 sock_flags |= O_NONBLOCK;
2688- _ = try lib_posix.fcntl(client_sock_fd, posix.F.SETFL, sock_flags);
2687+ _ = try lib_posix.fcntl(client_sock_fd, lib_posix.F.SETFL, sock_flags);
26892688
26902689 // Buffer for outgoing socket writes
26912690 var sock_write_buf = try std.ArrayList(u8).initCapacity(alloc, 4096);
26922691 defer sock_write_buf.deinit(alloc);
26932692
26942693 // Send init message with terminal size (buffered)
2695- const size = ipc.getTerminalSize(posix.STDOUT_FILENO);
2694+ const size = ipc.getTerminalSize(lib_posix.STDOUT_FILENO);
26962695 try ipc.appendMessage(alloc, &sock_write_buf, .Init, std.mem.asBytes(&size));
26972696
2698- var poll_fds = try std.ArrayList(posix.pollfd).initCapacity(alloc, 4);
2697+ var poll_fds = try std.ArrayList(lib_posix.pollfd).initCapacity(alloc, 4);
26992698 defer poll_fds.deinit(alloc);
27002699
27012700 var read_buf = try ipc.SocketBuffer.init(alloc);
......@@ -2704,28 +2703,28 @@ fn clientLoop(client_sock_fd: i32) !ClientResult {
27042703 var stdout_buf = try std.ArrayList(u8).initCapacity(alloc, 4096);
27052704 defer stdout_buf.deinit(alloc);
27062705
2707- const stdin_fd = posix.STDIN_FILENO;
2706+ const stdin_fd = lib_posix.STDIN_FILENO;
27082707
27092708 // Make stdin non-blocking. O_NONBLOCK is set on the open file description,
27102709 // which is shared with the parent shell; restore on exit to avoid
27112710 // corrupting the parent's stdin.
2712- const stdin_orig_flags = try lib_posix.fcntl(stdin_fd, posix.F.GETFL, 0);
2713- _ = try lib_posix.fcntl(stdin_fd, posix.F.SETFL, stdin_orig_flags | O_NONBLOCK);
2714- defer _ = lib_posix.fcntl(stdin_fd, posix.F.SETFL, stdin_orig_flags) catch {};
2711+ const stdin_orig_flags = try lib_posix.fcntl(stdin_fd, lib_posix.F.GETFL, 0);
2712+ _ = try lib_posix.fcntl(stdin_fd, lib_posix.F.SETFL, stdin_orig_flags | O_NONBLOCK);
2713+ defer _ = lib_posix.fcntl(stdin_fd, lib_posix.F.SETFL, stdin_orig_flags) catch {};
27152714
27162715 while (true) {
27172716 poll_fds.clearRetainingCapacity();
27182717
27192718 try poll_fds.append(alloc, .{
27202719 .fd = stdin_fd,
2721- .events = posix.POLL.IN,
2720+ .events = lib_posix.POLL.IN,
27222721 .revents = 0,
27232722 });
27242723
27252724 // Poll socket for read, and also for write if we have pending data
2726- var sock_events: i16 = posix.POLL.IN;
2725+ var sock_events: i16 = lib_posix.POLL.IN;
27272726 if (sock_write_buf.items.len > 0) {
2728- sock_events |= posix.POLL.OUT;
2727+ sock_events |= lib_posix.POLL.OUT;
27292728 }
27302729 try poll_fds.append(alloc, .{
27312730 .fd = client_sock_fd,
......@@ -2733,29 +2732,29 @@ fn clientLoop(client_sock_fd: i32) !ClientResult {
27332732 .revents = 0,
27342733 });
27352734
2736- try poll_fds.append(alloc, .{ .fd = sig_pipe[0], .events = posix.POLL.IN, .revents = 0 });
2735+ try poll_fds.append(alloc, .{ .fd = sig_pipe[0], .events = lib_posix.POLL.IN, .revents = 0 });
27372736
27382737 if (stdout_buf.items.len > 0) {
27392738 try poll_fds.append(alloc, .{
2740- .fd = posix.STDOUT_FILENO,
2741- .events = posix.POLL.OUT,
2739+ .fd = lib_posix.STDOUT_FILENO,
2740+ .events = lib_posix.POLL.OUT,
27422741 .revents = 0,
27432742 });
27442743 }
27452744
2746- _ = try posix.poll(poll_fds.items, -1);
2745+ _ = try lib_posix.poll(poll_fds.items, -1);
27472746
2748- if (poll_fds.items[2].revents & posix.POLL.IN != 0) {
2747+ if (poll_fds.items[2].revents & lib_posix.POLL.IN != 0) {
27492748 drainSignalPipe();
2750- const next_size = ipc.getTerminalSize(posix.STDOUT_FILENO);
2749+ const next_size = ipc.getTerminalSize(lib_posix.STDOUT_FILENO);
27512750 try ipc.appendMessage(alloc, &sock_write_buf, .Resize, std.mem.asBytes(&next_size));
27522751 }
27532752
27542753 // Handle stdin -> socket (Input)
2755- const inp_flags = (posix.POLL.IN | posix.POLL.HUP | posix.POLL.ERR | posix.POLL.NVAL);
2754+ const inp_flags = (lib_posix.POLL.IN | lib_posix.POLL.HUP | lib_posix.POLL.ERR | lib_posix.POLL.NVAL);
27562755 if (poll_fds.items[0].revents & inp_flags != 0) {
27572756 var buf: [4096]u8 = undefined;
2758- const n_opt: ?usize = posix.read(stdin_fd, &buf) catch |err| blk: {
2757+ const n_opt: ?usize = lib_posix.read(stdin_fd, &buf) catch |err| blk: {
27592758 if (err == error.WouldBlock) break :blk null;
27602759 return err;
27612760 };
......@@ -2778,7 +2777,7 @@ fn clientLoop(client_sock_fd: i32) !ClientResult {
27782777 }
27792778
27802779 // Handle socket read (incoming Output messages from daemon)
2781- if (poll_fds.items[1].revents & posix.POLL.IN != 0) {
2780+ if (poll_fds.items[1].revents & lib_posix.POLL.IN != 0) {
27822781 const n = read_buf.read(client_sock_fd) catch |err| {
27832782 if (err == error.WouldBlock) continue;
27842783 if (err == error.ConnectionResetByPeer or err == error.BrokenPipe) {
......@@ -2803,7 +2802,7 @@ fn clientLoop(client_sock_fd: i32) !ClientResult {
28032802 .Resize => {
28042803 // daemon is asking for the client's window size usually in response
28052804 // to this client being set as leader.
2806- const next_size = ipc.getTerminalSize(posix.STDOUT_FILENO);
2805+ const next_size = ipc.getTerminalSize(lib_posix.STDOUT_FILENO);
28072806 try ipc.appendMessage(
28082807 alloc,
28092808 &sock_write_buf,
......@@ -2821,7 +2820,7 @@ fn clientLoop(client_sock_fd: i32) !ClientResult {
28212820 }
28222821
28232822 // Handle socket write (flush buffered messages to daemon)
2824- if (poll_fds.items[1].revents & posix.POLL.OUT != 0) {
2823+ if (poll_fds.items[1].revents & lib_posix.POLL.OUT != 0) {
28252824 if (sock_write_buf.items.len > 0) {
28262825 const n = lib_posix.write(client_sock_fd, sock_write_buf.items) catch |err| blk: {
28272826 if (err == error.WouldBlock) break :blk 0;
......@@ -2838,7 +2837,7 @@ fn clientLoop(client_sock_fd: i32) !ClientResult {
28382837 }
28392838
28402839 if (stdout_buf.items.len > 0) {
2841- const n = lib_posix.write(posix.STDOUT_FILENO, stdout_buf.items) catch |err| blk: {
2840+ const n = lib_posix.write(lib_posix.STDOUT_FILENO, stdout_buf.items) catch |err| blk: {
28422841 if (err == error.WouldBlock) break :blk 0;
28432842 return err;
28442843 };
......@@ -2847,7 +2846,7 @@ fn clientLoop(client_sock_fd: i32) !ClientResult {
28472846 }
28482847 }
28492848
2850- if (poll_fds.items[1].revents & (posix.POLL.HUP | posix.POLL.ERR | posix.POLL.NVAL) != 0) {
2849+ if (poll_fds.items[1].revents & (lib_posix.POLL.HUP | lib_posix.POLL.ERR | lib_posix.POLL.NVAL) != 0) {
28512850 std.log.info("poll hup|err|nval", .{});
28522851 return ClientResult{ .kind = .detach, .session_name = null };
28532852 }
......@@ -2861,7 +2860,7 @@ fn daemonLoop(daemon: *Daemon, server_sock_fd: i32, pty_fd: i32) !void {
28612860 daemon.pty_fd = pty_fd;
28622861 try openSignalPipe();
28632862 installWakeHandler(@intFromEnum(lib_posix.SIG.TERM));
2864- var poll_fds = try std.ArrayList(posix.pollfd).initCapacity(daemon.alloc, 8);
2863+ var poll_fds = try std.ArrayList(lib_posix.pollfd).initCapacity(daemon.alloc, 8);
28652864 defer poll_fds.deinit(daemon.alloc);
28662865
28672866 const init_size = ipc.getTerminalSize(pty_fd);
......@@ -2885,13 +2884,13 @@ fn daemonLoop(daemon: *Daemon, server_sock_fd: i32, pty_fd: i32) !void {
28852884
28862885 try poll_fds.append(daemon.alloc, .{
28872886 .fd = server_sock_fd,
2888- .events = posix.POLL.IN,
2887+ .events = lib_posix.POLL.IN,
28892888 .revents = 0,
28902889 });
28912890
2892- var pty_events: i16 = posix.POLL.IN;
2891+ var pty_events: i16 = lib_posix.POLL.IN;
28932892 if (daemon.pty_write_buf.items.len > 0) {
2894- pty_events |= posix.POLL.OUT;
2893+ pty_events |= lib_posix.POLL.OUT;
28952894 }
28962895 try poll_fds.append(daemon.alloc, .{
28972896 .fd = pty_fd,
......@@ -2899,12 +2898,12 @@ fn daemonLoop(daemon: *Daemon, server_sock_fd: i32, pty_fd: i32) !void {
28992898 .revents = 0,
29002899 });
29012900
2902- try poll_fds.append(daemon.alloc, .{ .fd = sig_pipe[0], .events = posix.POLL.IN, .revents = 0 });
2901+ try poll_fds.append(daemon.alloc, .{ .fd = sig_pipe[0], .events = lib_posix.POLL.IN, .revents = 0 });
29032902
29042903 for (daemon.clients.items) |client| {
2905- var events: i16 = posix.POLL.IN;
2904+ var events: i16 = lib_posix.POLL.IN;
29062905 if (client.has_pending_output) {
2907- events |= posix.POLL.OUT;
2906+ events |= lib_posix.POLL.OUT;
29082907 }
29092908 try poll_fds.append(daemon.alloc, .{
29102909 .fd = client.socket_fd,
......@@ -2913,9 +2912,9 @@ fn daemonLoop(daemon: *Daemon, server_sock_fd: i32, pty_fd: i32) !void {
29132912 });
29142913 }
29152914
2916- _ = try posix.poll(poll_fds.items, -1);
2915+ _ = try lib_posix.poll(poll_fds.items, -1);
29172916
2918- if (poll_fds.items[2].revents & posix.POLL.IN != 0) {
2917+ if (poll_fds.items[2].revents & lib_posix.POLL.IN != 0) {
29192918 drainSignalPipe();
29202919 std.log.info(
29212920 "SIGTERM received, shutting down gracefully session={s}",
......@@ -2924,15 +2923,15 @@ fn daemonLoop(daemon: *Daemon, server_sock_fd: i32, pty_fd: i32) !void {
29242923 break :daemon_loop;
29252924 }
29262925
2927- if (poll_fds.items[0].revents & (posix.POLL.ERR | posix.POLL.HUP | posix.POLL.NVAL) != 0) {
2926+ if (poll_fds.items[0].revents & (lib_posix.POLL.ERR | lib_posix.POLL.HUP | lib_posix.POLL.NVAL) != 0) {
29282927 std.log.err("server socket error revents={d}", .{poll_fds.items[0].revents});
29292928 break :daemon_loop;
2930- } else if (poll_fds.items[0].revents & posix.POLL.IN != 0) {
2929+ } else if (poll_fds.items[0].revents & lib_posix.POLL.IN != 0) {
29312930 const client_fd = try lib_posix.accept(
29322931 server_sock_fd,
29332932 null,
29342933 null,
2935- posix.SOCK.NONBLOCK | posix.SOCK.CLOEXEC,
2934+ lib_posix.SOCK.NONBLOCK | lib_posix.SOCK.CLOEXEC,
29362935 );
29372936 const client = try daemon.alloc.create(Client);
29382937 client.* = Client{
......@@ -2953,14 +2952,14 @@ fn daemonLoop(daemon: *Daemon, server_sock_fd: i32, pty_fd: i32) !void {
29532952 );
29542953 }
29552954
2956- const inp_flags = posix.POLL.IN | posix.POLL.HUP | posix.POLL.ERR | posix.POLL.NVAL;
2955+ const inp_flags = lib_posix.POLL.IN | lib_posix.POLL.HUP | lib_posix.POLL.ERR | lib_posix.POLL.NVAL;
29572956 if (poll_fds.items[1].revents & inp_flags != 0) {
29582957 // Read from PTY. Buffer is sized to N_TTY_BUF_SIZE (4096): the hard
29592958 // kernel limit for the N_TTY line discipline. A larger buffer doesn't
29602959 // help: each read() from a PTY master returns at most 4096 bytes
29612960 // regardless of the userspace buffer size.
29622961 var buf: [4096]u8 = undefined;
2963- const n_opt: ?usize = posix.read(pty_fd, &buf) catch |err| blk: {
2962+ const n_opt: ?usize = lib_posix.read(pty_fd, &buf) catch |err| blk: {
29642963 if (err == error.WouldBlock) break :blk null;
29652964 break :blk 0;
29662965 };
......@@ -3039,7 +3038,7 @@ fn daemonLoop(daemon: *Daemon, server_sock_fd: i32, pty_fd: i32) !void {
30393038 }
30403039 }
30413040
3042- if (poll_fds.items[1].revents & posix.POLL.OUT != 0) {
3041+ if (poll_fds.items[1].revents & lib_posix.POLL.OUT != 0) {
30433042 while (daemon.pty_write_buf.items.len > 0) {
30443043 const n = lib_posix.write(pty_fd, daemon.pty_write_buf.items) catch |err| {
30453044 if (err != error.WouldBlock) {
......@@ -3069,7 +3068,7 @@ fn daemonLoop(daemon: *Daemon, server_sock_fd: i32, pty_fd: i32) !void {
30693068 const client = daemon.clients.items[i];
30703069 const revents = poll_fds.items[i + 3].revents;
30713070
3072- if (revents & posix.POLL.IN != 0) {
3071+ if (revents & lib_posix.POLL.IN != 0) {
30733072 const n = client.read_buf.read(client.socket_fd) catch |err| {
30743073 if (err == error.WouldBlock) continue;
30753074 std.log.debug(
......@@ -3123,7 +3122,7 @@ fn daemonLoop(daemon: *Daemon, server_sock_fd: i32, pty_fd: i32) !void {
31233122 }
31243123 }
31253124
3126- if (revents & posix.POLL.OUT != 0) {
3125+ if (revents & lib_posix.POLL.OUT != 0) {
31273126 // Flush pending output buffers
31283127 const n = lib_posix.write(client.socket_fd, client.write_buf.items) catch |err| blk: {
31293128 if (err == error.WouldBlock) break :blk 0;
......@@ -3142,7 +3141,7 @@ fn daemonLoop(daemon: *Daemon, server_sock_fd: i32, pty_fd: i32) !void {
31423141 }
31433142 }
31443143
3145- if (revents & (posix.POLL.HUP | posix.POLL.ERR | posix.POLL.NVAL) != 0) {
3144+ if (revents & (lib_posix.POLL.HUP | lib_posix.POLL.ERR | lib_posix.POLL.NVAL) != 0) {
31463145 const last = daemon.closeClient(client, i, false);
31473146 if (last) break :daemon_loop;
31483147 }
......@@ -3150,7 +3149,7 @@ fn daemonLoop(daemon: *Daemon, server_sock_fd: i32, pty_fd: i32) !void {
31503149 }
31513150 }
31523151
3153-fn wakeSignalPipe(_: std.os.linux.SIG, _: *const posix.siginfo_t, _: ?*anyopaque) callconv(.c) void {
3152+fn wakeSignalPipe(_: std.os.linux.SIG, _: *const lib_posix.siginfo_t, _: ?*anyopaque) callconv(.c) void {
31543153 const saved = std.c._errno().*;
31553154 _ = std.c.write(sig_pipe[1], "x", 1);
31563155 std.c._errno().* = saved;
......@@ -3160,19 +3159,19 @@ fn wakeSignalPipe(_: std.os.linux.SIG, _: *const posix.siginfo_t, _: ?*anyopaque
31603159 // setting wakes the loop. The handler writes to sig_pipe instead; poll()
31613160 // wakes on its read end.
31623161 fn installWakeHandler(sig: u6) void {
3163- const act: posix.Sigaction = .{
3162+ const act: lib_posix.Sigaction = .{
31643163 .handler = .{ .sigaction = wakeSignalPipe },
3165- .mask = posix.sigemptyset(),
3166- .flags = posix.SA.SIGINFO,
3164+ .mask = lib_posix.sigemptyset(),
3165+ .flags = lib_posix.SA.SIGINFO,
31673166 };
3168- posix.sigaction(@as(posix.SIG, @enumFromInt(sig)), &act, null);
3167+ lib_posix.sigaction(@as(lib_posix.SIG, @enumFromInt(sig)), &act, null);
31693168 }
31703169
31713170 fn ignoreSigpipe() void {
3172- const act: posix.Sigaction = .{
3173- .handler = .{ .handler = posix.SIG.IGN },
3174- .mask = posix.sigemptyset(),
3171+ const act: lib_posix.Sigaction = .{
3172+ .handler = .{ .handler = lib_posix.SIG.IGN },
3173+ .mask = lib_posix.sigemptyset(),
31753174 .flags = 0,
31763175 };
3177- posix.sigaction(posix.SIG.PIPE, &act, null);
3176+ lib_posix.sigaction(lib_posix.SIG.PIPE, &act, null);
31783177 }
+151, -8
......@@ -6,6 +6,7 @@ const mem = std.mem;
66 const native_os = builtin.os.tag;
77 const use_libc = builtin.link_libc;
88 const linux = std.os.linux;
9+const cast = std.math.cast;
910
1011 /// A libc-compatible API layer.
1112 const system = if (use_libc)
......@@ -23,28 +24,62 @@ else switch (native_os) {
2324 },
2425 };
2526
26-pub const SIG = system.SIG;
2727 const E = system.E;
2828 const PATH_MAX = system.PATH_MAX;
2929 const pid_t = system.pid_t;
30-const AT = system.AT;
3130 const lfs64_abi = native_os == .linux and builtin.link_libc and (builtin.abi.isGnu() or builtin.abi.isAndroid());
3231 const uid_t = system.uid_t;
33-const fd_t = system.fd_t;
3432 const mode_t = system.mode_t;
3533 const socket_t = fd_t;
36-const SOCK = system.SOCK;
37-const F = system.F;
38-const O = system.O;
39-const AF = system.AF;
4034 const FD_CLOEXEC = system.FD_CLOEXEC;
41-const sockaddr = system.sockaddr;
35+pub const SA = system.SA;
36+pub const fd_t = system.fd_t;
37+pub const O = system.O;
38+pub const F = system.F;
39+pub const sigset_t = system.sigset_t;
40+pub const nfds_t = system.nfds_t;
41+pub const SOCK = system.SOCK;
42+pub const AT = system.AT;
43+pub const AF = system.AF;
44+pub const sockaddr = system.sockaddr;
4245 pub const socklen_t = system.socklen_t;
46+pub const pollfd = system.pollfd;
47+pub const POLL = system.POLL;
48+pub const STDERR_FILENO = system.STDERR_FILENO;
49+pub const STDIN_FILENO = system.STDIN_FILENO;
50+pub const STDOUT_FILENO = system.STDOUT_FILENO;
51+pub const Sigaction = system.Sigaction;
52+pub const SIG = system.SIG;
53+pub const siginfo_t = system.siginfo_t;
4354
4455 pub fn getuid() uid_t {
4556 return system.getuid();
4657 }
4758
59+/// Return an empty sigset_t.
60+pub fn sigemptyset() sigset_t {
61+ if (builtin.link_libc) {
62+ var set: sigset_t = undefined;
63+ switch (errno(system.sigemptyset(&set))) {
64+ .SUCCESS => return set,
65+ else => unreachable,
66+ }
67+ }
68+ return system.sigemptyset();
69+}
70+
71+/// Examine and change a signal action.
72+pub fn sigaction(sig: SIG, noalias act: ?*const Sigaction, noalias oact: ?*Sigaction) void {
73+ switch (errno(system.sigaction(sig, act, oact))) {
74+ .SUCCESS => return,
75+ // EINVAL means the signal is either invalid or some signal that cannot have its action
76+ // changed. For POSIX, this means SIGKILL/SIGSTOP. For e.g. Solaris, this also includes the
77+ // non-standard SIGWAITING, SIGCANCEL, and SIGLWP. Either way, programmer error.
78+ .INVAL => unreachable,
79+ else => unreachable,
80+ }
81+}
82+
4883 /// Get an environment variable.
4984 /// See also `getenvZ`.
5085 pub fn getenv(key: []const u8) ?[:0]const u8 {
......@@ -555,6 +590,78 @@ pub fn setsid() SetSidError!pid_t {
555590 }
556591 }
557592
593+pub const ReadError = error{
594+ InputOutput,
595+ SystemResources,
596+ IsDir,
597+ OperationAborted,
598+ BrokenPipe,
599+ ConnectionResetByPeer,
600+ ConnectionTimedOut,
601+ NotOpenForReading,
602+ SocketNotConnected,
603+
604+ /// This error occurs when no global event loop is configured,
605+ /// and reading from the file descriptor would block.
606+ WouldBlock,
607+
608+ /// reading a timerfd with CANCEL_ON_SET will lead to this error
609+ /// when the clock goes through a discontinuous change
610+ Canceled,
611+
612+ /// In WASI, this error occurs when the file descriptor does
613+ /// not hold the required rights to read from it.
614+ AccessDenied,
615+
616+ /// This error occurs in Linux if the process to be read from
617+ /// no longer exists.
618+ ProcessNotFound,
619+
620+ /// Unable to read file due to lock.
621+ LockViolation,
622+} || UnexpectedError;
623+
624+/// Returns the number of bytes that were read, which can be less than
625+/// buf.len. If 0 bytes were read, that means EOF.
626+/// If `fd` is opened in non blocking mode, the function will return error.WouldBlock
627+/// when EAGAIN is received.
628+///
629+/// Linux has a limit on how many bytes may be transferred in one `read` call, which is `0x7ffff000`
630+/// on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as
631+/// well as stuffing the errno codes into the last `4096` values. This is noted on the `read` man page.
632+/// The limit on Darwin is `0x7fffffff`, trying to read more than that returns EINVAL.
633+/// The corresponding POSIX limit is `maxInt(isize)`.
634+pub fn read(fd: fd_t, buf: []u8) ReadError!usize {
635+ if (buf.len == 0) return 0;
636+ // Prevents EINVAL.
637+ const max_count = switch (native_os) {
638+ .linux => 0x7ffff000,
639+ .macos, .ios, .watchos, .tvos, .visionos => maxInt(i32),
640+ else => maxInt(isize),
641+ };
642+ while (true) {
643+ const rc = system.read(fd, buf.ptr, @min(buf.len, max_count));
644+ switch (errno(rc)) {
645+ .SUCCESS => return @intCast(rc),
646+ .INTR => continue,
647+ .INVAL => unreachable,
648+ .FAULT => unreachable,
649+ .SRCH => return error.ProcessNotFound,
650+ .AGAIN => return error.WouldBlock,
651+ .CANCELED => return error.Canceled,
652+ .BADF => return error.NotOpenForReading, // Can be a race condition.
653+ .IO => return error.InputOutput,
654+ .ISDIR => return error.IsDir,
655+ .NOBUFS => return error.SystemResources,
656+ .NOMEM => return error.SystemResources,
657+ .NOTCONN => return error.SocketNotConnected,
658+ .CONNRESET => return error.ConnectionResetByPeer,
659+ .TIMEDOUT => return error.ConnectionTimedOut,
660+ else => |err| return unexpectedErrno(err),
661+ }
662+ }
663+}
664+
558665 /// Open and possibly create a file. Keeps trying if it gets interrupted.
559666 /// On Windows, `file_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
560667 /// On WASI, `file_path` should be encoded as valid UTF-8.
......@@ -1106,6 +1213,30 @@ pub fn waitpid(pid: pid_t, flags: u32) WaitPidResult {
11061213 }
11071214 }
11081215
1216+pub const PollError = error{
1217+ /// The network subsystem has failed.
1218+ NetworkSubsystemFailed,
1219+
1220+ /// The kernel had no space to allocate file descriptor tables.
1221+ SystemResources,
1222+} || UnexpectedError;
1223+
1224+pub fn poll(fds: []pollfd, timeout: i32) PollError!usize {
1225+ while (true) {
1226+ const fds_count = cast(nfds_t, fds.len) orelse return error.SystemResources;
1227+ const rc = system.poll(fds.ptr, fds_count, timeout);
1228+ switch (errno(rc)) {
1229+ .SUCCESS => return @intCast(rc),
1230+ .FAULT => unreachable,
1231+ .INTR => continue,
1232+ .INVAL => unreachable,
1233+ .NOMEM => return error.SystemResources,
1234+ else => |err| return unexpectedErrno(err),
1235+ }
1236+ }
1237+ unreachable;
1238+}
1239+
11091240 /// Call this when you made a syscall or something that sets errno
11101241 /// and you get an unexpected error.
11111242 fn unexpectedErrno(err: E) UnexpectedError {
......@@ -1168,3 +1299,15 @@ pub fn initUnix(path: []const u8) !Address {
11681299
11691300 return Address{ .un = sock_addr };
11701301 }
1302+
1303+const KillError = error{ ProcessNotFound, PermissionDenied } || UnexpectedError;
1304+
1305+pub fn kill(pid: pid_t, sig: SIG) KillError!void {
1306+ switch (errno(system.kill(pid, sig))) {
1307+ .SUCCESS => return,
1308+ .INVAL => unreachable, // invalid signal
1309+ .PERM => return error.PermissionDenied,
1310+ .SRCH => return error.ProcessNotFound,
1311+ else => |err| return unexpectedErrno(err),
1312+ }
1313+}
+5, -6
......@@ -1,5 +1,4 @@
11 const std = @import("std");
2-const posix = std.posix;
32 const lib_posix = @import("posix.zig");
43
54 pub fn getSeshPrefix() []const u8 {
......@@ -31,7 +30,7 @@ pub fn getSeshName(alloc: std.mem.Allocator, sesh: []const u8) ![]const u8 {
3130
3231 pub fn sessionConnect(sesh: []const u8) !i32 {
3332 var unix_addr = try lib_posix.initUnix(sesh);
34- const socket_fd = try lib_posix.socket(posix.AF.UNIX, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
33+ const socket_fd = try lib_posix.socket(lib_posix.AF.UNIX, lib_posix.SOCK.STREAM | lib_posix.SOCK.CLOEXEC, 0);
3534 errdefer lib_posix.close(socket_fd);
3635 try lib_posix.connect(socket_fd, &unix_addr.any, unix_addr.getOsSockLen());
3736 return socket_fd;
......@@ -62,8 +61,8 @@ pub fn createSocket(sesh: []const u8) !i32 {
6261 // SOCK.STREAM: Reliable, bidirectional communication
6362 // SOCK.NONBLOCK: Set socket to non-blocking
6463 const fd = try lib_posix.socket(
65- posix.AF.UNIX,
66- posix.SOCK.STREAM | posix.SOCK.NONBLOCK | posix.SOCK.CLOEXEC,
64+ lib_posix.AF.UNIX,
65+ lib_posix.SOCK.STREAM | lib_posix.SOCK.NONBLOCK | lib_posix.SOCK.CLOEXEC,
6766 0,
6867 );
6968 errdefer lib_posix.close(fd);
......@@ -78,7 +77,7 @@ pub fn createSocket(sesh: []const u8) !i32 {
7877 /// Derived from the platform's sockaddr_un.path field, minus 1 for the
7978 /// required null terminator.
8079 pub const max_socket_path_len: usize = @typeInfo(
81- @TypeOf(@as(posix.sockaddr.un, undefined).path),
80+ @TypeOf(@as(lib_posix.sockaddr.un, undefined).path),
8281 ).array.len - 1;
8382
8483 pub fn getSocketPath(
......@@ -124,7 +123,7 @@ pub fn maxSessionNameLen(socket_dir: []const u8) ?usize {
124123
125124 test "max_socket_path_len matches platform sockaddr_un" {
126125 const path_field_len = @typeInfo(
127- @TypeOf(@as(posix.sockaddr.un, undefined).path),
126+ @TypeOf(@as(lib_posix.sockaddr.un, undefined).path),
128127 ).array.len;
129128 try std.testing.expectEqual(path_field_len - 1, max_socket_path_len);
130129 try std.testing.expect(max_socket_path_len > 0);
+0, -1
......@@ -1,5 +1,4 @@
11 const std = @import("std");
2-const posix = std.posix;
32 const ghostty_vt = @import("ghostty-vt");
43 const ipc = @import("ipc.zig");
54 const socket = @import("socket.zig");