diff --git a/lib/std/Io.zig b/lib/std/Io.zig index d83c63e7276615651ad896128e6878fcb0e8e792..14c7306ca6673d99bad8f3b8b5eafc150f935f74 100644 --- a/lib/std/Io.zig +++ b/lib/std/Io.zig @@ -430,6 +430,7 @@ pub const Operation = union(enum) { pub const NetRead = struct { socket_handle: net.Socket.Handle, data: [][]u8, + control: []u8 = &.{}, pub const Error = error{ SystemResources, @@ -443,7 +444,7 @@ pub const Operation = union(enum) { ConnectionTimedOut, } || Io.UnexpectedError; - pub const Result = Error!usize; + pub const Result = Error!net.Stream.ReadResult; }; pub const NetWrite = struct { @@ -451,6 +452,7 @@ pub const Operation = union(enum) { header: []const u8 = &.{}, data: []const []const u8, splat: usize = 1, + control: []const u8 = &.{}, pub const Error = error{ /// Another TCP Fast Open is already in progress. diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig index 6c44b6ab73e1d53fab7c6c61a4b6ee391e6790ed..2c7cdbc8e99e37081200900866b94edf7672e272 100644 --- a/lib/std/Io/Threaded.zig +++ b/lib/std/Io/Threaded.zig @@ -2583,16 +2583,16 @@ fn operate(userdata: ?*anyopaque, operation: Io.Operation) Io.Cancelable!Io.Oper }, }, .net_read => |o| return .{ - .net_read = netRead(o.socket_handle, o.data) catch |err| switch (err) { + .net_read = netRead(o.socket_handle, o.data, o.control) catch |err| switch (err) { error.Canceled => |e| return e, else => |e| e, }, }, .net_write => |o| return .{ .net_write = (if (is_windows) - netWriteWindows(o.socket_handle, o.header, o.data, o.splat) + netWriteWindows(o.socket_handle, o.header, o.data, o.splat, o.control) else - netWritePosix(o.socket_handle, o.header, o.data, o.splat)) catch |err| switch (err) { + netWritePosix(o.socket_handle, o.header, o.data, o.splat, o.control)) catch |err| switch (err) { error.Canceled => |e| return e, else => |e| e, }, @@ -3293,7 +3293,7 @@ fn batchDrainSubmittedWindows(t: *Threaded, b: *Io.Batch, concurrency: bool) (Io // TODO integrate with overlapped I/O or equivalent to avoid this error if (concurrency) return error.ConcurrencyUnavailable; batchCompleteBlockingWindows(b, operation_userdata, .{ - .net_read = netRead(o.socket_handle, o.data) catch |err| switch (err) { + .net_read = netRead(o.socket_handle, o.data, o.control) catch |err| switch (err) { error.Canceled => |e| return e, else => |e| e, }, @@ -3303,7 +3303,7 @@ fn batchDrainSubmittedWindows(t: *Threaded, b: *Io.Batch, concurrency: bool) (Io // TODO integrate with overlapped I/O or equivalent to avoid this error if (concurrency) return error.ConcurrencyUnavailable; batchCompleteBlockingWindows(b, operation_userdata, .{ - .net_write = netWriteWindows(o.socket_handle, o.header, o.data, o.splat) catch |err| switch (err) { + .net_write = netWriteWindows(o.socket_handle, o.header, o.data, o.splat, o.control) catch |err| switch (err) { error.Canceled => |e| return e, else => |e| e, }, @@ -12883,14 +12883,14 @@ fn deferAcceptAfd(t: *Threaded, listen_handle: net.Socket.Handle, info: windows. } } -fn netRead(socket_handle: net.Socket.Handle, data: [][]u8) net.Stream.Reader.Error!usize { +fn netRead(socket_handle: net.Socket.Handle, data: [][]u8, control: []u8) net.Stream.Reader.Error!net.Stream.ReadResult { if (!have_networking) return error.NetworkDown; - if (is_windows) return netReadWindows(socket_handle, data); - return netReadPosix(socket_handle, data); + if (is_windows) return .{ .data_len = try netReadWindows(socket_handle, data) }; + return netReadPosix(socket_handle, data, control); } -fn netReadPosix(fd: net.Socket.Handle, data: [][]u8) net.Stream.Reader.Error!usize { +fn netReadPosix(fd: net.Socket.Handle, data: [][]u8, control: []u8) net.Stream.Reader.Error!net.Stream.ReadResult { var iovecs_buffer: [max_iovecs_len]posix.iovec = undefined; var i: usize = 0; for (data) |buf| { @@ -12936,13 +12936,31 @@ fn netReadPosix(fd: net.Socket.Handle, data: [][]u8) net.Stream.Reader.Error!usi } } + var msg: posix.msghdr = .{ + .name = null, + .namelen = 0, + .iov = dest.ptr, + .iovlen = @intCast(dest.len), + .control = control.ptr, + .controllen = @intCast(control.len), + .flags = 0, + }; + + const flags: u32 = + @as(u32, if (@hasDecl(posix.MSG, "CMSG_CLOEXEC")) posix.MSG.CMSG_CLOEXEC else 0); + const syscall: Syscall = try .start(); while (true) { - const rc = posix.system.readv(fd, dest.ptr, @intCast(dest.len)); + const rc = posix.system.recvmsg(fd, &msg, flags); + const rc_control = msg.controllen; switch (posix.errno(rc)) { .SUCCESS => { syscall.finish(); - return @intCast(rc); + return .{ + .data_len = @intCast(rc), + .control_len = @intCast(rc_control), + .control_truncated = (msg.flags & posix.MSG.CTRUNC) != 0, + }; }, .INTR => { try syscall.checkCancel(); @@ -13229,7 +13247,6 @@ fn netReceivePosix( @as(u32, if (flags.oob) posix.MSG.OOB else 0) | @as(u32, if (flags.peek) posix.MSG.PEEK else 0) | @as(u32, if (flags.trunc) posix.MSG.TRUNC else 0) | - posix.MSG.NOSIGNAL | @as(u32, if (nonblocking) posix.MSG.DONTWAIT else 0); var storage: PosixAddress = undefined; @@ -13241,7 +13258,7 @@ fn netReceivePosix( .iovlen = 1, .control = message.control.ptr, .controllen = @intCast(message.control.len), - .flags = undefined, + .flags = 0, }; const syscall = try Syscall.start(); @@ -13362,6 +13379,7 @@ fn netWritePosix( header: []const u8, data: []const []const u8, splat: usize, + control: []const u8, ) net.Stream.Writer.Error!usize { if (!have_networking) return error.NetworkDown; @@ -13371,8 +13389,8 @@ fn netWritePosix( .namelen = 0, .iov = &iovecs, .iovlen = 0, - .control = null, - .controllen = 0, + .control = if (control.len == 0) null else @constCast(control.ptr), + .controllen = @intCast(control.len), .flags = 0, }; addBuf(&iovecs, &msg.iovlen, header); @@ -13454,9 +13472,13 @@ fn netWriteWindows( header: []const u8, data: []const []const u8, splat: usize, + control: []const u8, ) net.Stream.Writer.Error!usize { if (!have_networking) return error.NetworkDown; + // Windows doesn't have the concept of control/ancillary data. + _ = control; + var iovecs: [max_iovecs_len]windows.AFD.WSABUF(.@"const") = undefined; var len: u32 = 0; addAfdBuf(.@"const", &iovecs, &len, header); diff --git a/lib/std/Io/net.zig b/lib/std/Io/net.zig index ebaab6de90cc3ef610e0245fe952a90011d6c9f1..7c5ddbde5e9a711f39d250587c1a4fa95922fd3e 100644 --- a/lib/std/Io/net.zig +++ b/lib/std/Io/net.zig @@ -1269,13 +1269,37 @@ pub const Stream = struct { const max_iovecs_len = 8; + pub const ReadResult = struct { + data_len: usize, + control_len: usize = 0, + /// Whether only some of the control data was received. + /// + /// When control data is truncated, the extra data essentially + /// disappears into the ether. This is indicative of a design problem, + /// such as the control buffer being too small. + control_truncated: bool = false, + }; + /// This is a low-level API that calls the `Io` interface function directly. /// For a higher level API, see `reader`. pub fn read(s: *const Stream, io: Io, data: [][]u8) Reader.Error!usize { - return (try io.operate(.{ .net_read = .{ + const rc, _ = try (try io.operate(.{ .net_read = .{ .socket_handle = s.socket.handle, .data = data, } })).net_read; + return rc; + } + + /// Read with control data. + /// + /// This is a low-level API that calls the `Io` interface function directly. + /// For a higher level API, see `reader`. + pub fn readWithControl(s: *const Stream, io: Io, data: [][]u8, control: []u8) Reader.Error!ReadResult { + return try (try io.operate(.{ .net_read = .{ + .socket_handle = s.socket.handle, + .data = data, + .control = control, + } })).net_read; } pub fn close(s: *const Stream, io: Io) void { @@ -1289,12 +1313,25 @@ pub const Stream = struct { pub const Reader = struct { io: Io, interface: Io.Reader, + control_buffer: []align(cmsg_align) u8, + control_len: usize, + control_truncated: bool, stream: Stream, err: ?Error, pub const Error = Io.Operation.NetRead.Error || Io.Cancelable; pub fn init(stream: Stream, io: Io, buffer: []u8) Reader { + return initWithControl(stream, io, buffer, &.{}); + } + + /// Same as `init`, but also provides a buffer for storing control data. + pub fn initWithControl( + stream: Stream, + io: Io, + buffer: []u8, + control_buffer: []align(cmsg_align) u8, + ) Reader { return .{ .io = io, .interface = .{ @@ -1306,6 +1343,9 @@ pub const Stream = struct { .seek = 0, .end = 0, }, + .control_buffer = control_buffer, + .control_len = 0, + .control_truncated = false, .stream = stream, .err = null, }; @@ -1326,24 +1366,44 @@ pub const Stream = struct { const dest_n, const data_size = try io_r.writableVector(&iovecs_buffer, data); const dest = iovecs_buffer[0..dest_n]; assert(dest[0].len > 0); - const n = r.stream.read(io, dest) catch |err| { + const result = r.stream.readWithControl(io, dest, r.control_buffer[r.control_len..]) catch |err| { r.err = err; return error.ReadFailed; }; - if (n == 0) { + r.control_len += result.control_len; + r.control_truncated = r.control_truncated or result.control_truncated; + if (result.data_len == 0) { return error.EndOfStream; } - if (n > data_size) { - r.interface.end += n - data_size; + if (result.data_len > data_size) { + r.interface.end += result.data_len - data_size; return data_size; } - return n; + return result.data_len; + } + + /// Access raw buffered control data. + pub fn controlSlice(r: *const Reader) []align(cmsg_align) u8 { + return r.control_buffer[0..r.control_len]; + } + + /// Iterate over buffered control messages. + pub fn controlIterator(r: *const Reader) cmsg.Iterator { + return .{ .control = r.controlSlice() }; + } + + /// Clear buffered control data. + pub fn clearControl(r: *Reader) void { + r.control_len = 0; + r.control_truncated = false; } }; pub const Writer = struct { io: Io, interface: Io.Writer, + /// Will be sent on next drain, then set back to an empty slice. + control: []const u8 = &.{}, stream: Stream, err: ?Error = null, write_file_err: ?WriteFileError = null, @@ -1386,6 +1446,7 @@ pub const Stream = struct { .header = buffered, .data = data, .splat = splat, + .control = w.control, } }) catch |err| { w.err = err; return error.WriteFailed; @@ -1394,6 +1455,7 @@ pub const Stream = struct { w.err = err; return error.WriteFailed; }; + w.control = &.{}; return io_w.consume(n); } @@ -1424,11 +1486,68 @@ pub const Stream = struct { return .init(stream, io, buffer); } + /// Same as `reader`, but also provides a buffer for storing control data. + pub fn readerWithControl( + stream: Stream, + io: Io, + buffer: []u8, + control_buffer: []align(cmsg_align) u8, + ) Reader { + return .initWithControl(stream, io, buffer, control_buffer); + } + pub fn writer(stream: Stream, io: Io, buffer: []u8) Writer { return .init(stream, io, buffer); } }; +pub const cmsg_align = if (@TypeOf(std.posix.cmsg_align) == void) 1 else std.posix.cmsg_align; +/// Utility Functions for interacting with POSIX socket control messages. +/// See also https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/sys_socket.h.html +pub const cmsg = struct { + const cmsghdr = std.posix.cmsghdr; + + /// Equivalent to `CMSG_SPACE` in C. + pub fn space(data_len: usize) usize { + return std.mem.alignForward(usize, @sizeOf(cmsghdr), cmsg_align) + + std.mem.alignForward(usize, data_len, cmsg_align); + } + + /// Equivalent to `CMSG_LEN` in C. + pub fn len(data_len: @FieldType(cmsghdr, "len")) @TypeOf(data_len) { + return std.mem.alignForward(@TypeOf(data_len), @sizeOf(cmsghdr), cmsg_align) + data_len; + } + + /// Equivalent to `CMSG_DATA` in C. + pub fn data(header: *align(cmsg_align) cmsghdr) []align(cmsg_align) u8 { + const bytes: [*]u8 = @ptrCast(header); + return @alignCast(bytes[0..header.len][std.mem.alignForward(usize, @sizeOf(cmsghdr), cmsg_align)..]); + } + + /// Equivalent to `CMSG_FIRSTHDR` and `CMSG_NXTHDR` in C. + pub const Iterator = struct { + control: []align(cmsg_align) u8, + + pub const Message = struct { + header: *cmsghdr, + data: []align(cmsg_align) u8, + }; + + pub fn next(it: *Iterator) ?Message { + if (it.control.len < @sizeOf(cmsghdr)) return null; + const header: *align(cmsg_align) cmsghdr = @ptrCast(it.control.ptr); + if (it.control.len < header.len) return null; + const next_header = std.mem.alignForward(usize, header.len, cmsg_align); + if (it.control.len < next_header) { + it.control = &.{}; + } else { + it.control = @alignCast(it.control[next_header..]); + } + return .{ .header = header, .data = data(header) }; + } + }; +}; + pub const Server = struct { socket: Socket, options: AcceptOptions, diff --git a/lib/std/Io/net/test.zig b/lib/std/Io/net/test.zig index 45389715cd749e77fe4ff99b088b92b78968e262..2c5a4a852e201d6480b1e4e790560c787d881602 100644 --- a/lib/std/Io/net/test.zig +++ b/lib/std/Io/net/test.zig @@ -4,6 +4,7 @@ const std = @import("std"); const Io = std.Io; const net = std.Io.net; const mem = std.mem; +const posix = std.posix; const testing = std.testing; const Allocator = std.mem.Allocator; @@ -295,6 +296,99 @@ test "listen on a unix socket, send bytes, receive bytes" { try client_task.await(io); } +test "listen on a unix socket, pass file descriptor" { + if (builtin.os.tag == .windows or builtin.os.tag == .wasi) { + // Windows and WASI don't have the concept of control/ancillary data. + return error.SkipZigTest; + } + + const io = testing.io; + const gpa = testing.allocator; + + const socket_path = try generateFileName(gpa, io, "socket.unix"); + defer gpa.free(socket_path); + const temp_file_path = try generateFileName(gpa, io, "temp_file"); + defer gpa.free(temp_file_path); + + defer Io.Dir.cwd().deleteFile(io, socket_path) catch {}; + defer Io.Dir.cwd().deleteFile(io, temp_file_path) catch {}; + + const socket_addr = try net.UnixAddress.init(socket_path); + + var server = socket_addr.listen(io, .{}) catch |err| switch (err) { + error.AddressFamilyUnsupported => return error.SkipZigTest, + error.NetworkDown => return error.SkipZigTest, + else => |e| return e, + }; + defer server.socket.close(io); + + const S = struct { + fn clientFn(path: []const u8, file_path: []const u8) !void { + const temp_file = try Io.Dir.cwd().createFile(io, file_path, .{}); + defer temp_file.close(io); + + const server_path: net.UnixAddress = try .init(path); + var stream = try server_path.connect(io); + defer stream.close(io); + + var cmsg_buf: [net.cmsg.space(@sizeOf(posix.fd_t))]u8 align(Io.net.cmsg_align) = @splat(0); + + const header: *align(Io.net.cmsg_align) posix.cmsghdr = @ptrCast(&cmsg_buf); + header.* = .{ + .len = net.cmsg.len(@sizeOf(posix.fd_t)), + .level = posix.SOL.SOCKET, + .type = posix.SCM.RIGHTS, + }; + + const fds: []posix.fd_t = @ptrCast(net.cmsg.data(header)); + fds[0] = temp_file.handle; + + var stream_writer = stream.writer(io, &.{}); + stream_writer.control = &cmsg_buf; + try stream_writer.interface.writeAll("Hello world!"); + } + }; + + var client_task = io.concurrent(S.clientFn, .{ socket_path, temp_file_path }) catch |err| switch (err) { + error.ConcurrencyUnavailable => return error.SkipZigTest, + }; + defer client_task.cancel(io) catch {}; + + var stream = try server.accept(io); + defer stream.close(io); + + var control_buf: [32]u8 align(Io.net.cmsg_align) = undefined; + var buf: [16]u8 = undefined; + var stream_reader = stream.readerWithControl(io, &.{}, &control_buf); + + const n = try stream_reader.interface.readSliceShort(&buf); + + try testing.expect(!stream_reader.control_truncated); // The control buffer should be big enough to receive the entire control message + + try testing.expectEqual(12, n); + try testing.expectEqualStrings("Hello world!", buf[0..n]); + + var it = stream_reader.controlIterator(); + const control = it.next() orelse return error.TestUnexpectedResult; + try testing.expectEqual(null, it.next()); + + try testing.expectEqual(net.cmsg.len(@sizeOf(posix.fd_t)), control.header.len); + try testing.expectEqual(posix.SOL.SOCKET, control.header.level); + try testing.expectEqual(posix.SCM.RIGHTS, control.header.type); + + const fds: []posix.fd_t = @ptrCast(control.data); + + const temp_file: Io.File = .{ + .handle = fds[0], + .flags = .{ .nonblocking = false }, + }; + defer temp_file.close(io); + var temp_file_writer = temp_file.writer(io, &.{}); + try temp_file_writer.interface.writeAll("if this is a real file descriptor, this shouldn't fail"); + + try client_task.await(io); +} + fn generateFileName(gpa: Allocator, io: Io, base_name: []const u8) ![]const u8 { const random_bytes_count = 12; const sub_path_len = comptime std.base64.url_safe.Encoder.calcSize(random_bytes_count); diff --git a/lib/std/c.zig b/lib/std/c.zig index 51c6656d4f21dc23717f757faddc76f109049e2a..c03e83fd65ac14590516f3a16572f2d3cc6a41df 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -3373,6 +3373,19 @@ pub const Sigaction = switch (native_os) { restorer: ?*const fn () callconv(.c) void = null, mask: sigset_t, } else common_linux_Sigaction, + .sparc64 => if (builtin.abi == .gnu) extern struct { + pub const handler_fn = *align(1) const fn (SIG) callconv(.c) void; + pub const sigaction_fn = *const fn (SIG, *const siginfo_t, ?*anyopaque) callconv(.c) void; + + handler: extern union { + handler: ?handler_fn, + sigaction: ?sigaction_fn, + }, + mask: sigset_t, + __glibc_reserved0: c_int = 0, + flags: c_uint, + restorer: ?*const fn () callconv(.c) void = null, + } else common_linux_Sigaction, else => common_linux_Sigaction, }, .emscripten => emscripten.Sigaction, @@ -4432,6 +4445,68 @@ const posix_cmsghdr = extern struct { type: c_int, }; +// Alignment used by POSIX specified CMSG_* Macros +// https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/sys_socket.h.html +pub const cmsg_align = switch (native_os) { + .linux => linux.cmsg_align, + // https://github.com/emscripten-core/emscripten/blob/96371ed7888fc78c040179f4d4faa82a6a07a116/system/lib/libc/musl/include/sys/socket.h#L362 + .emscripten => @sizeOf(usize), + // https://github.com/freebsd/freebsd-src/blob/b197d2abcb6895d78bc9df8404e374397aa44748/sys/sys/socket.h#L584 + // https://github.com/freebsd/freebsd-src/blob/d2018cedb414ef17e77b7fc2fa19fd81328da46b/sys/sys/_align.h#L30 + .freebsd => @sizeOf(*anyopaque), + // https://github.com/DragonFlyBSD/DragonFlyBSD/blob/d1f4fb943c73e2b61ddabff6aa48ce7551db72f4/sys/sys/socket.h#L441 + // https://github.com/DragonFlyBSD/DragonFlyBSD/blob/d1f4fb943c73e2b61ddabff6aa48ce7551db72f4/sys/cpu/x86_64/include/alignbytes.h#L35 + .dragonfly => switch (builtin.cpu.arch) { + .x86_64 => @sizeOf(c_long), + else => {}, + }, + // https://github.com/NetBSD/src/blob/ba8e1774fd9c0c26ecca461c07bc95d9ebb69579/sys/sys/socket.h#L544 + .netbsd => switch (builtin.cpu.arch) { + // https://github.com/NetBSD/src/blob/ac016822f750dc9166c84b6d2657479992622192/sys/arch/arm/include/cdefs.h#L63 + .aarch64, .aarch64_be => @sizeOf(i128), + .arm, .armeb => @sizeOf(c_longlong), + // https://github.com/NetBSD/src/blob/ac016822f750dc9166c84b6d2657479992622192/sys/arch/i386/include/cdefs.h#L9 + .x86 => @sizeOf(c_int), + // https://github.com/NetBSD/src/blob/ac016822f750dc9166c84b6d2657479992622192/sys/arch/amd64/include/cdefs.h#L6 + .x86_64 => @sizeOf(c_long), + // https://github.com/NetBSD/src/blob/ac016822f750dc9166c84b6d2657479992622192/sys/arch/mips/include/cdefs.h#L74 + .mips, .mips64, .mipsel, .mips64el => 8, + // https://github.com/NetBSD/src/blob/ac016822f750dc9166c84b6d2657479992622192/sys/arch/sparc/include/cdefs.h#L8 + .sparc64 => 16, + // https://github.com/NetBSD/src/blob/ac016822f750dc9166c84b6d2657479992622192/sys/arch/sparc/include/cdefs.h#L10 + .sparc => 8, + // https://github.com/NetBSD/src/blob/ac016822f750dc9166c84b6d2657479992622192/sys/arch/alpha/include/cdefs.h#L6 + .alpha => 8, + // https://github.com/NetBSD/src/blob/ac016822f750dc9166c84b6d2657479992622192/sys/arch/m68k/include/cdefs.h#L6 + .m68k => @sizeOf(c_int), + // https://github.com/NetBSD/src/blob/ac016822f750dc9166c84b6d2657479992622192/sys/arch/hppa/include/cdefs.h#L12 + .hppa => 8, + // https://github.com/NetBSD/src/blob/ac016822f750dc9166c84b6d2657479992622192/sys/arch/riscv/include/cdefs.h#L5 + .riscv32, .riscv32be, .riscv64, .riscv64be => @as(comptime_int, builtin.target.cMaxIntAlignment()), + // https://github.com/NetBSD/src/blob/ac016822f750dc9166c84b6d2657479992622192/sys/arch/powerpc/include/cdefs.h#L6 + .powerpc, .powerpcle, .powerpc64, .powerpc64le => @sizeOf(f64), + // https://github.com/NetBSD/src/blob/ac016822f750dc9166c84b6d2657479992622192/sys/arch/or1k/include/cdefs.h#L6 + .or1k => @as(comptime_int, builtin.target.cMaxIntAlignment()), + else => {}, + }, + // https://github.com/openbsd/src/blob/4a7ecdb29021f26ee0ef397afdd2931e20e63071/sys/sys/socket.h#L564 + // https://github.com/openbsd/src/blob/4a7ecdb29021f26ee0ef397afdd2931e20e63071/sys/arch/amd64/include/_types.h#L50 + .openbsd => @sizeOf(c_long), + // https://github.com/illumos/illumos-gate/blob/c1e950d976948602134f0e0e5dadf3d48a7ee996/usr/src/uts/common/sys/socket.h#L469 + .illumos => 4, + // https://github.com/haiku/haiku/blob/b54f586058fd6623645512e4631468cede9933b9/headers/posix/sys/socket.h#L152 + // https://github.com/haiku/haiku/blob/6059d64cb7cd145f0f1eb5b89c383f71e4b6bebf/headers/posix/sys/param.h#L28 + .haiku => @sizeOf(c_long), + // https://github.com/apple/darwin-xnu/blob/2ff845c2e033bd0ff64b5b6aa6063a1f8f65aa32/bsd/sys/socket.h#L1077 + .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => 4, + else => {}, +}; +comptime { + if (@TypeOf(cmsg_align) != void and @TypeOf(cmsghdr) != void) { + assert(cmsg_align >= @alignOf(cmsghdr)); + } +} + pub const nfds_t = switch (native_os) { .linux => linux.nfds_t, .emscripten => emscripten.nfds_t, diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index be4e4fd069e736ca170cde09def4e0545f9e8e21..38e4abd197ba6c17245e7fe7038d7db0528ca395 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -11244,6 +11244,9 @@ pub const cmsghdr = extern struct { type: i32, }; +// https://github.com/torvalds/linux/blob/72d3fcf802c45d00b300f25b848a93c3a2bd7c7e/include/linux/socket.h#L133 +pub const cmsg_align = @sizeOf(c_long); + pub const riscv_hwprobe = extern struct { key: i64, value: u64, diff --git a/lib/std/posix.zig b/lib/std/posix.zig index 712b093e71f458810d4a8e11e451c7a44f9905d1..d845a8607dbb89550018673c8b20ec2db7aebdce 100644 --- a/lib/std/posix.zig +++ b/lib/std/posix.zig @@ -150,6 +150,8 @@ pub const blkcnt_t = system.blkcnt_t; pub const blksize_t = system.blksize_t; pub const clock_t = system.clock_t; pub const clockid_t = system.clockid_t; +pub const cmsghdr = system.cmsghdr; +pub const cmsg_align = system.cmsg_align; pub const timerfd_clockid_t = system.timerfd_clockid_t; pub const cpu_set_t = system.cpu_set_t; pub const dev_t = system.dev_t;