From 7e2a3b04fa609a34359896ef9df8dbcf042da219 Mon Sep 17 00:00:00 2001 From: Emil Lerch Date: Thu, 26 Oct 2023 15:40:23 -0700 Subject: [PATCH] straighten out socket file mess, part 1 This was the hard part. Under abnormal termination (kill -9, seg faults, whatever) socket files do not get cleaned up properly. As long as this is the IPC mechanism, there is very little that can be done about this. So, with this change we are leaving them to linger. Fixing this we can do one of two things. First, we can look for EADDRINUSE and just delete the file and retry. However, that prevents someone from running two binaries at the same time (maybe to run two servers on different ports). The second option is to make the files unique, so this is the path chosen. Here we append a system timestamp to the file name. Part two of this eventually will need to be locating the TEMP directory and placing the files there. --- src/Watch.zig | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/Watch.zig b/src/Watch.zig index aab7ff4..8bf19e3 100644 --- a/src/Watch.zig +++ b/src/Watch.zig @@ -20,12 +20,14 @@ dir_nfds_t: usize = 0, dir_wds: [MAX_FDS]Wd = [_]Wd{.{}} ** MAX_FDS, control_socket: ?std.os.socket_t = null, watch_started: bool = false, +sock_name: [:0]const u8, pub fn init(file_changed: *const fn (usize) void) Self { if (builtin.os.tag != .linux) @compileError("Unsupported OS"); return .{ .fileChanged = file_changed, + .sock_name = sockName(), }; } @@ -50,12 +52,15 @@ pub fn deinit(self: *Self) void { std.os.close(fd); } const cwd = std.fs.cwd(); - cwd.deleteFileZ(SOCK_NAME) catch |e| - log.err("error removing socket file " ++ SOCK_NAME ++ ": {any}", .{e}); + cwd.deleteFileZ(self.sock_name) catch |e| + log.err("error removing socket file {s}: {any}", .{ self.sock_name, e }); } const SOCK_NAME = "S.watch-control"; - +var buf = [_]u8{0} ** (SOCK_NAME.len + "-9223372036854775807 ".len); +fn sockName() [:0]const u8 { + return std.fmt.bufPrintZ(buf[0..], "{s}-{d}", .{ SOCK_NAME, std.time.timestamp() }) catch unreachable; // buffer designed for Max(i64) with sock name and a trailing \0 +} /// starts the file watch. This function will not return, so it is best /// to put this function in its own thread: /// @@ -66,7 +71,7 @@ const SOCK_NAME = "S.watch-control"; /// is intended later pub fn startWatch(self: *Self) void { if (self.control_socket == null) - self.addControlSocket(SOCK_NAME) catch @panic("could not add control socket"); + self.addControlSocket(self.sock_name) catch @panic("could not add control socket"); std.debug.assert(self.control_socket != null); while (true) { @@ -103,7 +108,7 @@ pub fn startWatch(self: *Self) void { // self.control_socket_accepted_fd = self.control_socket_accepted_fd orelse acceptSocket(self.control_socket.?); // const fd = self.control_socket_accepted_fd.?; // let's save some typing - const fd = acceptSocket(self.control_socket.?); + const fd = acceptSocket(self.sock_name, self.control_socket.?); defer std.os.close(fd); var readcount = std.os.recv(fd, &control_buf, 0) catch unreachable; @@ -157,8 +162,8 @@ pub fn startWatch(self: *Self) void { } } -fn acceptSocket(socket: std.os.socket_t) std.os.socket_t { - var sockaddr = std.net.Address.initUnix(SOCK_NAME) catch @panic("could not get sockaddr"); +fn acceptSocket(name: [:0]const u8, socket: std.os.socket_t) std.os.socket_t { + var sockaddr = std.net.Address.initUnix(name) catch @panic("could not get sockaddr"); var sockaddr_len: std.os.socklen_t = sockaddr.getOsSockLen(); log.debug("tid={d} accepting on socket fd {d}", .{ std.Thread.getCurrentId(), socket }); return std.os.accept( @@ -322,7 +327,7 @@ fn sendControl(self: Self, control: u8) !void { // log.debug("request to send control 0x{x}", .{control}); if (self.control_socket == null) return; // nothing to do // log.debug("tid={d} opening stream", .{std.Thread.getCurrentId()}); - var stream = try std.net.connectUnixSocket(SOCK_NAME); + var stream = try std.net.connectUnixSocket(self.sock_name); defer stream.close(); log.debug("tid={d} sending control 0x{x} on socket fd={d}", .{ std.Thread.getCurrentId(), control, stream.handle }); try stream.writer().writeByte(control); @@ -373,7 +378,7 @@ fn addControlSocket(self: *Self, path: [:0]const u8) !void { const sockaddr = try std.net.Address.initUnix(path); - // TODO: If this bind fails with EADDRINUSE we can probably delete the existing file + log.debug("binding to path: {s}", .{path}); try std.os.bind(sock, &sockaddr.any, sockaddr.getOsSockLen()); try std.os.listen(sock, 10); self.control_socket = sock;