getent passwd

This commit is contained in:
Motiejus Jakštys 2022-07-11 11:11:25 +03:00
parent aaf9d2cc67
commit f0777b9e94
6 changed files with 151 additions and 16 deletions

View File

@ -388,15 +388,18 @@ fn pushStr(str: []const u8, buf: []u8, offset: *usize) [*:0]const u8 {
return buf[start .. start + str.len :0].ptr; return buf[start .. start + str.len :0].ptr;
} }
pub fn writeUser(self: *const DB, user: PackedUser, buf: []u8) error{BufferTooSmall}!CUser { pub fn shellReader(self: *const DB) ShellReader {
const shell_reader = ShellReader{ return ShellReader{
.index = self.shell_index, .index = self.shell_index,
.blob = self.shell_blob, .blob = self.shell_blob,
}; };
}
pub fn writeUser(self: *const DB, user: PackedUser, buf: []u8) error{BufferTooSmall}!CUser {
const name = user.name(); const name = user.name();
const gecos = user.gecos(); const gecos = user.gecos();
const home = user.home(); const home = user.home();
const shell = user.shell(shell_reader); const shell = user.shell(self.shellReader());
const strlen = const strlen =
name.len + 1 + name.len + 1 +
@ -421,7 +424,7 @@ pub fn writeUser(self: *const DB, user: PackedUser, buf: []u8) error{BufferTooSm
}; };
} }
pub fn getUser(self: *const DB, name: []const u8) ?PackedUser { pub fn getUserByName(self: *const DB, name: []const u8) ?PackedUser {
const idx = bdz.search(self.bdz_username, name); const idx = bdz.search(self.bdz_username, name);
// bdz may return a hash that's bigger than the number of users // bdz may return a hash that's bigger than the number of users
if (idx >= self.header.num_users) return null; if (idx >= self.header.num_users) return null;
@ -434,18 +437,23 @@ pub fn getUser(self: *const DB, name: []const u8) ?PackedUser {
// get a CUser entry by name. // get a CUser entry by name.
pub fn getpwnam(self: *const DB, name: []const u8, buf: []u8) error{BufferTooSmall}!?CUser { pub fn getpwnam(self: *const DB, name: []const u8, buf: []u8) error{BufferTooSmall}!?CUser {
const user = self.getUser(name) orelse return null; const user = self.getUserByName(name) orelse return null;
return try self.writeUser(user, buf); return try self.writeUser(user, buf);
} }
// get a CUser entry by uid. pub fn getUserByUid(self: *const DB, uid: u32) ?PackedUser {
pub fn getpwuid(self: *const DB, uid: u32, buf: []u8) error{BufferTooSmall}!?CUser {
const idx = bdz.search_u32(self.bdz_uid, uid); const idx = bdz.search_u32(self.bdz_uid, uid);
if (idx >= self.header.num_users) return null; if (idx >= self.header.num_users) return null;
const offset = self.idx_uid2user[idx]; const offset = self.idx_uid2user[idx];
const nbits = PackedUser.alignment_bits; const nbits = PackedUser.alignment_bits;
const user = PackedUser.fromBytes(self.users[offset << nbits ..]).user; const user = PackedUser.fromBytes(self.users[offset << nbits ..]).user;
if (uid != user.uid()) return null; if (uid != user.uid()) return null;
return user;
}
// get a CUser entry by uid.
pub fn getpwuid(self: *const DB, uid: u32, buf: []u8) error{BufferTooSmall}!?CUser {
const user = self.getUserByUid(uid) orelse return null;
return try self.writeUser(user, buf); return try self.writeUser(user, buf);
} }

View File

@ -205,6 +205,18 @@ pub fn shell(self: PackedUser, shell_reader: ShellReader) []const u8 {
return shell_reader.get(self.inner.shell_len_or_idx); return shell_reader.get(self.inner.shell_len_or_idx);
} }
// returns a User representation of the PackedUser.
pub fn toUser(self: *const PackedUser, shell_reader: ShellReader) User {
return User{
.uid = self.uid(),
.gid = self.gid(),
.name = self.name(),
.gecos = self.gecos(),
.home = self.home(),
.shell = self.shell(shell_reader),
};
}
pub const max_str_len = pub const max_str_len =
math.maxInt(fieldInfo(Inner, .shell_len_or_idx).field_type) + 1 + math.maxInt(fieldInfo(Inner, .shell_len_or_idx).field_type) + 1 +
math.maxInt(fieldInfo(Inner, .home_len).field_type) + 1 + math.maxInt(fieldInfo(Inner, .home_len).field_type) + 1 +

View File

@ -266,13 +266,8 @@ fn setpwent(state: *State) void {
state.getpwent_iterator_mu.lock(); state.getpwent_iterator_mu.lock();
defer state.getpwent_iterator_mu.unlock(); defer state.getpwent_iterator_mu.unlock();
state.getpwent_iterator = PackedUser.iterator( const db = state.file.db;
state.file.db.users, state.getpwent_iterator = PackedUser.iterator(db.users, db.shellReader());
ShellReader{
.index = state.file.db.shell_index,
.blob = state.file.db.shell_blob,
},
);
} }
export fn _nss_turbo_endpwent() void { export fn _nss_turbo_endpwent() void {
@ -425,7 +420,7 @@ fn initgroups_dyn(
errnop: *c_int, errnop: *c_int,
) c.enum_nss_status { ) c.enum_nss_status {
const db = state.file.db; const db = state.file.db;
const user = db.getUser(mem.sliceTo(user_name, 0)) orelse { const user = db.getUserByName(mem.sliceTo(user_name, 0)) orelse {
errnop.* = @enumToInt(os.E.NOENT); errnop.* = @enumToInt(os.E.NOENT);
return c.NSS_STATUS_NOTFOUND; return c.NSS_STATUS_NOTFOUND;
}; };

View File

@ -16,6 +16,7 @@ test "turbonss test suite" {
_ = @import("validate.zig"); _ = @import("validate.zig");
// main // main
_ = @import("turbo-getent.zig");
_ = @import("turbo-unix2db.zig"); _ = @import("turbo-unix2db.zig");
_ = @import("turbo-analyze.zig"); _ = @import("turbo-analyze.zig");
} }

View File

@ -12,7 +12,6 @@ const Allocator = std.mem.Allocator;
const BoundedArray = std.BoundedArray; const BoundedArray = std.BoundedArray;
const flags = @import("flags.zig"); 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");

View File

@ -1,9 +1,16 @@
const std = @import("std"); const std = @import("std");
const io = std.io; const io = std.io;
const os = std.os; const os = std.os;
const mem = std.mem;
const fmt = std.fmt; const fmt = std.fmt;
const ArrayList = std.ArrayList;
const flags = @import("flags.zig"); const flags = @import("flags.zig");
const DB = @import("DB.zig");
const File = @import("File.zig");
const PackedUser = @import("PackedUser.zig");
const Mode = enum { group, passwd };
const usage = const usage =
\\usage: turbo-getent [OPTION]... group|passwd [key...] \\usage: turbo-getent [OPTION]... group|passwd [key...]
@ -16,6 +23,13 @@ const usage =
\\entries that match the supplied keys will be displayed. If no key is \\entries that match the supplied keys will be displayed. If no key is
\\provided, all entries will be displayed. \\provided, all entries will be displayed.
\\ \\
\\Exit codes:
\\
\\ 0 command completed successfully
\\ 1 invalid arguments or db could not be opened
\\ 2 one or more supplied key could not be found in the database
\\ 3 output error
\\
; ;
pub fn main() !void { pub fn main() !void {
@ -36,6 +50,7 @@ fn execute(
) u8 { ) u8 {
const myflags = flags.parse(argv, &[_]flags.Flag{ const myflags = flags.parse(argv, &[_]flags.Flag{
.{ .name = "-h", .kind = .boolean }, .{ .name = "-h", .kind = .boolean },
.{ .name = "--db", .kind = .arg },
}) catch { }) catch {
stderr.writeAll(usage) catch {}; stderr.writeAll(usage) catch {};
return 1; return 1;
@ -46,5 +61,110 @@ fn execute(
return 0; return 0;
} }
if (myflags.args.len == 0) {
stderr.writeAll(usage) catch {};
return 1;
}
const arg0 = mem.span(myflags.args[0]);
const mode: Mode = blk: {
if (mem.eql(u8, arg0, "passwd")) {
break :blk .passwd;
} else if (mem.eql(u8, arg0, "group")) {
break :blk .group;
} else {
stderr.print("bad argument {s}: expected passwd or group\n", .{
myflags.args[0],
}) catch return 3;
return 1;
}
};
const db_file = myflags.argFlag("--db") orelse "/etc/turbonss/db.turbo";
var file = File.open(db_file) catch |err| {
stderr.print(
"ERROR {s}: file '{s}' is corrupted or cannot be read\n",
.{ @errorName(err), db_file },
) catch return 3;
return 1;
};
defer file.close();
return switch (mode) {
.passwd => passwd(stdout, &file.db, myflags.args[1..]),
.group => group(stdout, &file.db, myflags.args[1..]),
};
}
fn passwd(stdout: anytype, db: *const DB, keys: []const [*:0]const u8) u8 {
var some_notfound = false;
if (keys.len == 0)
return passwd_all(stdout, db);
const shell_reader = db.shellReader();
for (keys) |key| {
const keyZ = mem.span(key);
const maybe_packed_user = if (fmt.parseUnsigned(u32, keyZ, 10)) |uid|
db.getUserByUid(uid)
else |_|
db.getUserByName(keyZ);
const packed_user = maybe_packed_user orelse {
some_notfound = true;
continue;
};
const line = packed_user.toUser(shell_reader).toLine();
stdout.writeAll(line.constSlice()) catch return 3;
}
return if (some_notfound) 2 else 0;
}
fn passwd_all(stdout: anytype, db: *const DB) u8 {
var it = PackedUser.iterator(db.users, db.shellReader());
while (it.next()) |packed_user| {
const line = packed_user.toUser(db.shellReader()).toLine();
stdout.writeAll(line.constSlice()) catch return 3;
}
return 0;
}
const testing = std.testing;
test "passwd and passwd_all" {
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,
"passwd",
"root",
"doesnotexist",
"vidmantas",
"0",
"1",
};
const got = execute(stdout.writer(), stderr.writer(), args);
try testing.expectEqual(got, 2);
const want_root = "root:x:0:0::/root:/bin/bash\n";
try testing.expectEqualStrings(stdout.items[0..want_root.len], want_root);
const want_vidmantas = "vidmantas:x:128:128:Vidmantas Kaminskas:/home/vidmantas:/bin/bash\n";
var offset: usize = want_root.len + want_vidmantas.len;
try testing.expectEqualStrings(stdout.items[want_root.len..offset], want_vidmantas);
try testing.expectEqualStrings(stdout.items[offset..], want_root);
}
fn group(stdout: anytype, db: *const DB, keys: []const [*:0]const u8) u8 {
_ = db;
_ = stdout;
_ = keys;
return 0; return 0;
} }