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 }