getent passwd
This commit is contained in:
parent
aaf9d2cc67
commit
f0777b9e94
22
src/DB.zig
22
src/DB.zig
@ -388,15 +388,18 @@ fn pushStr(str: []const u8, buf: []u8, offset: *usize) [*:0]const u8 {
|
||||
return buf[start .. start + str.len :0].ptr;
|
||||
}
|
||||
|
||||
pub fn writeUser(self: *const DB, user: PackedUser, buf: []u8) error{BufferTooSmall}!CUser {
|
||||
const shell_reader = ShellReader{
|
||||
pub fn shellReader(self: *const DB) ShellReader {
|
||||
return ShellReader{
|
||||
.index = self.shell_index,
|
||||
.blob = self.shell_blob,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn writeUser(self: *const DB, user: PackedUser, buf: []u8) error{BufferTooSmall}!CUser {
|
||||
const name = user.name();
|
||||
const gecos = user.gecos();
|
||||
const home = user.home();
|
||||
const shell = user.shell(shell_reader);
|
||||
const shell = user.shell(self.shellReader());
|
||||
|
||||
const strlen =
|
||||
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);
|
||||
// bdz may return a hash that's bigger than the number of users
|
||||
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.
|
||||
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);
|
||||
}
|
||||
|
||||
// get a CUser entry by uid.
|
||||
pub fn getpwuid(self: *const DB, uid: u32, buf: []u8) error{BufferTooSmall}!?CUser {
|
||||
pub fn getUserByUid(self: *const DB, uid: u32) ?PackedUser {
|
||||
const idx = bdz.search_u32(self.bdz_uid, uid);
|
||||
if (idx >= self.header.num_users) return null;
|
||||
const offset = self.idx_uid2user[idx];
|
||||
const nbits = PackedUser.alignment_bits;
|
||||
const user = PackedUser.fromBytes(self.users[offset << nbits ..]).user;
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -205,6 +205,18 @@ pub fn shell(self: PackedUser, shell_reader: ShellReader) []const u8 {
|
||||
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 =
|
||||
math.maxInt(fieldInfo(Inner, .shell_len_or_idx).field_type) + 1 +
|
||||
math.maxInt(fieldInfo(Inner, .home_len).field_type) + 1 +
|
||||
|
@ -266,13 +266,8 @@ fn setpwent(state: *State) void {
|
||||
state.getpwent_iterator_mu.lock();
|
||||
defer state.getpwent_iterator_mu.unlock();
|
||||
|
||||
state.getpwent_iterator = PackedUser.iterator(
|
||||
state.file.db.users,
|
||||
ShellReader{
|
||||
.index = state.file.db.shell_index,
|
||||
.blob = state.file.db.shell_blob,
|
||||
},
|
||||
);
|
||||
const db = state.file.db;
|
||||
state.getpwent_iterator = PackedUser.iterator(db.users, db.shellReader());
|
||||
}
|
||||
|
||||
export fn _nss_turbo_endpwent() void {
|
||||
@ -425,7 +420,7 @@ fn initgroups_dyn(
|
||||
errnop: *c_int,
|
||||
) c.enum_nss_status {
|
||||
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);
|
||||
return c.NSS_STATUS_NOTFOUND;
|
||||
};
|
||||
|
@ -16,6 +16,7 @@ test "turbonss test suite" {
|
||||
_ = @import("validate.zig");
|
||||
|
||||
// main
|
||||
_ = @import("turbo-getent.zig");
|
||||
_ = @import("turbo-unix2db.zig");
|
||||
_ = @import("turbo-analyze.zig");
|
||||
}
|
||||
|
@ -12,7 +12,6 @@ const Allocator = std.mem.Allocator;
|
||||
const BoundedArray = std.BoundedArray;
|
||||
|
||||
const flags = @import("flags.zig");
|
||||
|
||||
const DB = @import("DB.zig");
|
||||
const File = @import("File.zig");
|
||||
const PackedUser = @import("PackedUser.zig");
|
||||
|
@ -1,9 +1,16 @@
|
||||
const std = @import("std");
|
||||
const io = std.io;
|
||||
const os = std.os;
|
||||
const mem = std.mem;
|
||||
const fmt = std.fmt;
|
||||
const ArrayList = std.ArrayList;
|
||||
|
||||
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 =
|
||||
\\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
|
||||
\\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 {
|
||||
@ -36,6 +50,7 @@ fn execute(
|
||||
) u8 {
|
||||
const myflags = flags.parse(argv, &[_]flags.Flag{
|
||||
.{ .name = "-h", .kind = .boolean },
|
||||
.{ .name = "--db", .kind = .arg },
|
||||
}) catch {
|
||||
stderr.writeAll(usage) catch {};
|
||||
return 1;
|
||||
@ -46,5 +61,110 @@ fn execute(
|
||||
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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user