finish(?) turbonss-getent
This commit is contained in:
parent
c4e84be1a9
commit
abf7edf14c
11
build.zig
11
build.zig
@ -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{
|
||||||
|
@ -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());
|
||||||
|
@ -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;
|
||||||
|
@ -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,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user