summaryrefslogtreecommitdiff
path: root/9proc
diff options
context:
space:
mode:
Diffstat (limited to '9proc')
-rw-r--r--9proc/src/linux/probe.zig7
-rwxr-xr-x9proc/test/adv_linux_probe.py34
2 files changed, 40 insertions, 1 deletions
diff --git a/9proc/src/linux/probe.zig b/9proc/src/linux/probe.zig
index c2f1026..00c256e 100644
--- a/9proc/src/linux/probe.zig
+++ b/9proc/src/linux/probe.zig
@@ -542,7 +542,12 @@ pub fn Probe(comptime Srv: type) type {
const st = unixStat(@ptrCast(&sa.path)) catch return error.Occupied;
if (st) |s| {
if (s.mode & linux.S.IFMT != linux.S.IFSOCK) return error.Occupied;
- if (cloud9.post.probe(@ptrCast(&sa.path)) != .stale) return error.AlreadyListening;
+ // The probe wants the path, not the whole `sun_path`
+ // array: a 108-byte slice is past the address budget, and
+ // `probe` owns that uncertainty as `.live` — which would
+ // make every stale socket look like a live server.
+ const probe_path: [:0]const u8 = sa.path[0..path.len :0];
+ if (cloud9.post.probe(probe_path) != .stale) return error.AlreadyListening;
_ = linux.unlink(@ptrCast(&sa.path));
}
try p.check(linux.bind(lfd, @ptrCast(&sa), @sizeOf(linux.sockaddr.un)));
diff --git a/9proc/test/adv_linux_probe.py b/9proc/test/adv_linux_probe.py
index 186f008..c921ebb 100755
--- a/9proc/test/adv_linux_probe.py
+++ b/9proc/test/adv_linux_probe.py
@@ -289,6 +289,40 @@ def attack_signals(server):
ok("SIGTERM unlinks the unix socket (clean stop path)", not os.path.exists(path))
s.stop()
+ # A server killed outright leaves its socket behind. The next server
+ # of the same name must recognise the corpse and take the name over:
+ # the listener probes the path, and a probe that cannot tell answers
+ # "live", so a caller that hands it the whole 108-byte sun_path array
+ # instead of the path turns every stale socket into AlreadyListening.
+ s = Srv(server)
+ client(s.path, timeout=5)
+ stale = s.path
+ s.proc.send_signal(signal.SIGKILL)
+ s.proc.wait(timeout=5)
+ ok("SIGKILL leaves the socket behind", os.path.exists(stale))
+ taker = subprocess.Popen([server, "--unix", stale], stderr=subprocess.PIPE)
+ try:
+ # The path exists throughout (the corpse, then the new socket), so
+ # the connect itself is the readiness signal.
+ err, c = None, None
+ for _ in range(250):
+ try:
+ c = client(stale, timeout=10)
+ break
+ except OSError as e:
+ err = e
+ time.sleep(0.02)
+ ok("a fresh server takes over a stale socket path",
+ c is not None and rd(c, [b"build", b"zig_version"])[0] == Rread,
+ err or taker.poll())
+ except Exception as e:
+ ok("a fresh server takes over a stale socket path", False, e)
+ finally:
+ taker.kill()
+ taker.wait(timeout=5)
+ if os.path.exists(stale):
+ os.unlink(stale)
+
def attack_probe(ns, server):
print("# probe loop and admission")