finish(?) turbonss-getent

This commit is contained in:
Motiejus Jakštys 2022-07-14 11:09:01 +03:00
parent c4e84be1a9
commit abf7edf14c
5 changed files with 97 additions and 47 deletions

View File

@ -80,6 +80,17 @@ pub fn build(b: *zbs.Builder) void {
exe.install();
}
{
const exe = b.addExecutable("turbo-getent", "src/turbo-getent.zig");
exe.strip = strip;
exe.linkLibC();
exe.linkLibrary(bdz);
exe.addIncludeDir("deps/cmph/src");
exe.setTarget(target);
exe.setBuildMode(mode);
exe.install();
}
{
const so = b.addSharedLibrary("nss_turbo", "src/libnss.zig", .{
.versioned = builtin.Version{

View File

@ -36,7 +36,7 @@ members_offset: u64,
pub const Entry = struct {
group: PackedGroup,
next: ?[]const u8,
end: usize,
};
pub fn fromBytes(bytes: []const u8) Entry {
@ -47,11 +47,6 @@ pub fn fromBytes(bytes: []const u8) Entry {
error.Overflow => unreachable,
};
const end_blob = end_strings + members_offset.bytes_read;
const next_start = pad.roundUp(usize, alignment_bits, end_blob);
var next: ?[]const u8 = null;
if (next_start < bytes.len)
next = bytes[next_start..];
return Entry{
.group = PackedGroup{
@ -59,7 +54,7 @@ pub fn fromBytes(bytes: []const u8) Entry {
.groupdata = bytes[start_blob..end_strings],
.members_offset = members_offset.value,
},
.next = next,
.end = pad.roundUp(usize, alignment_bits, end_blob),
};
}
@ -69,20 +64,26 @@ fn validateUtf8(s: []const u8) InvalidRecord!void {
}
pub const Iterator = struct {
section: ?[]const u8,
section: []const u8,
nextStart: usize = 0,
idx: u32 = 0,
total: u32,
pub fn next(it: *Iterator) ?PackedGroup {
if (it.section) |section| {
const entry = fromBytes(section);
it.section = entry.next;
return entry.group;
}
return null;
if (it.idx == it.total) return null;
const entry = fromBytes(it.section[it.nextStart..]);
it.nextStart += entry.end;
it.idx += 1;
return entry.group;
}
};
pub fn iterator(section: []const u8) Iterator {
return Iterator{ .section = section };
pub fn iterator(section: []const u8, total: u32) Iterator {
return Iterator{
.section = section,
.total = total,
};
}
pub fn gid(self: *const PackedGroup) u32 {
@ -139,7 +140,7 @@ test "PackedGroup construct" {
}
var i: u29 = 0;
var it = PackedGroup.iterator(buf.items);
var it = PackedGroup.iterator(buf.items, groups.len);
while (it.next()) |group| : (i += 1) {
try testing.expectEqual(groups[i].gid, group.gid());
try testing.expectEqualStrings(groups[i].name, group.name());

View File

@ -111,8 +111,7 @@ pub const Iterator = struct {
total: u32,
pub fn next(it: *Iterator) ?PackedUser {
if (it.idx == it.total)
return null;
if (it.idx == it.total) return null;
const entry = PackedUser.fromBytes(it.section[it.nextStart..]);
it.nextStart += entry.end;

View File

@ -296,6 +296,7 @@ fn setgrent(state: *State) void {
defer state.getgrent_iterator_mu.unlock();
state.getgrent_iterator = PackedGroup.iterator(
state.file.db.groups,
state.file.db.header.num_groups,
);
}

View File

@ -10,6 +10,7 @@ const flags = @import("flags.zig");
const DB = @import("DB.zig");
const File = @import("File.zig");
const PackedUser = @import("PackedUser.zig");
const PackedGroup = @import("PackedGroup.zig");
const User = @import("User.zig");
const Mode = enum { group, passwd };
@ -100,7 +101,7 @@ fn execute(
fn passwd(stdout: anytype, db: *const DB, keys: []const [*:0]const u8) u8 {
if (keys.len == 0)
return passwd_all(stdout, db);
return passwdAll(stdout, db);
var some_notfound = false;
const shell_reader = db.shellReader();
for (keys) |key| {
@ -122,7 +123,7 @@ fn passwd(stdout: anytype, db: *const DB, keys: []const [*:0]const u8) u8 {
return if (some_notfound) 2 else 0;
}
fn passwd_all(stdout: anytype, db: *const DB) u8 {
fn passwdAll(stdout: anytype, db: *const DB) u8 {
const shell_reader = db.shellReader();
var it = PackedUser.iterator(db.users, db.header.num_users, shell_reader);
while (it.next()) |packed_user| {
@ -134,7 +135,7 @@ fn passwd_all(stdout: anytype, db: *const DB) u8 {
fn group(stdout: anytype, db: *const DB, keys: []const [*:0]const u8) u8 {
if (keys.len == 0)
return group_all(stdout, db);
return groupAll(stdout, db);
var some_notfound = false;
for (keys) |key| {
@ -149,38 +150,48 @@ fn group(stdout: anytype, db: *const DB, keys: []const [*:0]const u8) u8 {
continue;
};
// not converting to Group to save a few memory allocations.
stdout.print("{s}:x:{d}:", .{ g.name(), g.gid() }) catch return 3;
// TODO: move member iteration from here and DB.packCGroup
// to a common place.
const members_slice = db.groupmembers[g.members_offset..];
var vit = compress.varintSliceIteratorMust(members_slice);
var it = compress.deltaDecompressionIterator(&vit);
// lines will be buffered, but flushed on every EOL.
var line_writer = io.bufferedWriter(stdout);
var i: usize = 0;
while (it.nextMust()) |member_offset| : (i += 1) {
const puser = PackedUser.fromBytes(db.users[member_offset << 3 ..]);
const name = puser.user.name();
if (i != 0)
_ = line_writer.write(",") catch return 3;
_ = line_writer.write(name) catch return 3;
}
_ = line_writer.write("\n") catch return 3;
line_writer.flush() catch return 3;
if (printGroup(stdout, db, &g)) |exit_code|
return exit_code;
}
return if (some_notfound) 2 else 0;
}
fn group_all(stdout: anytype, db: *const DB) u8 {
_ = stdout;
_ = db;
fn groupAll(stdout: anytype, db: *const DB) u8 {
var it = PackedGroup.iterator(db.groups, db.header.num_groups);
while (it.next()) |g|
if (printGroup(stdout, db, &g)) |exit_code|
return exit_code;
return 0;
}
fn printGroup(stdout: anytype, db: *const DB, g: *const PackedGroup) ?u8 {
// not converting to Group to save a few memory allocations.
stdout.print("{s}:x:{d}:", .{ g.name(), g.gid() }) catch return 3;
// TODO: move member iteration from here and DB.packCGroup
// to a common place.
const members_slice = db.groupmembers[g.members_offset..];
var vit = compress.varintSliceIteratorMust(members_slice);
var it = compress.deltaDecompressionIterator(&vit);
// lines will be buffered, but flushed on every EOL.
var line_writer = io.bufferedWriter(stdout);
var i: usize = 0;
while (it.nextMust()) |member_offset| : (i += 1) {
const puser = PackedUser.fromBytes(db.users[member_offset << 3 ..]);
const name = puser.user.name();
if (i != 0)
_ = line_writer.write(",") catch return 3;
_ = line_writer.write(name) catch return 3;
}
_ = line_writer.write("\n") catch return 3;
line_writer.flush() catch return 3;
return null;
}
const testing = std.testing;
test "turbo-getent passwd" {
@ -213,7 +224,7 @@ test "turbo-getent passwd" {
}
}
test "turbo-getent passwd_all" {
test "turbo-getent passwdAll" {
var tf = try File.TestDB.init(testing.allocator);
defer tf.deinit();
var stdout = ArrayList(u8).init(testing.allocator);
@ -278,3 +289,30 @@ test "turbo-getent group" {
try testing.expectEqualStrings(want, stdout.items);
}
}
test "turbo-getent groupAll" {
var tf = try File.TestDB.init(testing.allocator);
defer tf.deinit();
var stdout = ArrayList(u8).init(testing.allocator);
defer stdout.deinit();
var stderr = ArrayList(u8).init(testing.allocator);
defer stderr.deinit();
{
const args = &[_][*:0]const u8{
"--db",
tf.path,
"group",
};
const got = execute(stdout.writer(), stderr.writer(), args);
try testing.expectEqual(got, 0);
const want =
\\root:x:0:
\\vidmantas:x:128:
\\all:x:9999:NameNameNameNameNameNameNameName,root,svc-bar,vidmantas
\\service-group:x:100000:root,vidmantas
\\
;
try testing.expectEqualStrings(want, stdout.items);
}
}