Compare commits

..

2 Commits

Author SHA1 Message Date
685d8b574b
update watcher based on socket changes
Some checks failed
Build / build (push) Successful in 3m23s
Build / sign (push) Failing after 15s
Build / deploy (push) Failing after 1m33s
2023-10-28 08:17:17 -07:00
7e2a3b04fa
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.
2023-10-26 15:41:07 -07:00
2 changed files with 16 additions and 10 deletions

View File

@ -20,12 +20,14 @@ dir_nfds_t: usize = 0,
dir_wds: [MAX_FDS]Wd = [_]Wd{.{}} ** MAX_FDS, dir_wds: [MAX_FDS]Wd = [_]Wd{.{}} ** MAX_FDS,
control_socket: ?std.os.socket_t = null, control_socket: ?std.os.socket_t = null,
watch_started: bool = false, watch_started: bool = false,
sock_name: [:0]const u8,
pub fn init(file_changed: *const fn (usize) void) Self { pub fn init(file_changed: *const fn (usize) void) Self {
if (builtin.os.tag != .linux) if (builtin.os.tag != .linux)
@compileError("Unsupported OS"); @compileError("Unsupported OS");
return .{ return .{
.fileChanged = file_changed, .fileChanged = file_changed,
.sock_name = sockName(),
}; };
} }
@ -50,12 +52,15 @@ pub fn deinit(self: *Self) void {
std.os.close(fd); std.os.close(fd);
} }
const cwd = std.fs.cwd(); const cwd = std.fs.cwd();
cwd.deleteFileZ(SOCK_NAME) catch |e| cwd.deleteFileZ(self.sock_name) catch |e|
log.err("error removing socket file " ++ SOCK_NAME ++ ": {any}", .{e}); log.err("error removing socket file {s}: {any}", .{ self.sock_name, e });
} }
const SOCK_NAME = "S.watch-control"; 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 /// starts the file watch. This function will not return, so it is best
/// to put this function in its own thread: /// to put this function in its own thread:
/// ///
@ -66,7 +71,7 @@ const SOCK_NAME = "S.watch-control";
/// is intended later /// is intended later
pub fn startWatch(self: *Self) void { pub fn startWatch(self: *Self) void {
if (self.control_socket == null) 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); std.debug.assert(self.control_socket != null);
while (true) { 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.?); // 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 = 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); defer std.os.close(fd);
var readcount = std.os.recv(fd, &control_buf, 0) catch unreachable; 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 { fn acceptSocket(name: [:0]const u8, socket: std.os.socket_t) std.os.socket_t {
var sockaddr = std.net.Address.initUnix(SOCK_NAME) catch @panic("could not get sockaddr"); var sockaddr = std.net.Address.initUnix(name) catch @panic("could not get sockaddr");
var sockaddr_len: std.os.socklen_t = sockaddr.getOsSockLen(); var sockaddr_len: std.os.socklen_t = sockaddr.getOsSockLen();
log.debug("tid={d} accepting on socket fd {d}", .{ std.Thread.getCurrentId(), socket }); log.debug("tid={d} accepting on socket fd {d}", .{ std.Thread.getCurrentId(), socket });
return std.os.accept( return std.os.accept(
@ -322,7 +327,7 @@ fn sendControl(self: Self, control: u8) !void {
// log.debug("request to send control 0x{x}", .{control}); // log.debug("request to send control 0x{x}", .{control});
if (self.control_socket == null) return; // nothing to do if (self.control_socket == null) return; // nothing to do
// log.debug("tid={d} opening stream", .{std.Thread.getCurrentId()}); // 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(); defer stream.close();
log.debug("tid={d} sending control 0x{x} on socket fd={d}", .{ std.Thread.getCurrentId(), control, stream.handle }); log.debug("tid={d} sending control 0x{x} on socket fd={d}", .{ std.Thread.getCurrentId(), control, stream.handle });
try stream.writer().writeByte(control); 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); 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.bind(sock, &sockaddr.any, sockaddr.getOsSockLen());
try std.os.listen(sock, 10); try std.os.listen(sock, 10);
self.control_socket = sock; self.control_socket = sock;

View File

@ -25,7 +25,7 @@ const requestDeinitFn = *const fn () void;
const timeout = 250; const timeout = 250;
var watcher = Watch.init(executorChanged); var watcher: Watch = undefined;
var watcher_thread: ?std.Thread = null; var watcher_thread: ?std.Thread = null;
// Timer used by processRequest to provide ttfb/ttlb data in output // Timer used by processRequest to provide ttfb/ttlb data in output
@ -405,6 +405,7 @@ fn childMain(allocator: std.mem.Allocator) !void {
defer allocator.free(executors); defer allocator.free(executors);
defer parsed_config.deinit(); defer parsed_config.deinit();
watcher = Watch.init(executorChanged);
watcher_thread = try std.Thread.spawn(.{}, Watch.startWatch, .{&watcher}); watcher_thread = try std.Thread.spawn(.{}, Watch.startWatch, .{&watcher});
var server = std.http.Server.init(allocator, .{ .reuse_address = true }); var server = std.http.Server.init(allocator, .{ .reuse_address = true });