From bd13e6a322038986a697fc3a4b9ec62307522793 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Motiejus=20Jak=C5=A1tys?= Date: Fri, 15 Jul 2022 09:01:51 +0300 Subject: [PATCH] turbonss-analyze: most memberships --- src/PackedGroup.zig | 6 ++--- src/turbonss-analyze.zig | 58 ++++++++++++++++++++++++++++++++++------ 2 files changed, 53 insertions(+), 11 deletions(-) diff --git a/src/PackedGroup.zig b/src/PackedGroup.zig index 9f1f363..ebe8404 100644 --- a/src/PackedGroup.zig +++ b/src/PackedGroup.zig @@ -86,15 +86,15 @@ pub fn iterator(section: []const u8, total: u32) Iterator { }; } -pub fn gid(self: *const PackedGroup) u32 { +pub inline fn gid(self: *const PackedGroup) u32 { return self.inner.gid; } -pub fn membersOffset(self: *const PackedGroup) u64 { +pub inline fn membersOffset(self: *const PackedGroup) u64 { return self.members_offset; } -pub fn name(self: *const PackedGroup) []const u8 { +pub inline fn name(self: *const PackedGroup) []const u8 { return self.groupdata; } diff --git a/src/turbonss-analyze.zig b/src/turbonss-analyze.zig index f6041a7..7a7dfa2 100644 --- a/src/turbonss-analyze.zig +++ b/src/turbonss-analyze.zig @@ -12,9 +12,11 @@ const Allocator = std.mem.Allocator; const BoundedArray = std.BoundedArray; const flags = @import("flags.zig"); +const compress = @import("compress.zig"); const DB = @import("DB.zig"); const File = @import("File.zig"); const PackedUser = @import("PackedUser.zig"); +const PackedGroup = @import("PackedGroup.zig"); const Header = @import("header.zig").Header; const section_length_bits = @import("header.zig").section_length_bits; @@ -61,12 +63,12 @@ fn execute( const myflags = flags.parse(argv, &[_]flags.Flag{ .{ .name = "-h", .kind = .boolean }, }) catch { - stderr.writeAll(usage) catch {}; + stderr.writeAll(usage) catch return 3; return 1; }; if (myflags.boolFlag("-h")) { - stdout.writeAll(usage) catch return 1; + stdout.writeAll(usage) catch return 3; return 0; } @@ -74,8 +76,8 @@ fn execute( 0 => "/etc/turbonss/db.turbo", 1 => mem.span(myflags.args[0]), else => { - stderr.print("ERROR: too many arguments\n", .{}) catch {}; - stderr.writeAll(usage) catch {}; + stderr.print("ERROR: too many arguments\n", .{}) catch return 3; + stderr.writeAll(usage) catch return 3; return 1; }, }; @@ -133,14 +135,54 @@ fn execute( \\Users: {[users]d} \\Groups: {[groups]d} \\Shells: {[shells]d} - \\Sections: - \\ Name Begin End Size bytes \\ ; - stdout.print(template, info) catch {}; + stdout.print(template, info) catch return 3; + + // Popularity contest: + // - user with most groups + // - TODO: group with most users. Not trivial, because + // group memberships do not include users whose primary + // gid is the target one. + if (db.header.num_users > 0) { + const Name = BoundedArray(u8, 32); + const Popular = struct { + name: Name = Name.init(0) catch unreachable, + score: u64 = 0, + }; + + var popUser = Popular{}; + var it = PackedUser.iterator( + db.users, + db.header.num_users, + db.shellReader(), + ); + while (it.next()) |packed_user| { + const offset = packed_user.additional_gids_offset; + const additional_gids = db.additional_gids[offset..]; + const vit = compress.varintSliceIteratorMust(additional_gids); + // the primary gid of the user is never in "additional gids" + const ngroups = vit.remaining + 1; + if (ngroups > popUser.score) { + const name = packed_user.name(); + popUser.name = Name.fromSlice(name) catch unreachable; + popUser.score = ngroups; + } + } + + stdout.print("Most memberships: {s} ({d})\n", .{ + popUser.name.constSlice(), + popUser.score, + }) catch return 3; + } var lengths = DB.fieldLengths(db.header); var offsets = DB.fieldOffsets(lengths); + stdout.writeAll( + \\Sections: + \\ Name Begin End Size bytes + \\ + ) catch return 3; inline for (meta.fields(DB.DBNumbers)) |field| { const length = @field(lengths, field.name); @@ -151,7 +193,7 @@ fn execute( start << section_length_bits, end << section_length_bits, splitInt(length << section_length_bits).constSlice(), - }) catch {}; + }) catch return 3; } return 0;