1
Fork 0

finish(?) turbonss-getent

main
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(); 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", .{ const so = b.addSharedLibrary("nss_turbo", "src/libnss.zig", .{
.versioned = builtin.Version{ .versioned = builtin.Version{

View File

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

View File

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

View File

@ -296,6 +296,7 @@ fn setgrent(state: *State) void {
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,
); );
} }

View File

@ -10,6 +10,7 @@ const flags = @import("flags.zig");
const DB = @import("DB.zig"); const DB = @import("DB.zig");
const File = @import("File.zig"); const File = @import("File.zig");
const PackedUser = @import("PackedUser.zig"); const PackedUser = @import("PackedUser.zig");
const PackedGroup = @import("PackedGroup.zig");
const User = @import("User.zig"); const User = @import("User.zig");
const Mode = enum { group, passwd }; const Mode = enum { group, passwd };
@ -100,7 +101,7 @@ fn execute(
fn passwd(stdout: anytype, db: *const DB, keys: []const [*:0]const u8) u8 { fn passwd(stdout: anytype, db: *const DB, keys: []const [*:0]const u8) u8 {
if (keys.len == 0) if (keys.len == 0)
return passwd_all(stdout, db); return passwdAll(stdout, db);
var some_notfound = false; var some_notfound = false;
const shell_reader = db.shellReader(); const shell_reader = db.shellReader();
for (keys) |key| { 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; 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(); const shell_reader = db.shellReader();
var it = PackedUser.iterator(db.users, db.header.num_users, shell_reader); var it = PackedUser.iterator(db.users, db.header.num_users, shell_reader);
while (it.next()) |packed_user| { 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 { fn group(stdout: anytype, db: *const DB, keys: []const [*:0]const u8) u8 {
if (keys.len == 0) if (keys.len == 0)
return group_all(stdout, db); return groupAll(stdout, db);
var some_notfound = false; var some_notfound = false;
for (keys) |key| { for (keys) |key| {
@ -149,38 +150,48 @@ fn group(stdout: anytype, db: *const DB, keys: []const [*:0]const u8) u8 {
continue; continue;
}; };
// not converting to Group to save a few memory allocations. if (printGroup(stdout, db, &g)) |exit_code|
stdout.print("{s}:x:{d}:", .{ g.name(), g.gid() }) catch return 3; return exit_code;
// 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 if (some_notfound) 2 else 0; return if (some_notfound) 2 else 0;
} }
fn group_all(stdout: anytype, db: *const DB) u8 { fn groupAll(stdout: anytype, db: *const DB) u8 {
_ = stdout; var it = PackedGroup.iterator(db.groups, db.header.num_groups);
_ = db; while (it.next()) |g|
if (printGroup(stdout, db, &g)) |exit_code|
return exit_code;
return 0; 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; const testing = std.testing;
test "turbo-getent passwd" { 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); var tf = try File.TestDB.init(testing.allocator);
defer tf.deinit(); defer tf.deinit();
var stdout = ArrayList(u8).init(testing.allocator); var stdout = ArrayList(u8).init(testing.allocator);
@ -278,3 +289,30 @@ test "turbo-getent group" {
try testing.expectEqualStrings(want, stdout.items); 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);
}
}