@@ -36,7 +36,7 @@ const impl = struct {
3636 allocator : std.mem.Allocator ,
3737 io : IO ,
3838 args : * Arg.Iterator ,
39- cwd : std.fs.Dir ,
39+ system : System ,
4040 exe_path : []const u8 ,
4141 ) Command.Error ! void {
4242 const z : tracy.Zone = .begin (.{ .src = @src (), .name = command .name });
@@ -46,25 +46,50 @@ const impl = struct {
4646
4747 const opt_arg = try args .nextWithHelpOrVersion (true );
4848
49- const passwd_file = try shared .mapFile (command , allocator , io , cwd , "/etc/passwd" );
50- defer passwd_file .close ();
49+ const mapped_passwd_file = blk : {
50+ const passwd_file = system .cwd ().openFile ("/etc/passwd" , .{}) catch | err |
51+ return command .printErrorAlloc (
52+ allocator ,
53+ io ,
54+ "unable to open '/etc/passwd': {s}" ,
55+ .{@errorName (err )},
56+ );
57+ errdefer if (shared .free_on_close ) passwd_file .close ();
58+
59+ const stat = passwd_file .stat () catch | err |
60+ return command .printErrorAlloc (
61+ allocator ,
62+ io ,
63+ "unable to stat '/etc/passwd': {s}" ,
64+ .{@errorName (err )},
65+ );
66+
67+ break :blk passwd_file .mapReadonly (stat .size ) catch | err |
68+ return command .printErrorAlloc (
69+ allocator ,
70+ io ,
71+ "unable to map '/etc/passwd': {s}" ,
72+ .{@errorName (err )},
73+ );
74+ };
75+ defer if (shared .free_on_close ) mapped_passwd_file .close ();
5176
5277 return if (opt_arg ) | arg |
53- namedUser (allocator , io , arg .raw , passwd_file .file_contents , cwd )
78+ namedUser (allocator , io , arg .raw , mapped_passwd_file .file_contents , system )
5479 else
55- currentUser (allocator , io , passwd_file .file_contents , cwd );
80+ currentUser (allocator , io , mapped_passwd_file .file_contents , system );
5681 }
5782
5883 fn currentUser (
5984 allocator : std.mem.Allocator ,
6085 io : IO ,
6186 passwd_file_contents : []const u8 ,
62- cwd : std.fs.Dir ,
87+ system : System ,
6388 ) Command.Error ! void {
6489 const z : tracy.Zone = .begin (.{ .src = @src (), .name = "current user" });
6590 defer z .end ();
6691
67- const euid = std . os . linux . geteuid ();
92+ const euid = system . getEffectiveUserId ();
6893
6994 log .debug ("currentUser called, euid: {}" , .{euid });
7095
@@ -98,7 +123,7 @@ const impl = struct {
98123 "format of '/etc/passwd' is invalid" ,
99124 );
100125
101- return printGroups (allocator , entry .user_name , primary_group_id , io , cwd );
126+ return printGroups (allocator , entry .user_name , primary_group_id , io , system );
102127 }
103128
104129 return command .printError (
@@ -112,7 +137,7 @@ const impl = struct {
112137 io : IO ,
113138 user : []const u8 ,
114139 passwd_file_contents : []const u8 ,
115- cwd : std.fs.Dir ,
140+ system : System ,
116141 ) Command.Error ! void {
117142 const z : tracy.Zone = .begin (.{ .src = @src (), .name = "namedUser" });
118143 defer z .end ();
@@ -140,7 +165,7 @@ const impl = struct {
140165 "format of '/etc/passwd' is invalid" ,
141166 );
142167
143- return printGroups (allocator , entry .user_name , primary_group_id , io , cwd );
168+ return printGroups (allocator , entry .user_name , primary_group_id , io , system );
144169 }
145170
146171 return command .printErrorAlloc (allocator , io , "unknown user '{s}'" , .{user });
@@ -151,7 +176,7 @@ const impl = struct {
151176 user : []const u8 ,
152177 primary_group_id : std.posix.uid_t ,
153178 io : IO ,
154- cwd : std.fs.Dir ,
179+ system : System ,
155180 ) ! void {
156181 const z : tracy.Zone = .begin (.{ .src = @src (), .name = "print groups" });
157182 defer z .end ();
@@ -162,10 +187,35 @@ const impl = struct {
162187 .{ user , primary_group_id },
163188 );
164189
165- const group_file = try shared .mapFile (command , allocator , io , cwd , "/etc/group" );
166- defer group_file .close ();
190+ const mapped_group_file = blk : {
191+ const group_file = system .cwd ().openFile ("/etc/group" , .{}) catch | err |
192+ return command .printErrorAlloc (
193+ allocator ,
194+ io ,
195+ "unable to open '/etc/group': {s}" ,
196+ .{@errorName (err )},
197+ );
198+ errdefer if (shared .free_on_close ) group_file .close ();
199+
200+ const stat = group_file .stat () catch | err |
201+ return command .printErrorAlloc (
202+ allocator ,
203+ io ,
204+ "unable to stat '/etc/group': {s}" ,
205+ .{@errorName (err )},
206+ );
167207
168- var group_file_iter = shared .groupFileIterator (group_file .file_contents );
208+ break :blk group_file .mapReadonly (stat .size ) catch | err |
209+ return command .printErrorAlloc (
210+ allocator ,
211+ io ,
212+ "unable to map '/etc/group': {s}" ,
213+ .{@errorName (err )},
214+ );
215+ };
216+ defer if (shared .free_on_close ) mapped_group_file .close ();
217+
218+ var group_file_iter = shared .groupFileIterator (mapped_group_file .file_contents );
169219
170220 var first = true ;
171221
@@ -211,14 +261,55 @@ const impl = struct {
211261 try command .testVersion ();
212262 }
213263
214- // TODO: How do we test this without introducing the amount of complexity that https://github.com/leecannon/zsw does?
215- // https://github.com/leecannon/zig-coreutils/issues/7
264+ test "groups" {
265+ const passwd_contents =
266+ \\root:x:0:0::/root:/usr/bin/bash
267+ \\daemon:x:1:1::/:/usr/sbin/nologin
268+ \\bin:x:2:2::/:/usr/sbin/nologin
269+ \\sys:x:3:3::/:/usr/sbin/nologin
270+ \\user:x:1001:1001:A User:/home/user:/usr/bin/zsh
271+ \\
272+ ;
273+
274+ const group_contents =
275+ \\root:x:0:
276+ \\daemon:x:1:
277+ \\bin:x:2:
278+ \\sys:x:3:user
279+ \\user:x:1001:
280+ \\wheel:x:10:user
281+ \\
282+ ;
283+
284+ const file_system : * System.TestBackend.Description.FileSystemDescription = try .create (std .testing .allocator );
285+ defer file_system .destroy ();
286+
287+ const etc_dir = try file_system .root .addDirectory ("etc" );
288+ _ = try etc_dir .addFile ("passwd" , passwd_contents );
289+ _ = try etc_dir .addFile ("group" , group_contents );
290+
291+ var stdout : std .ArrayList (u8 ) = .init (std .testing .allocator );
292+ defer stdout .deinit ();
293+
294+ try command .testExecute (&.{}, .{
295+ .stdout = stdout .writer ().any (),
296+ .system_description = .{
297+ .file_system = file_system ,
298+ .user_group = .{
299+ .effective_user_id = 1001 ,
300+ },
301+ },
302+ });
303+
304+ try std .testing .expectEqualStrings ("sys user wheel\n " , stdout .items );
305+ }
216306};
217307
218308const Arg = @import ("../Arg.zig" );
219309const Command = @import ("../Command.zig" );
220310const IO = @import ("../IO.zig" );
221311const shared = @import ("../shared.zig" );
312+ const System = @import ("../system/System.zig" );
222313
223314const log = std .log .scoped (.groups );
224315
0 commit comments