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.
This commit is contained in:
parent
ec0f736cad
commit
7e2a3b04fa
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user