lib/sys/src/process/inspect/census.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 //! Process-group membership census over the host process table.
2
3 const std = @import("std");
4 const builtin = @import("builtin");
5 const types = @import("types.zig");
6
7 const backend = switch (builtin.os.tag) {
8 .linux => @import("linux.zig"),
9 .macos => @import("darwin.zig"),
10 else => @import("unsupported.zig"),
11 };
12
13 /// Reports whether any member of `group_id`, its leader included, still runs.
14 /// A member that exited counts as gone while its parent or an adopting
15 /// subreaper still owns the zombie entry, because an exited process holds no
16 /// descriptor. Members that vanish during the scan count as gone. A process
17 /// whose group or state cannot be read returns that error, so membership that
18 /// cannot be established never reads as drained. The scan visits at most
19 /// `storage.len` processes and refuses a larger table with `BufferTooSmall`.
20 /// On Linux a procfs that shows another pid namespace refuses with
21 /// `QueryFailed`. Callers treat every error as possibly running.
22 pub fn groupRunning(
23 group_id: types.ProcessId,
24 storage: []types.ProcessId,
25 ) types.Error!bool {
26 if (group_id <= 1) return error.ProcessNotFound;
27 if (comptime builtin.os.tag == .linux) {
28 if (!try procfsSharesPidView()) return error.QueryFailed;
29 }
30 const processes = try backend.list(storage);
31 for (processes) |pid| {
32 const member_group = backend.group(pid) catch |err| switch (err) {
33 error.ProcessNotFound => continue,
34 else => return err,
35 };
36 if (member_group != group_id) continue;
37 const member = backend.info(pid) catch |err| switch (err) {
38 error.ProcessNotFound => continue,
39 else => return err,
40 };
41 if (member.alive) return true;
42 }
43 return false;
44 }
45
46 fn procfsSharesPidView() types.Error!bool {
47 var storage: [16]u8 = undefined;
48 const count = std.Io.Dir.cwd().readLink(std.Options.debug_io, "/proc/self", &storage) catch
49 return error.QueryFailed;
50 if (count >= storage.len) return error.InvalidData;
51 const viewed = std.fmt.parseUnsigned(types.ProcessId, storage[0..count], 10) catch
52 return error.InvalidData;
53 return viewed == std.os.linux.getpid();
54 }