fixes for libnss.zig

- global state is a pointer; avoid copying
- fix getgr* tests
This commit is contained in:
Motiejus Jakštys 2022-07-14 17:59:21 +03:00
parent abf7edf14c
commit 43015f4d1d

View File

@ -1,7 +1,6 @@
const std = @import("std"); const std = @import("std");
const os = std.os; const os = std.os;
const fmt = std.fmt; const fmt = std.fmt;
const log = std.log;
const mem = std.mem; const mem = std.mem;
const math = std.math; const math = std.math;
const heap = std.heap; const heap = std.heap;
@ -28,13 +27,12 @@ const ENV_OMIT_MEMBERS = "TURBONSS_OMIT_MEMBERS";
export var turbonss_db_path: [:0]const u8 = "/etc/turbonss/db.turbo"; export var turbonss_db_path: [:0]const u8 = "/etc/turbonss/db.turbo";
pub const log_level: std.log.Level = .debug;
// State is a type of the global variable holding the process state: // State is a type of the global variable holding the process state:
// the DB handle and all the iterators. // the DB handle and all the iterators.
const State = struct { const State = struct {
file: File, file: File,
omit_members: bool, omit_members: bool,
v: bool = false,
getpwent_iterator_mu: Mutex = Mutex{}, getpwent_iterator_mu: Mutex = Mutex{},
getpwent_iterator: ?PackedUser.Iterator = null, getpwent_iterator: ?PackedUser.Iterator = null,
@ -52,56 +50,65 @@ var global_init = once(init);
// assigns State from environment variables et al // assigns State from environment variables et al
fn init() void { fn init() void {
//if (os.getenvZ(ENV_LOGLEVEL)) |env| { //const verbose = blk: {
// const got = mem.sliceTo(env, 0); // if (os.getenvZ(ENV_LOGLEVEL)) |env| {
// if (mem.eql(u8, got, "0")) { // const got = mem.sliceTo(env, 0);
// log_level = .err; // if (mem.eql(u8, got, "0")) {
// } else if (mem.eql(u8, got, "1")) { // break :blk false;
// log_level = .warn; // } else if (mem.eql(u8, got, "1")) {
// } else if (mem.eql(u8, got, "2")) { // break :blk true;
// log_level = .info; // } else {
// } else if (mem.eql(u8, got, "3")) { // std.debug.print(
// log_level = .debug; // "warning: unrecognized {s}={s}. Expected between 0 or 1\n",
// } else { // .{ ENV_LOGLEVEL, got },
// std.debug.print( // );
// "warning: unrecognized {s}={s}. Expected between 0 and 3\n", // }
// .{ ENV_LOGLEVEL, got },
// );
// } // }
//} // break :blk false;
//};
const omit_members = blk: { //const omit_members = blk: {
if (os.getenvZ(ENV_OMIT_MEMBERS)) |env| { // if (os.getenvZ(ENV_OMIT_MEMBERS)) |env| {
const got = mem.sliceTo(env, 0); // const got = mem.sliceTo(env, 0);
if (mem.eql(u8, got, "1")) { // if (mem.eql(u8, got, "1")) {
break :blk true; // break :blk true;
} else if (mem.eql(u8, got, "0")) { // } else if (mem.eql(u8, got, "0")) {
break :blk false; // break :blk false;
} else if (mem.eql(u8, got, "auto")) { // } else if (mem.eql(u8, got, "auto")) {
// not set, do autodiscover // // not set, do autodiscover
} else { // } else {
std.debug.print( // std.debug.print(
"warning: unrecognized {s}={s}. Expected 0, 1 or auto\n", // "warning: unrecognized {s}={s}. Expected 0, 1 or auto\n",
.{ ENV_OMIT_MEMBERS, got }, // .{ ENV_OMIT_MEMBERS, got },
); // );
} // }
} // }
if (os.argv.len == 0) break :blk false; // if (os.argv.len == 0) break :blk false;
break :blk mem.eql(u8, mem.sliceTo(os.argv[0], 0), "id"); // break :blk mem.eql(u8, mem.sliceTo(os.argv[0], 0), "id");
}; //};
log.debug("omitting members from getgr* calls: {any}\n", .{omit_members});
const fname = os.getenvZ(ENV_DB) orelse turbonss_db_path; //if (verbose)
log.debug("opening '{s}'", .{fname}); // std.debug.print("omitting members from getgr* calls: {any}\n", .{omit_members});
//const fname = os.getenvZ(ENV_DB) orelse turbonss_db_path;
//if (verbose)
// std.debug.print("opening '{s}'\n", .{fname});
const fname = turbonss_db_path;
const verbose = true;
const omit_members = true;
const file = File.open(fname) catch |err| { const file = File.open(fname) catch |err| {
log.warn("open '{s}': {s}", .{ fname, @errorName(err) }); if (verbose)
std.debug.print("open '{s}': {s}\n", .{ fname, @errorName(err) });
return; return;
}; };
log.debug("turbonss database opened", .{});
if (verbose)
std.debug.print("turbonss database opened\n", .{});
global_state = State{ global_state = State{
.file = file, .file = file,
.v = verbose,
.omit_members = omit_members, .omit_members = omit_members,
.initgroups_dyn_allocator = heap.raw_c_allocator, .initgroups_dyn_allocator = heap.raw_c_allocator,
}; };
@ -115,7 +122,7 @@ export fn _nss_turbo_getpwuid_r(
errnop: *c_int, errnop: *c_int,
) c.enum_nss_status { ) c.enum_nss_status {
const db = getDBErrno(errnop) orelse return c.NSS_STATUS_UNAVAIL; const db = getDBErrno(errnop) orelse return c.NSS_STATUS_UNAVAIL;
return getpwuid_r(&db, uid, passwd, buffer, buflen, errnop); return getpwuid_r(db, uid, passwd, buffer, buflen, errnop);
} }
fn getpwuid_r( fn getpwuid_r(
@ -150,7 +157,7 @@ export fn _nss_turbo_getpwnam_r(
errnop: *c_int, errnop: *c_int,
) c.enum_nss_status { ) c.enum_nss_status {
const db = getDBErrno(errnop) orelse return c.NSS_STATUS_UNAVAIL; const db = getDBErrno(errnop) orelse return c.NSS_STATUS_UNAVAIL;
return getpwnam_r(&db, name, passwd, buffer, buflen, errnop); return getpwnam_r(db, name, passwd, buffer, buflen, errnop);
} }
fn getpwnam_r( fn getpwnam_r(
@ -188,7 +195,7 @@ export fn _nss_turbo_getgrgid_r(
errnop: *c_int, errnop: *c_int,
) c.enum_nss_status { ) c.enum_nss_status {
const state = getStateErrno(errnop) orelse return c.NSS_STATUS_UNAVAIL; const state = getStateErrno(errnop) orelse return c.NSS_STATUS_UNAVAIL;
return getgrgid_r(&state, gid, gr, buffer, buflen, errnop); return getgrgid_r(state, gid, gr, buffer, buflen, errnop);
} }
fn getgrgid_r( fn getgrgid_r(
@ -227,7 +234,7 @@ export fn _nss_turbo_getgrnam_r(
errnop: *c_int, errnop: *c_int,
) c.enum_nss_status { ) c.enum_nss_status {
const state = getStateErrno(errnop) orelse return c.NSS_STATUS_UNAVAIL; const state = getStateErrno(errnop) orelse return c.NSS_STATUS_UNAVAIL;
return getgrnam_r(&state, name, group, buffer, buflen, errnop); return getgrnam_r(state, name, group, buffer, buflen, errnop);
} }
fn getgrnam_r( fn getgrnam_r(
@ -256,13 +263,13 @@ fn getgrnam_r(
return c.NSS_STATUS_SUCCESS; return c.NSS_STATUS_SUCCESS;
} }
export fn _nss_turbo_setpwent(_: c_int) void { export fn _nss_turbo_setpwent(_: c_int) c.enum_nss_status {
global_init.call(); global_init.call();
var state = global_state orelse return; var state = global_state orelse return c.NSS_STATUS_UNAVAIL;
setpwent(&state); return setpwent(&state);
} }
fn setpwent(state: *State) void { fn setpwent(state: *State) c.enum_nss_status {
state.getpwent_iterator_mu.lock(); state.getpwent_iterator_mu.lock();
defer state.getpwent_iterator_mu.unlock(); defer state.getpwent_iterator_mu.unlock();
@ -272,43 +279,51 @@ fn setpwent(state: *State) void {
db.header.num_users, db.header.num_users,
db.shellReader(), db.shellReader(),
); );
std.debug.print("state.getpwent_iterator: {*}\n", .{
&state.getpwent_iterator.?,
});
return c.NSS_STATUS_SUCCESS;
} }
export fn _nss_turbo_endpwent() void { export fn _nss_turbo_endpwent() c.enum_nss_status {
global_init.call(); global_init.call();
var state = global_state orelse return; var state = global_state orelse return c.NSS_STATUS_UNAVAIL;
endpwent(&state); return endpwent(&state);
} }
fn endpwent(state: *State) void { fn endpwent(state: *State) c.enum_nss_status {
std.debug.print("endpwent\n", .{});
state.getpwent_iterator_mu.lock(); state.getpwent_iterator_mu.lock();
state.getpwent_iterator = null; state.getpwent_iterator = null;
state.getpwent_iterator_mu.unlock(); state.getpwent_iterator_mu.unlock();
return c.NSS_STATUS_SUCCESS;
} }
export fn _nss_turbo_setgrent(_: c_int) void { export fn _nss_turbo_setgrent(_: c_int) c.enum_nss_status {
var state = getState() orelse return; var state = getState() orelse return c.NSS_STATUS_UNAVAIL;
setgrent(&state); return setgrent(&state);
} }
fn setgrent(state: *State) void { fn setgrent(state: *State) c.enum_nss_status {
state.getgrent_iterator_mu.lock(); state.getgrent_iterator_mu.lock();
defer state.getgrent_iterator_mu.unlock(); defer state.getgrent_iterator_mu.unlock();
state.getgrent_iterator = PackedGroup.iterator( state.getgrent_iterator = PackedGroup.iterator(
state.file.db.groups, state.file.db.groups,
state.file.db.header.num_groups, state.file.db.header.num_groups,
); );
return c.NSS_STATUS_SUCCESS;
} }
export fn _nss_turbo_endgrent() void { export fn _nss_turbo_endgrent() c.enum_nss_status {
var state = getState() orelse return; var state = getState() orelse return c.NSS_STATUS_UNAVAIL;
endgrent(&state); return endgrent(&state);
} }
fn endgrent(state: *State) void { fn endgrent(state: *State) c.enum_nss_status {
state.getgrent_iterator_mu.lock(); state.getgrent_iterator_mu.lock();
state.getgrent_iterator = null; state.getgrent_iterator = null;
state.getgrent_iterator_mu.unlock(); state.getgrent_iterator_mu.unlock();
return c.NSS_STATUS_SUCCESS;
} }
export fn _nss_turbo_getgrent_r( export fn _nss_turbo_getgrent_r(
@ -318,7 +333,7 @@ export fn _nss_turbo_getgrent_r(
errnop: *c_int, errnop: *c_int,
) c.enum_nss_status { ) c.enum_nss_status {
var state = getStateErrno(errnop) orelse return c.NSS_STATUS_UNAVAIL; var state = getStateErrno(errnop) orelse return c.NSS_STATUS_UNAVAIL;
return getgrent_r(&state, result, buffer, buflen, errnop); return getgrent_r(state, result, buffer, buflen, errnop);
} }
fn getgrent_r( fn getgrent_r(
@ -338,7 +353,7 @@ fn getgrent_r(
}; };
const group = it.next() orelse { const group = it.next() orelse {
errnop.* = @enumToInt(os.E.NOENT); errnop.* = 0;
return c.NSS_STATUS_NOTFOUND; return c.NSS_STATUS_NOTFOUND;
}; };
@ -365,7 +380,7 @@ export fn _nss_turbo_getpwent_r(
errnop: *c_int, errnop: *c_int,
) c.enum_nss_status { ) c.enum_nss_status {
var state = getStateErrno(errnop) orelse return c.NSS_STATUS_UNAVAIL; var state = getStateErrno(errnop) orelse return c.NSS_STATUS_UNAVAIL;
return getpwent_r(&state, result, buffer, buflen, errnop); return getpwent_r(state, result, buffer, buflen, errnop);
} }
fn getpwent_r( fn getpwent_r(
@ -378,14 +393,17 @@ fn getpwent_r(
state.getpwent_iterator_mu.lock(); state.getpwent_iterator_mu.lock();
defer state.getpwent_iterator_mu.unlock(); defer state.getpwent_iterator_mu.unlock();
std.debug.print("starting getpwent_r\n", .{});
var it = state.getpwent_iterator orelse { var it = state.getpwent_iterator orelse {
// logic from _nss_systemd_getgrent_r // logic from _nss_systemd_getgrent_r
std.debug.print("no iterator, bailing\n", .{});
errnop.* = @enumToInt(os.E.HOSTDOWN); errnop.* = @enumToInt(os.E.HOSTDOWN);
return c.NSS_STATUS_UNAVAIL; return c.NSS_STATUS_UNAVAIL;
}; };
const user = it.next() orelse { const user = it.next() orelse {
errnop.* = @enumToInt(os.E.NOENT); std.debug.print("end of iteration, returning 0\n", .{});
errnop.* = 0;
return c.NSS_STATUS_NOTFOUND; return c.NSS_STATUS_NOTFOUND;
}; };
@ -411,7 +429,7 @@ export fn _nss_turbo_initgroups_dyn(
errnop: *c_int, errnop: *c_int,
) c.enum_nss_status { ) c.enum_nss_status {
const state = getStateErrno(errnop) orelse return c.NSS_STATUS_UNAVAIL; const state = getStateErrno(errnop) orelse return c.NSS_STATUS_UNAVAIL;
return initgroups_dyn(&state, user_name, gid, start, size, groupsp, limit, errnop); return initgroups_dyn(state, user_name, gid, start, size, groupsp, limit, errnop);
} }
fn initgroups_dyn( fn initgroups_dyn(
@ -475,9 +493,9 @@ fn getState() ?State {
return global_state; return global_state;
} }
fn getStateErrno(errnop: *c_int) ?State { fn getStateErrno(errnop: *c_int) ?*State {
global_init.call(); global_init.call();
if (global_state) |state| { if (global_state) |*state| {
return state; return state;
} else { } else {
errnop.* = @enumToInt(os.E.AGAIN); errnop.* = @enumToInt(os.E.AGAIN);
@ -485,9 +503,9 @@ fn getStateErrno(errnop: *c_int) ?State {
} }
} }
fn getDBErrno(errnop: *c_int) ?DB { fn getDBErrno(errnop: *c_int) ?*const DB {
if (getStateErrno(errnop)) |state| { if (getStateErrno(errnop)) |state| {
return state.file.db; return &state.file.db;
} else return null; } else return null;
} }
@ -548,18 +566,19 @@ fn testVidmantas(u: CUser) !void {
test "libnss getgrgid_r and getgrnam_r" { test "libnss getgrgid_r and getgrnam_r" {
var tf = try File.TestDB.init(testing.allocator); var tf = try File.TestDB.init(testing.allocator);
defer tf.deinit(); defer tf.deinit();
const turbonss_db_path_old = turbonss_db_path;
turbonss_db_path = tf.path; const state = State{
defer { .file = tf.file,
turbonss_db_path = turbonss_db_path_old; .omit_members = false,
} .initgroups_dyn_allocator = testing.failing_allocator,
};
var buf: [1024]u8 = undefined; var buf: [1024]u8 = undefined;
var errno: c_int = 0; var errno: c_int = 0;
var group: CGroup = undefined; var group: CGroup = undefined;
try testing.expectEqual( try testing.expectEqual(
c.NSS_STATUS_SUCCESS, c.NSS_STATUS_SUCCESS,
_nss_turbo_getgrgid_r(100000, &group, &buf, buf.len, &errno), getgrgid_r(&state, 100000, &group, &buf, buf.len, &errno),
); );
try testing.expectEqual(@as(c_int, 0), errno); try testing.expectEqual(@as(c_int, 0), errno);
try testSvcGroup(group); try testSvcGroup(group);
@ -567,7 +586,7 @@ test "libnss getgrgid_r and getgrnam_r" {
group = undefined; group = undefined;
try testing.expectEqual( try testing.expectEqual(
c.NSS_STATUS_SUCCESS, c.NSS_STATUS_SUCCESS,
_nss_turbo_getgrnam_r("service-group", &group, &buf, buf.len, &errno), getgrnam_r(&state, "service-group", &group, &buf, buf.len, &errno),
); );
try testing.expectEqual(@as(c_int, 0), errno); try testing.expectEqual(@as(c_int, 0), errno);
try testSvcGroup(group); try testSvcGroup(group);
@ -575,7 +594,7 @@ test "libnss getgrgid_r and getgrnam_r" {
group = undefined; group = undefined;
try testing.expectEqual( try testing.expectEqual(
c.NSS_STATUS_NOTFOUND, c.NSS_STATUS_NOTFOUND,
_nss_turbo_getgrnam_r("does not exist", &group, &buf, buf.len, &errno), getgrnam_r(&state, "does not exist", &group, &buf, buf.len, &errno),
); );
try testing.expectEqual(@enumToInt(os.E.NOENT), @intCast(u16, errno)); try testing.expectEqual(@enumToInt(os.E.NOENT), @intCast(u16, errno));
} }