summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/9p_io.zig29
1 files changed, 28 insertions, 1 deletions
diff --git a/src/9p_io.zig b/src/9p_io.zig
index 0eaa2d7e..e475416e 100644
--- a/src/9p_io.zig
+++ b/src/9p_io.zig
@@ -874,7 +874,29 @@ pub fn listen(io: std.Io, gpa: std.mem.Allocator, core: *pardes.Pardes, named: [
.greet_timeout_ms = Listener.greet_deadline_ms,
});
const p = socketPath(&l.path_buf, dir, entry_name).?;
- _ = l.runner.listen(.{ .unix = p }, max_conns) catch |err| retry: {
+ // Listened on under a name of its own, then renamed into place: a client
+ // that waits for the socket to appear finds it listening already. Bound
+ // at its own name, it was there a moment before listen(2), and a connect
+ // then was refused. (No room for the longer name: bound in place.)
+ var tmp_buf: [sun_path_len]u8 = undefined;
+ if (std.fmt.bufPrintSentinel(&tmp_buf, "{s}.{d}", .{ p, libc.getpid() }, 0) catch null) |tmp| {
+ const existing = @import("fs.zig").statPath(io, p, .{ .follow_symlinks = false }) catch null;
+ if (existing != null and (existing.?.kind != .unix_domain_socket or alive(p))) {
+ log.warn("something is already listening on {s}", .{p});
+ l.deinit(gpa);
+ return null;
+ }
+ _ = libc.unlink(tmp);
+ _ = l.runner.listen(.{ .unix = tmp }, max_conns) catch {
+ l.deinit(gpa);
+ return null;
+ };
+ if (libc.chmod(tmp, 0o600) != 0 or libc.rename(tmp, p) != 0) {
+ _ = libc.unlink(tmp);
+ l.deinit(gpa);
+ return null;
+ }
+ } else _ = l.runner.listen(.{ .unix = p }, max_conns) catch |err| retry: {
const existing = @import("fs.zig").statPath(io, p, .{ .follow_symlinks = false }) catch null;
if (err != error.AddressInUse or existing == null or existing.?.kind != .unix_domain_socket or alive(p)) {
log.warn("something is already listening on {s}", .{p});
@@ -1338,6 +1360,11 @@ test "a listening editor posts itself into the 9P registry and unposts on stop"
// The socket stays exactly where pardes has always bound it:
// adopting the registry moves nothing, it only advertises.
try testing.expect(statNoFollow(sock) != null);
+ // It appeared already listening, renamed into place from a name of
+ // its own, which is gone.
+ try testing.expect(alive(sock));
+ var tmp_buf: [sun_path_len:0]u8 = undefined;
+ try testing.expect(statNoFollow(try std.fmt.bufPrintSentinel(&tmp_buf, "{s}.{d}", .{ sock, libc.getpid() }, 0)) == null);
// And the registry holds a symlink to it one directory down, so
// several editors group under /mnt/9p/pardes/ instead of
// crowding the registry root — the layout zmx posts its