diff --git a/src/analyze.zig b/src/analyze.zig index b4aa178..47b5e52 100644 --- a/src/analyze.zig +++ b/src/analyze.zig @@ -178,6 +178,6 @@ test "trivial error: db file" { try testing.expectEqual(@as(u8, 1), exit_code); try testing.expectEqualStrings( stderr.items, - "ERROR: failed to open './db.turbo': FileNotFound\n", + "ERROR: failed to open 'db.turbo': FileNotFound\n", ); } diff --git a/src/libnss.zig b/src/libnss.zig index 6975527..ee2e048 100644 --- a/src/libnss.zig +++ b/src/libnss.zig @@ -1,8 +1,10 @@ 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"); @@ -16,92 +18,61 @@ const c = @cImport({ @cInclude("nss.h"); }); -const TURBONSS_DB = "DB"; -const TURBONSS_VERBOSE = "TURBONSS_VERBOSE"; -const TURBONSS_OMIT_MEMBERS = "TURBONSS_OMIT_MEMBERS"; - -extern fn putenv(name: [*:0]const u8, value: [*:0]const u8, overwrite: c_int) c_int; -extern fn getenv(name: [*:0]const u8) ?[*:0]const u8; +const ENV_DB = "TURBONSS_DB"; +const ENV_VERBOSE = "TURBONSS__VERBOSE"; +const ENV_OMIT_MEMBERS = "TURBONSS_OMIT_MEMBERS"; export const turbonss_default_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, - getgrent_iterator: ?PackedGroup.Iterator, + getpwent_iterator: ?PackedUser.Iterator = null, + getgrent_iterator: ?PackedGroup.Iterator = null, omit_members: bool, - verbose: bool = false, - - fn debug(self: *const State, comptime format: []const u8, args: anytype) void { - self.log(.debug, format, args); - } - fn info(self: *const State, comptime format: []const u8, args: anytype) void { - self.log(.info, format, args); - } - fn err(self: *const State, comptime format: []const u8, args: anytype) void { - self.log(.err, format, args); - } - fn log( - self: *const State, - comptime level: std.log.Level, - comptime format: []const u8, - args: anytype, - ) void { - if (!self.verbose) return; - const stderr = std.io.getStdErr().writer(); - std.debug.getStderrMutex().lock(); - defer std.debug.getStderrMutex().unlock(); - stderr.print("[" ++ level.asText() ++ "] " ++ format ++ "\n", args) catch return; - } }; -// state is initialized on library startup. -var state: State = undefined; +// global_state is initialized on first call to an nss function +var global_state: State = undefined; +var global_start = once(init); -// constructor -export fn _turbo_init() void { - //if (os.getenvZ(TURBONSS_VERBOSE)) |env| - // state.verbose = mem.eql(u8, env, "1"); +// 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; - if (getenv(TURBONSS_VERBOSE)) |env| { - const envZ = mem.sliceTo(env, 0); - state.verbose = mem.eql(u8, envZ, "1"); - } + const fname = os.getenvZ(ENV_DB) orelse turbonss_default_path[0..]; + log.debug("opening {s}", .{fname}); - //std.debug.print("TURBONSS_VERBOSE: {s}\n", .{os.getenv(TURBONSS_VERBOSE)}); - std.debug.print("TURBONSS_VERBOSE: {s}\n", .{getenv(TURBONSS_VERBOSE)}); + 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 fname = os.getenvZ(TURBONSS_DB) orelse turbonss_default_path[0..]; - state.debug("opening {s}", .{fname}); - state.file = File.open(fname) catch |err| { - state.err("open {s}: {s}", .{ fname, @errorName(err) }); + const file = File.open(fname) catch |err| { + log.warn("open '{s}': {s}", .{ fname, @errorName(err) }); return; }; - state.debug("turbonss database opened", .{}); + log.debug("turbonss database opened", .{}); - const omit_members_env = os.getenvZ(TURBONSS_OMIT_MEMBERS) orelse "auto"; - state.omit_members = shouldOmitMembers(omit_members_env, os.argv); - state.debug("omitting members from getgr* calls: {any}\n", .{state.omit_members}); - - return; + global_state = State{ + .file = file, + .omit_members = omit_members, + }; } -fn shouldOmitMembers(env: []const u8, argv: [][*:0]u8) bool { - if (mem.eql(u8, env, "1")) return true; - if (mem.eql(u8, env, "0")) return false; +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"); } -// destructor -export fn _turbo_fini() void { - if (state.file) |*fooo| - fooo.close(); -} - export fn _nss_turbo_getpwuid_r( uid: c_uint, res: *CUser, @@ -109,11 +80,16 @@ export fn _nss_turbo_getpwuid_r( len: usize, errnop: *c_int, ) c.enum_nss_status { - if (state.file == null) { + global_start.call(); + + if (global_state.file == null) { errnop.* = @enumToInt(os.E.AGAIN); return c.NSS_STATUS_UNAVAIL; } - const cuser = state.file.?.db.getpwuid(uid, buf[0..len]) catch |err| switch (err) { + + const db = global_state.file.?.db; + + const cuser = db.getpwuid(uid, buf[0..len]) catch |err| switch (err) { error.OutOfMemory => { errnop.* = @enumToInt(os.E.RANGE); return c.NSS_STATUS_TRYAGAIN; @@ -135,21 +111,4 @@ test "nss_turbo_getpwuid_r" { var errc = ErrCtx{}; var tf = try File.TestDB.init(testing.allocator, &errc); defer tf.deinit(); - - var env = try process.getEnvMap(testing.allocator); - defer env.deinit(); - - if (true) - return error.SkipZigTest; - - try testing.expectEqual(putenv(TURBONSS_VERBOSE, "1", 1), 0); - try testing.expectEqual(putenv(TURBONSS_DB, tf.path, 1), 0); - _turbo_init(); - try testing.expect(state.file != null); - - var buf = try testing.allocator.alloc(u8, state.file.?.db.getpwBufsize()); - var user: CUser = undefined; - var errno: c_int = undefined; - const ret = _nss_turbo_getpwuid_r(128, &user, buf.ptr, buf.len, &errno); - try testing.expectEqual(ret, c.NSS_STATUS_SUCCESS); } diff --git a/src/unix2db.zig b/src/unix2db.zig index 0046852..c0ee53d 100644 --- a/src/unix2db.zig +++ b/src/unix2db.zig @@ -156,7 +156,7 @@ test "trivial error: missing passwd file" { const exit_code = execute(allocator, stdout.writer(), stderr.writer(), args[0..]); try testing.expectEqual(@as(u8, 1), exit_code); - try testing.expectEqualStrings(stderr.items, "ERROR FileNotFound: open './passwd'\n"); + try testing.expectEqualStrings(stderr.items, "ERROR FileNotFound: open 'passwd'\n"); } test "fail" {