From 14509b8d284dcf17e726741b24d2ac3a626869c2 Mon Sep 17 00:00:00 2001 From: Philippe Pittoli Date: Sun, 1 Jan 2023 20:50:08 +0100 Subject: [PATCH] Sending a file descriptor through a UNIX socket. --- zig-impl/misc/cmsghdr.zig | 63 ++++++++++++++++++++++++++++ zig-impl/misc/snd-fd.zig | 86 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 149 insertions(+) create mode 100644 zig-impl/misc/cmsghdr.zig create mode 100644 zig-impl/misc/snd-fd.zig diff --git a/zig-impl/misc/cmsghdr.zig b/zig-impl/misc/cmsghdr.zig new file mode 100644 index 0000000..5271dd6 --- /dev/null +++ b/zig-impl/misc/cmsghdr.zig @@ -0,0 +1,63 @@ +const std = @import("std"); + +/// TODO: move this to std + +/// This definition enables the use of Zig types with a cmsghdr structure. +/// The oddity of this layout is that the data must be aligned to @sizeOf(usize) +/// rather than its natural alignment. +pub fn Cmsghdr(comptime T: type) type { + const Header = extern struct { + len: usize, + level: c_int, + @"type": c_int, + }; + + const data_align = @sizeOf(usize); + const data_offset = std.mem.alignForward(@sizeOf(Header), data_align); + + return extern struct { + const Self = @This(); + + bytes: [data_offset + @sizeOf(T)]u8 align(@alignOf(Header)), + + pub fn init(args: struct { + level: c_int, + @"type": c_int, + data: T, + }) Self { + var self: Self = undefined; + self.headerPtr().* = .{ + .len = data_offset + @sizeOf(T), + .level = args.level, + .@"type" = args.@"type", + }; + self.dataPtr().* = args.data; + return self; + } + + // TODO: include this version if we submit a PR to add this to std + pub fn initNoData(args: struct { + level: c_int, + @"type": c_int, + }) Self { + var self: Self = undefined; + self.headerPtr().* = .{ + .len = data_offset + @sizeOf(T), + .level = args.level, + .@"type" = args.@"type", + }; + return self; + } + + pub fn headerPtr(self: *Self) *Header { + return @ptrCast(*Header, self); + } + pub fn dataPtr(self: *Self) *align(data_align) T { + return @ptrCast(*T, self.bytes[data_offset..]); + } + }; +} + +test { + std.testing.refAllDecls(Cmsghdr([3]std.os.fd_t)); +} diff --git a/zig-impl/misc/snd-fd.zig b/zig-impl/misc/snd-fd.zig new file mode 100644 index 0000000..55e7e99 --- /dev/null +++ b/zig-impl/misc/snd-fd.zig @@ -0,0 +1,86 @@ +const std = @import("std"); +const testing = std.testing; +const net = std.net; +const fmt = std.fmt; +const mem = std.mem; +const os = std.os; +const print = std.debug.print; + +const Cmsghdr = @import("./cmsghdr.zig").Cmsghdr; + +fn disconnect(stream: net.Stream) void { stream.close(); } + +fn connect(path: []const u8) !net.Stream { + return try net.connectUnixSocket(path); +} + +const SCM_RIGHTS: c_int = 1; + +fn send_msg(sock: os.socket_t, msg: []const u8, fd: os.fd_t) void { + var iov = [_]os.iovec_const{ + .{ + .iov_base = msg.ptr, + .iov_len = msg.len, + }, + }; + + var cmsg = Cmsghdr(os.fd_t).init(.{ + .level = os.SOL.SOCKET, + .@"type" = SCM_RIGHTS, + .data = fd, + }); + + const len = os.sendmsg(sock, .{ + .name = undefined, + .namelen = 0, + .iov = &iov, + .iovlen = iov.len, + .control = &cmsg, + .controllen = @sizeOf(@TypeOf(cmsg)), + .flags = 0, + }, 0) catch |err| { + print("error sendmsg failed with {s}", .{@errorName(err)}); + return; + }; + + if (len != msg.len) { + // we don't have much choice but to exit here + // log.err(@src(), "expected sendmsg to return {} but got {}", .{msg.len, len}); + print("expected sendmsg to return {} but got {}", .{msg.len, len}); + os.exit(0xff); + } +} + +// const buffer_size = 10000; +// var buffer: [buffer_size]u8 = undefined; +// var fba = std.heap.fixedBufferAllocator(&buffer); + +fn add_line_in_file() !void { + var cwd = std.fs.cwd(); + var f = try cwd.createFile("some-file.log", .{.read = true}); + defer f.close(); + + var writer = f.writer(); + try writer.print("hello\n", .{}); +} +pub fn main() !u8 { + var path = "/tmp/.TEST_USOCK"; + + print("Connection to {s}...\n", .{path}); + var stream = try connect(path); + + print("Connected! Opening a file...\n", .{}); + var file = try std.fs.cwd().createFile("some-file.log", .{.read = true}); + defer file.close(); + + print("File opened! Writing some data into it...\n", .{}); + var writer = file.writer(); + try writer.print("hello this is the first process\n", .{}); + + print("Data written! Sending its fd...\n", .{}); + send_msg(stream.handle, "hello", file.handle); + print("Sent fd! Disconnection...\n", .{}); + disconnect(stream); + print("Disconnected!\n", .{}); + return 0; +}