const std = @import("std"); const os = std.os; const fmt = std.fmt; const log = std.log; const mem = std.mem; const process = std.process; const once = std.once; const DB = @import("DB.zig"); const File = @import("File.zig"); const ErrCtx = @import("ErrCtx.zig"); const CGroup = @import("Group.zig").CGroup; const PackedGroup = @import("PackedGroup.zig"); const CUser = @import("User.zig").CUser; const PackedUser = @import("PackedUser.zig"); const c = @cImport({ @cInclude("nss.h"); }); const ENV_DB = "TURBONSS_DB"; const ENV_VERBOSE = "TURBONSS_VERBOSE"; const ENV_OMIT_MEMBERS = "TURBONSS_OMIT_MEMBERS"; export var turbonss_db_path: [:0]const u8 = "/etc/turbonss/db.turbo"; pub var log_level: std.log.Level = .err; // State is a type of the global variable holding the process state: // the DB handle and all the iterators. const State = struct { file: ?File, getpwent_iterator: ?PackedUser.Iterator = null, getgrent_iterator: ?PackedGroup.Iterator = null, omit_members: bool, }; // global_state is initialized on first call to an nss function var global_state: State = undefined; var global_init = once(init); // assigns State from environment variables et al fn init() void { if (os.getenvZ(ENV_VERBOSE)) |env| log_level = if (mem.eql(u8, mem.sliceTo(env, 0), "1")) .debug else .err; const fname = os.getenvZ(ENV_DB) orelse turbonss_db_path[0..]; log.debug("opening {s}", .{fname}); const env_omit_members = os.getenvZ(ENV_OMIT_MEMBERS) orelse "auto"; const omit_members = shouldOmitMembers(env_omit_members, os.argv); log.debug("omitting members from getgr* calls: {any}\n", .{omit_members}); const file = File.open(fname) catch |err| { log.warn("open '{s}': {s}", .{ fname, @errorName(err) }); return; }; log.debug("turbonss database opened", .{}); global_state = State{ .file = file, .omit_members = omit_members, }; } fn shouldOmitMembers(envvar: []const u8, argv: [][*:0]u8) bool { if (mem.eql(u8, envvar, "1")) return true; if (mem.eql(u8, envvar, "0")) return false; if (argv.len == 0) return false; return mem.eql(u8, mem.sliceTo(argv[0], 0), "id"); } export fn _nss_turbo_getpwuid_r( uid: c_uint, pwd: *CUser, buf: [*]u8, buflen: usize, errnop: *c_int, ) c.enum_nss_status { const db = get_db(errnop) orelse return c.NSS_STATUS_UNAVAIL; if (db.getpwuid(uid, buf[0..buflen])) |maybe_cuser| { if (maybe_cuser) |cuser| { pwd.* = cuser; return c.NSS_STATUS_SUCCESS; } else { errnop.* = @enumToInt(os.E.NOENT); return c.NSS_STATUS_NOTFOUND; } } else |err| switch (err) { error.BufferTooSmall => { errnop.* = @enumToInt(os.E.RANGE); return c.NSS_STATUS_TRYAGAIN; }, } } fn get_db(errnop: *c_int) ?DB { global_init.call(); if (global_state.file) |file| { return file.db; } else { errnop.* = @enumToInt(os.E.AGAIN); return null; } } const testing = std.testing; test "nss_turbo_getpwuid_r" { var errc = ErrCtx{}; var tf = try File.TestDB.init(testing.allocator, &errc); defer tf.deinit(); }