//! Generators for `Config.runtime`: /runtime/{pid,ppid,uptime,argv,cwd,env,clients}. //! The core passes every generator `Shared.ctx`; `Fns(Ctx, field)` casts it //! to `*Ctx` and reads the `Info` stored in `@field(ctx, field)`. const std = @import("std"); const linux = std.os.linux; const Writer = std.Io.Writer; /// What the generators report. Texts are borrowed for the server's lifetime. pub const Info = struct { /// argv, one argument per line. argv: []const u8 = "", /// Environment, one KEY=VALUE per line. env: []const u8 = "", cwd: []const u8 = "", /// Monotonic seconds at startup; /runtime/uptime is the difference. start_mono: i64 = 0, /// Live client count, published by the probe. clients: ?*const std.atomic.Value(u32) = null, pub fn now() Info { return .{ .start_mono = monotonicSecs() }; } }; pub fn monotonicSecs() i64 { var ts: linux.timespec = undefined; _ = linux.clock_gettime(.MONOTONIC, &ts); return ts.sec; } /// Seconds since the epoch, clamped to u32 (for atime/mtime and `fn/now`). pub fn realtimeSecs() u32 { var ts: linux.timespec = undefined; _ = linux.clock_gettime(.REALTIME, &ts); return @intCast(std.math.clamp(ts.sec, 0, std.math.maxInt(u32))); } /// The `Config.runtime` type: `Ctx` is the type behind `Shared.ctx`, `field` /// the name of its `Info` field. pub fn Fns(comptime Ctx: type, comptime field: []const u8) type { return struct { fn info(ctx: *anyopaque) *const Info { const c: *Ctx = @ptrCast(@alignCast(ctx)); return &@field(c, field); } pub fn pid(_: *anyopaque, w: *Writer) anyerror!void { try w.print("{d}", .{linux.getpid()}); } pub fn ppid(_: *anyopaque, w: *Writer) anyerror!void { try w.print("{d}", .{linux.getppid()}); } pub fn uptime(ctx: *anyopaque, w: *Writer) anyerror!void { try w.print("{d}", .{monotonicSecs() - info(ctx).start_mono}); } pub fn argv(ctx: *anyopaque, w: *Writer) anyerror!void { try w.writeAll(info(ctx).argv); } pub fn cwd(ctx: *anyopaque, w: *Writer) anyerror!void { try w.writeAll(info(ctx).cwd); } pub fn env(ctx: *anyopaque, w: *Writer) anyerror!void { try w.writeAll(info(ctx).env); } pub fn clients(ctx: *anyopaque, w: *Writer) anyerror!void { const n: u32 = if (info(ctx).clients) |c| c.load(.acquire) else 0; try w.print("{d}", .{n}); } }; } test "runtime generators read Info through the context" { const Ctx = struct { x: u32, info: Info }; var count: std.atomic.Value(u32) = .init(3); var ctx: Ctx = .{ .x = 0, .info = .{ .argv = "a\nb\n", .cwd = "/tmp", .env = "K=V\n", .start_mono = monotonicSecs(), .clients = &count } }; const F = Fns(Ctx, "info"); var buf: [64]u8 = undefined; var w: Writer = .fixed(&buf); try F.clients(&ctx, &w); try std.testing.expectEqualStrings("3", w.buffered()); w = .fixed(&buf); try F.argv(&ctx, &w); try std.testing.expectEqualStrings("a\nb\n", w.buffered()); w = .fixed(&buf); try F.uptime(&ctx, &w); try std.testing.expect(w.buffered().len >= 1); w = .fixed(&buf); try F.pid(&ctx, &w); try std.testing.expectEqual(linux.getpid(), try std.fmt.parseInt(i32, w.buffered(), 10)); try std.testing.expectEqual(@as(usize, 7), @typeInfo(F).@"struct".decls.len); }