Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 20 additions & 9 deletions src/Command.zig
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ execute: *const fn (
allocator: std.mem.Allocator,
io: IO,
args: *Arg.Iterator,
cwd: std.fs.Dir,
system: System,
exe_path: []const u8,
) Error!void,

Expand Down Expand Up @@ -190,7 +190,7 @@ pub const TestExecuteSettings = struct {
stdin: ?std.io.AnyReader = null,
stdout: ?std.io.AnyWriter = null,
stderr: ?std.io.AnyWriter = null,
cwd: ?std.fs.Dir = null,
system_description: System.TestBackend.Description = .{},
};

pub fn testExecute(
Expand All @@ -200,11 +200,15 @@ pub fn testExecute(
) ExposedError!void {
std.debug.assert(builtin.is_test);

const cwd_provided = settings.cwd != null;

var tmp_dir: std.testing.TmpDir = if (!cwd_provided) std.testing.tmpDir(.{}) else undefined;
defer if (!cwd_provided) tmp_dir.cleanup();
const cwd = if (settings.cwd) |c| c else tmp_dir.dir;
const system: System = .{
._backend = System.TestBackend.create(
std.testing.allocator,
settings.system_description,
) catch |err| {
std.debug.panic("unable to create system backend: {s}", .{@errorName(err)});
},
};
defer system._backend.destroy();

var arg_iter: Arg.Iterator = .{ .slice = .{ .slice = arguments } };

Expand All @@ -218,7 +222,7 @@ pub fn testExecute(
std.testing.allocator,
io,
&arg_iter,
cwd,
system,
command.name,
) catch |full_err| command.narrowError(io, command.name, full_err);
}
Expand Down Expand Up @@ -356,6 +360,8 @@ pub const TestFuzzOptions = struct {

/// If true the command is expected to output something to stderr on failure.
expect_stderr_output_on_failure: bool = true,

system_description: System.TestBackend.Description = .{},
};

pub fn testFuzz(command: Command, options: TestFuzzOptions) !void {
Expand Down Expand Up @@ -388,7 +394,11 @@ pub fn testFuzz(command: Command, options: TestFuzzOptions) !void {

context.inner_command.testExecute(
arguments,
.{ .stdout = stdout.writer().any(), .stderr = stderr.writer().any() },
.{
.stdout = stdout.writer().any(),
.stderr = stderr.writer().any(),
.system_description = context.options.system_description,
},
) catch |err| {
switch (err) {
error.OutOfMemory => {
Expand Down Expand Up @@ -491,6 +501,7 @@ pub const enabled_command_lookup: std.StaticStringMap(Command) = .initComptime(b
const Arg = @import("Arg.zig");
const IO = @import("IO.zig");
const shared = @import("shared.zig");
const System = @import("system/System.zig");

const builtin = @import("builtin");
const std = @import("std");
Expand Down
5 changes: 3 additions & 2 deletions src/commands/basename.zig
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,13 @@ const impl = struct {
allocator: std.mem.Allocator,
io: IO,
args: *Arg.Iterator,
cwd: std.fs.Dir,
system: System,
exe_path: []const u8,
) Command.Error!void {
const z: tracy.Zone = .begin(.{ .src = @src(), .name = command.name });
defer z.end();

_ = cwd;
_ = system;

const options = try parseArguments(allocator, io, args, exe_path);
log.debug("{}", .{options});
Expand Down Expand Up @@ -387,6 +387,7 @@ const Arg = @import("../Arg.zig");
const Command = @import("../Command.zig");
const IO = @import("../IO.zig");
const shared = @import("../shared.zig");
const System = @import("../system/System.zig");

const log = std.log.scoped(.basename);

Expand Down
5 changes: 3 additions & 2 deletions src/commands/clear.zig
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,13 @@ const impl = struct {
allocator: std.mem.Allocator,
io: IO,
args: *Arg.Iterator,
cwd: std.fs.Dir,
system: System,
exe_path: []const u8,
) Command.Error!void {
const z: tracy.Zone = .begin(.{ .src = @src(), .name = command.name });
defer z.end();

_ = cwd;
_ = system;

const options = try parseArguments(allocator, io, args, exe_path);
log.debug("{}", .{options});
Expand Down Expand Up @@ -194,6 +194,7 @@ const Arg = @import("../Arg.zig");
const Command = @import("../Command.zig");
const IO = @import("../IO.zig");
const shared = @import("../shared.zig");
const System = @import("../system/System.zig");

const log = std.log.scoped(.clear);

Expand Down
5 changes: 3 additions & 2 deletions src/commands/dirname.zig
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,13 @@ const impl = struct {
allocator: std.mem.Allocator,
io: IO,
args: *Arg.Iterator,
cwd: std.fs.Dir,
system: System,
exe_path: []const u8,
) Command.Error!void {
const z: tracy.Zone = .begin(.{ .src = @src(), .name = command.name });
defer z.end();

_ = cwd;
_ = system;

const options = try parseArguments(allocator, io, args, exe_path);
log.debug("{}", .{options});
Expand Down Expand Up @@ -251,6 +251,7 @@ const Arg = @import("../Arg.zig");
const Command = @import("../Command.zig");
const IO = @import("../IO.zig");
const shared = @import("../shared.zig");
const System = @import("../system/System.zig");

const log = std.log.scoped(.dirname);

Expand Down
5 changes: 3 additions & 2 deletions src/commands/false.zig
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,15 @@ const impl = struct {
allocator: std.mem.Allocator,
io: IO,
args: *Arg.Iterator,
cwd: std.fs.Dir,
system: System,
exe_path: []const u8,
) Command.Error!void {
const z: tracy.Zone = .begin(.{ .src = @src(), .name = command.name });
defer z.end();

_ = io;
_ = exe_path;
_ = cwd;
_ = system;
_ = allocator;

_ = try args.nextWithHelpOrVersion(true);
Expand Down Expand Up @@ -84,6 +84,7 @@ const Arg = @import("../Arg.zig");
const Command = @import("../Command.zig");
const IO = @import("../IO.zig");
const shared = @import("../shared.zig");
const System = @import("../system/System.zig");

const std = @import("std");
const tracy = @import("tracy");
123 changes: 107 additions & 16 deletions src/commands/groups.zig
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const impl = struct {
allocator: std.mem.Allocator,
io: IO,
args: *Arg.Iterator,
cwd: std.fs.Dir,
system: System,
exe_path: []const u8,
) Command.Error!void {
const z: tracy.Zone = .begin(.{ .src = @src(), .name = command.name });
Expand All @@ -46,25 +46,50 @@ const impl = struct {

const opt_arg = try args.nextWithHelpOrVersion(true);

const passwd_file = try shared.mapFile(command, allocator, io, cwd, "/etc/passwd");
defer passwd_file.close();
const mapped_passwd_file = blk: {
const passwd_file = system.cwd().openFile("/etc/passwd", .{}) catch |err|
return command.printErrorAlloc(
allocator,
io,
"unable to open '/etc/passwd': {s}",
.{@errorName(err)},
);
errdefer if (shared.free_on_close) passwd_file.close();

const stat = passwd_file.stat() catch |err|
return command.printErrorAlloc(
allocator,
io,
"unable to stat '/etc/passwd': {s}",
.{@errorName(err)},
);

break :blk passwd_file.mapReadonly(stat.size) catch |err|
return command.printErrorAlloc(
allocator,
io,
"unable to map '/etc/passwd': {s}",
.{@errorName(err)},
);
};
defer if (shared.free_on_close) mapped_passwd_file.close();

return if (opt_arg) |arg|
namedUser(allocator, io, arg.raw, passwd_file.file_contents, cwd)
namedUser(allocator, io, arg.raw, mapped_passwd_file.file_contents, system)
else
currentUser(allocator, io, passwd_file.file_contents, cwd);
currentUser(allocator, io, mapped_passwd_file.file_contents, system);
}

fn currentUser(
allocator: std.mem.Allocator,
io: IO,
passwd_file_contents: []const u8,
cwd: std.fs.Dir,
system: System,
) Command.Error!void {
const z: tracy.Zone = .begin(.{ .src = @src(), .name = "current user" });
defer z.end();

const euid = std.os.linux.geteuid();
const euid = system.getEffectiveUserId();

log.debug("currentUser called, euid: {}", .{euid});

Expand Down Expand Up @@ -98,7 +123,7 @@ const impl = struct {
"format of '/etc/passwd' is invalid",
);

return printGroups(allocator, entry.user_name, primary_group_id, io, cwd);
return printGroups(allocator, entry.user_name, primary_group_id, io, system);
}

return command.printError(
Expand All @@ -112,7 +137,7 @@ const impl = struct {
io: IO,
user: []const u8,
passwd_file_contents: []const u8,
cwd: std.fs.Dir,
system: System,
) Command.Error!void {
const z: tracy.Zone = .begin(.{ .src = @src(), .name = "namedUser" });
defer z.end();
Expand Down Expand Up @@ -140,7 +165,7 @@ const impl = struct {
"format of '/etc/passwd' is invalid",
);

return printGroups(allocator, entry.user_name, primary_group_id, io, cwd);
return printGroups(allocator, entry.user_name, primary_group_id, io, system);
}

return command.printErrorAlloc(allocator, io, "unknown user '{s}'", .{user});
Expand All @@ -151,7 +176,7 @@ const impl = struct {
user: []const u8,
primary_group_id: std.posix.uid_t,
io: IO,
cwd: std.fs.Dir,
system: System,
) !void {
const z: tracy.Zone = .begin(.{ .src = @src(), .name = "print groups" });
defer z.end();
Expand All @@ -162,10 +187,35 @@ const impl = struct {
.{ user, primary_group_id },
);

const group_file = try shared.mapFile(command, allocator, io, cwd, "/etc/group");
defer group_file.close();
const mapped_group_file = blk: {
const group_file = system.cwd().openFile("/etc/group", .{}) catch |err|
return command.printErrorAlloc(
allocator,
io,
"unable to open '/etc/group': {s}",
.{@errorName(err)},
);
errdefer if (shared.free_on_close) group_file.close();

const stat = group_file.stat() catch |err|
return command.printErrorAlloc(
allocator,
io,
"unable to stat '/etc/group': {s}",
.{@errorName(err)},
);

var group_file_iter = shared.groupFileIterator(group_file.file_contents);
break :blk group_file.mapReadonly(stat.size) catch |err|
return command.printErrorAlloc(
allocator,
io,
"unable to map '/etc/group': {s}",
.{@errorName(err)},
);
};
defer if (shared.free_on_close) mapped_group_file.close();

var group_file_iter = shared.groupFileIterator(mapped_group_file.file_contents);

var first = true;

Expand Down Expand Up @@ -211,14 +261,55 @@ const impl = struct {
try command.testVersion();
}

// TODO: How do we test this without introducing the amount of complexity that https://github.com/leecannon/zsw does?
// https://github.com/leecannon/zig-coreutils/issues/7
test "groups" {
const passwd_contents =
\\root:x:0:0::/root:/usr/bin/bash
\\daemon:x:1:1::/:/usr/sbin/nologin
\\bin:x:2:2::/:/usr/sbin/nologin
\\sys:x:3:3::/:/usr/sbin/nologin
\\user:x:1001:1001:A User:/home/user:/usr/bin/zsh
\\
;

const group_contents =
\\root:x:0:
\\daemon:x:1:
\\bin:x:2:
\\sys:x:3:user
\\user:x:1001:
\\wheel:x:10:user
\\
;

const file_system: *System.TestBackend.Description.FileSystemDescription = try .create(std.testing.allocator);
defer file_system.destroy();

const etc_dir = try file_system.root.addDirectory("etc");
_ = try etc_dir.addFile("passwd", passwd_contents);
_ = try etc_dir.addFile("group", group_contents);

var stdout: std.ArrayList(u8) = .init(std.testing.allocator);
defer stdout.deinit();

try command.testExecute(&.{}, .{
.stdout = stdout.writer().any(),
.system_description = .{
.file_system = file_system,
.user_group = .{
.effective_user_id = 1001,
},
},
});

try std.testing.expectEqualStrings("sys user wheel\n", stdout.items);
}
};

const Arg = @import("../Arg.zig");
const Command = @import("../Command.zig");
const IO = @import("../IO.zig");
const shared = @import("../shared.zig");
const System = @import("../system/System.zig");

const log = std.log.scoped(.groups);

Expand Down
Loading