passwd uses new FlatpakHostCommand

This commit is contained in:
Mitchell Hashimoto
2023-02-27 11:02:59 -08:00
parent f64d871847
commit 630374060d
2 changed files with 57 additions and 44 deletions

View File

@ -19,6 +19,10 @@ pub fn isFlatpak() bool {
/// This makes it easy for the command to behave synchronously similar to /// This makes it easy for the command to behave synchronously similar to
/// std.process.ChildProcess. /// std.process.ChildProcess.
/// ///
/// There are lots of chances for low-hanging improvements here (automatic
/// pipes, /dev/null, etc.) but this was purpose built for my needs so
/// it doesn't have all of those.
///
/// Requires GIO, GLib to be available and linked. /// Requires GIO, GLib to be available and linked.
pub const FlatpakHostCommand = struct { pub const FlatpakHostCommand = struct {
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
@ -78,16 +82,13 @@ pub const FlatpakHostCommand = struct {
}, },
}; };
/// Execute the command and wait for it to finish. This will automatically /// Errors that are possible from us.
/// read all the data from the provided stdout/stderr fds and return them pub const Error = error{
/// in the result. FlatpakMustBeStarted,
/// FlatpakSpawnFail,
/// This runs the exec in a dedicated thread with a dedicated GLib FlatpakSetupFail,
/// event loop so that it can run synchronously. FlatpakRPCFail,
pub fn exec(self: *FlatpakHostCommand, alloc: Allocator) !void { };
const thread = try std.Thread.spawn(.{}, threadMain, .{ self, alloc });
thread.join();
}
/// Spawn the command. This will start the host command. On return, /// Spawn the command. This will start the host command. On return,
/// the pid will be available. This must only be called with the /// the pid will be available. This must only be called with the
@ -105,7 +106,7 @@ pub const FlatpakHostCommand = struct {
return switch (self.state) { return switch (self.state) {
.init => unreachable, .init => unreachable,
.err => error.FlatpakSpawnFail, .err => Error.FlatpakSpawnFail,
.started => |v| v.pid, .started => |v| v.pid,
.exited => |v| v.pid, .exited => |v| v.pid,
}; };
@ -119,8 +120,8 @@ pub const FlatpakHostCommand = struct {
while (true) { while (true) {
switch (self.state) { switch (self.state) {
.init => return error.FlatpakCommandNotStarted, .init => return Error.FlatpakMustBeStarted,
.err => return error.FlatpakSpawnFail, .err => return Error.FlatpakSpawnFail,
.started => {}, .started => {},
.exited => |v| { .exited => |v| {
self.state = .{ .init = {} }; self.state = .{ .init = {} };
@ -187,15 +188,15 @@ pub const FlatpakHostCommand = struct {
defer c.g_object_unref(fd_list); defer c.g_object_unref(fd_list);
if (c.g_unix_fd_list_append(fd_list, self.stdin, &err) < 0) { if (c.g_unix_fd_list_append(fd_list, self.stdin, &err) < 0) {
log.warn("error adding fd: {s}", .{err.*.message}); log.warn("error adding fd: {s}", .{err.*.message});
return error.FlatpakFdFailed; return Error.FlatpakSetupFail;
} }
if (c.g_unix_fd_list_append(fd_list, self.stdout, &err) < 0) { if (c.g_unix_fd_list_append(fd_list, self.stdout, &err) < 0) {
log.warn("error adding fd: {s}", .{err.*.message}); log.warn("error adding fd: {s}", .{err.*.message});
return error.FlatpakFdFailed; return Error.FlatpakSetupFail;
} }
if (c.g_unix_fd_list_append(fd_list, self.stderr, &err) < 0) { if (c.g_unix_fd_list_append(fd_list, self.stderr, &err) < 0) {
log.warn("error adding fd: {s}", .{err.*.message}); log.warn("error adding fd: {s}", .{err.*.message});
return error.FlatpakFdFailed; return Error.FlatpakSetupFail;
} }
// Build our arguments for the file descriptors. // Build our arguments for the file descriptors.
@ -279,7 +280,7 @@ pub const FlatpakHostCommand = struct {
&err, &err,
) orelse { ) orelse {
log.warn("Flatpak.HostCommand failed: {s}", .{err.*.message}); log.warn("Flatpak.HostCommand failed: {s}", .{err.*.message});
return error.FlatpakHostCommandFailed; return Error.FlatpakRPCFail;
}; };
defer c.g_variant_unref(reply); defer c.g_variant_unref(reply);
@ -335,6 +336,7 @@ pub const FlatpakHostCommand = struct {
.status = std.math.cast(u8, exit_status) orelse 255, .status = std.math.cast(u8, exit_status) orelse 255,
}, },
}); });
log.debug("HostCommand exited pid={} status={}", .{ pid, exit_status });
// We're done now, so we can unsubscribe // We're done now, so we can unsubscribe
c.g_dbus_connection_signal_unsubscribe(bus.?, state.subscription); c.g_dbus_connection_signal_unsubscribe(bus.?, state.subscription);

View File

@ -49,13 +49,12 @@ pub fn get(alloc: Allocator) !Entry {
// If we're in flatpak then our entry is always empty so we grab it // If we're in flatpak then our entry is always empty so we grab it
// by shelling out to the host. note that we do HAVE an entry in the // by shelling out to the host. note that we do HAVE an entry in the
// sandbox but only the username is correct. // sandbox but only the username is correct.
//
// Note: we wrap our getent call in a /bin/sh login shell because
// some operating systems (NixOS tested) don't set the PATH for various
// utilities properly until we get a login shell.
if (internal_os.isFlatpak()) { if (internal_os.isFlatpak()) {
log.info("flatpak detected, will use host-spawn to get our entry", .{}); log.info("flatpak detected, will use host-spawn to get our entry", .{});
// Note: we wrap our getent call in a /bin/sh login shell because
// some operating systems (NixOS tested) don't set the PATH for various
// utilities properly until we get a login shell.
const Pty = @import("Pty.zig"); const Pty = @import("Pty.zig");
var pty = try Pty.open(.{}); var pty = try Pty.open(.{});
defer pty.deinit(); defer pty.deinit();
@ -76,30 +75,42 @@ pub fn get(alloc: Allocator) !Entry {
}; };
_ = try cmd.spawn(alloc); _ = try cmd.spawn(alloc);
_ = try cmd.wait(); _ = try cmd.wait();
if (true) @panic("END");
const exec = try std.ChildProcess.exec(.{ // Once started, we can close the child side. We do this after
.allocator = alloc, // wait right now but that is fine too. This lets us read the
.argv = &[_][]const u8{ // parent and detect EOF.
"/app/bin/host-spawn", _ = std.os.close(pty.slave);
"-pty",
"/bin/sh", // Read all of our output
"-l", const output = output: {
"-c", var output: std.ArrayListUnmanaged(u8) = .{};
try std.fmt.allocPrint( while (true) {
alloc, const n = std.os.read(pty.master, &buf) catch |err| {
"getent passwd {s}", switch (err) {
.{std.mem.sliceTo(pw.pw_name, 0)}, // EIO is triggered at the end since we closed our
), // child side. This is just EOF for this. I'm not sure
}, // if I'm doing this wrong.
}); error.InputOutput => break,
if (exec.term == .Exited) { else => return err,
// Shell and home are the last two entries }
var it = std.mem.splitBackwards(u8, std.mem.trimRight(u8, exec.stdout, " \r\n"), ":"); };
result.shell = it.next() orelse null;
result.home = it.next() orelse null; try output.appendSlice(alloc, buf[0..n]);
return result;
} // Max total size is buf.len. We can do better here by trimming
// the front and continuing reading but we choose to just exit.
if (output.items.len > buf.len) break;
}
break :output try output.toOwnedSlice(alloc);
};
log.warn("DONE output={s}", .{output});
// Shell and home are the last two entries
var it = std.mem.splitBackwards(u8, std.mem.trimRight(u8, output, " \r\n"), ":");
result.shell = it.next() orelse null;
result.home = it.next() orelse null;
return result;
} }
if (pw.pw_shell) |ptr| { if (pw.pw_shell) |ptr| {