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;
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 +
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
@ -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");
|
||||||
}
|
}
|
||||||
|
@ -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");
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user