1
Fork 0
turbonss/src/libnss.zig

154 lines
4.6 KiB
Zig

const std = @import("std");
const os = std.os;
const fmt = std.fmt;
const mem = std.mem;
const process = std.process;
const DB = @import("DB.zig");
const File = @import("File.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 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;
export const turbonss_default_path: [:0]const u8 = "/etc/turbonss/db.turbo";
// 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,
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;
// constructor
export fn _turbo_init() void {
//if (os.getenvZ(TURBONSS_VERBOSE)) |env|
// state.verbose = mem.eql(u8, env, "1");
if (getenv(TURBONSS_VERBOSE)) |env| {
const envZ = mem.sliceTo(env, 0);
state.verbose = mem.eql(u8, envZ, "1");
}
//std.debug.print("TURBONSS_VERBOSE: {s}\n", .{os.getenv(TURBONSS_VERBOSE)});
std.debug.print("TURBONSS_VERBOSE: {s}\n", .{getenv(TURBONSS_VERBOSE)});
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) });
return;
};
state.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;
}
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;
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,
buf: [*]u8,
len: usize,
errnop: *c_int,
) c.enum_nss_status {
if (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) {
error.OutOfMemory => {
errnop.* = @enumToInt(os.E.RANGE);
return c.NSS_STATUS_TRYAGAIN;
},
};
if (cuser == null) {
errnop.* = @enumToInt(os.E.NOENT);
return c.NSS_STATUS_NOTFOUND;
}
res.* = cuser.?;
return c.NSS_STATUS_SUCCESS;
}
const testing = std.testing;
test "nss_turbo_getpwuid_r" {
var tf = try File.TestFile.init(testing.allocator);
defer tf.deinit(testing.allocator);
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);
}