diff --git a/include/aisap.h b/include/aisap.h index 21a84a6..6eba60b 100644 --- a/include/aisap.h +++ b/include/aisap.h @@ -70,19 +70,6 @@ typedef uint8_t aisap_error; extern "C" { #endif -// Go-implemented C functions. These are on the way out in favor of the Zig -// implementation. The Go version WILL stay and continue being maintained for -// other Go projects, it just won't have C bindings. This is because Go comes -// with a rather large runtime that would bloat C programs trying to use it, -// along with having some CGo quirks that are annoying to get around that just -// work in Zig. -// TODO: Make char get passed correctly. This may just be easiest to just -// make another AppImage run function that accepts char** instead of Go strings -//extern void aisap_appimage_init_go(aisap_appimage* ai, const char* path, aisap_error* err); -//extern void aisap_appimage_destroy_go(aisap_appimage* ai); -//extern int aisap_appimage_run(aisap_appimage* ai, char** args); -//extern int aisap_appimage_ismounted(aisap_appimage* ai); - // Zig-implemented C functions // `aisap_appimage_new` initializes both the Zig and Go AppImage structs, so // until I can get the rest of the functions ported over you'll still be able @@ -100,13 +87,13 @@ extern size_t aisap_appimage_offset(aisap_appimage* ai, aisap_error* extern const char* aisap_appimage_md5(aisap_appimage* ai, char* buf, size_t buf_len, aisap_error* err); extern void aisap_appimage_mount(aisap_appimage* ai, char* path, aisap_error* err); extern const char* aisap_appimage_mount_dir(aisap_appimage* ai); +extern void aisap_appimage_sandbox(aisap_appimage* ai, int argc, char** args, aisap_error* err); +extern char** aisap_appimage_wrapargs(aisap_appimage* ai, aisap_error* err); // THESE FUNCTIONS NOT YET IMPLEMENTED -//extern uint8_t aisap_appimage_sandbox(aisap_appimage* ai, int argc, char** args); //extern char* aisap_appimage_mountdir(aisap_appimage* ai); //extern char* aisap_appimage_tempdir(aisap_appimage* ai); //extern char* aisap_appimage_runid(aisap_appimage* ai); -extern char** aisap_appimage_wrapargs(aisap_appimage* ai, aisap_error* err); // For ABI compat with libAppImage extern off_t appimage_get_payload_offset(const char* path); diff --git a/zig/build_test.sh b/zig/examples/build_test.sh similarity index 74% rename from zig/build_test.sh rename to zig/examples/build_test.sh index ebc8bbe..90251cd 100755 --- a/zig/build_test.sh +++ b/zig/examples/build_test.sh @@ -4,9 +4,11 @@ # which will generate the `test` executable, which prints debug information # about an AppImage +# TODO: also build `test.c` in `build.zig` + $CC -static \ -o test test.c \ - ../libaisap-x86_64.a \ - -I../include + ../../libaisap-x86_64.a \ + -I../../include # -lfuse3 \ diff --git a/zig/examples/src/main.zig b/zig/examples/src/main.zig index 8fec6a4..ca675fd 100644 --- a/zig/examples/src/main.zig +++ b/zig/examples/src/main.zig @@ -42,7 +42,7 @@ fn printSockets(slice: []AppImage.SocketPermissions) void { } pub fn main() !void { - var allocator = std.heap.c_allocator; + var allocator = std.heap.page_allocator; var args = std.process.args(); // Skip arg[0] @@ -96,10 +96,11 @@ pub fn main() !void { try ai.mount(.{}); const wrapArgs = try ai.wrapArgs(allocator); - - try aisap.bwrap(allocator, wrapArgs); - printWrapArgs(wrapArgs); + + try ai.sandbox(.{ + //.args = &[_][]const u8{"build"}, + }); } fn printWrapArgs(args: []const []const u8) void { diff --git a/zig/test.c b/zig/examples/test.c similarity index 85% rename from zig/test.c rename to zig/examples/test.c index 451ffd0..cf20622 100644 --- a/zig/test.c +++ b/zig/examples/test.c @@ -53,10 +53,19 @@ int main(int argc, char** argv) { return err; } -// printf(" wrapargs: %s\n", wrap_args[0]); - + printf(" wrapargs:\n"); + char** i = wrap_args; + for (char* str = *i; str; str = *++i) { + printf("%s ", str); + } printf("\n"); + aisap_appimage_sandbox(&ai, argc - 2, argv + 2, &err); + if (err) { + printf("%d\n", err); + return err; + } + // Test libappimage API: printf("libappimage API:\n"); printf(" offset: %ld\n", appimage_get_payload_offset(ai.path)); diff --git a/zig/lib/appimage.zig b/zig/lib/appimage.zig index 0fe4716..45d192b 100644 --- a/zig/lib/appimage.zig +++ b/zig/lib/appimage.zig @@ -524,9 +524,11 @@ pub const AppImage = struct { return perms; } - const WrapArgsError = error{ + pub const WrapArgsError = error{ HomeNotFound, BundleNotMounted, + NoPermissions, + SandboxLevelTooLow, }; // TODO: finish implementing in Zig @@ -536,363 +538,363 @@ pub const AppImage = struct { return WrapArgsError.BundleNotMounted; } - var list = std.ArrayList([]const u8).init(allocator); + var perms = try ai.permissions(allocator) orelse { + return WrapArgsError.NoPermissions; + }; + defer perms.deinit(); - var perms = try ai.permissions(allocator); + if (perms.level == 0) return WrapArgsError.SandboxLevelTooLow; var home = try known_folders.getPath(allocator, .home) orelse { return WrapArgsError.HomeNotFound; }; - if (perms) |*prms| { - defer prms.deinit(); + var list = std.ArrayList([]const u8).init(allocator); + try list.append("bwrap"); - if (prms.level > 0) { - var md5_buf: [33]u8 = undefined; - const ai_md5 = try ai.md5(&md5_buf); + if (perms.level > 0) { + var md5_buf: [33]u8 = undefined; + const ai_md5 = try ai.md5(&md5_buf); - // TODO: fallback if `LOGNAME` not present - const logname = os.getenv("LOGNAME") orelse ""; - const user = try std.process.getUserInfo(logname); + // TODO: fallback if `LOGNAME` not present + const logname = os.getenv("LOGNAME") orelse ""; + const user = try std.process.getUserInfo(logname); - // Bwrap args for AppImages regardless of level - try list.appendSlice(&[_][]const u8{ - "--setenv", "TMPDIR", "/tmp", - "--setenv", "HOME", home, - "--setenv", "ARGV0", fs.path.basename(ai.path), - - // If these directories are symlinks, they will be resolved, - // otherwise - "--ro-bind-try", try resolve(allocator, "/opt"), "/opt", - "--ro-bind-try", try resolve(allocator, "/bin"), "/bin", - "--ro-bind-try", try resolve(allocator, "/sbin"), "/sbin", - "--ro-bind-try", try resolve(allocator, "/lib"), "/lib", - "--ro-bind-try", try resolve(allocator, "/lib32"), "/lib32", - "--ro-bind-try", try resolve(allocator, "/lib64"), "/lib64", - "--ro-bind-try", try resolve(allocator, "/usr/bin"), "/usr/bin", - "--ro-bind-try", try resolve(allocator, "/usr/sbin"), "/usr/sbin", - "--ro-bind-try", try resolve(allocator, "/usr/lib"), "/usr/lib", - "--ro-bind-try", try resolve(allocator, "/usr/lib32"), "/usr/lib32", - "--ro-bind-try", try resolve(allocator, "/usr/lib64"), "/usr/lib64", - - "--setenv", "APPDIR", + // Bwrap args for AppImages regardless of level + try list.appendSlice(&[_][]const u8{ + "--setenv", "TMPDIR", "/tmp", + "--setenv", "HOME", home, + "--setenv", "ARGV0", fs.path.basename(ai.path), + + // If these directories are symlinks, they will be resolved, + // otherwise + "--ro-bind-try", try resolve(allocator, "/opt"), "/opt", + "--ro-bind-try", try resolve(allocator, "/bin"), "/bin", + "--ro-bind-try", try resolve(allocator, "/sbin"), "/sbin", + "--ro-bind-try", try resolve(allocator, "/lib"), "/lib", + "--ro-bind-try", try resolve(allocator, "/lib32"), "/lib32", + "--ro-bind-try", try resolve(allocator, "/lib64"), "/lib64", + "--ro-bind-try", try resolve(allocator, "/usr/bin"), "/usr/bin", + "--ro-bind-try", try resolve(allocator, "/usr/sbin"), "/usr/sbin", + "--ro-bind-try", try resolve(allocator, "/usr/lib"), "/usr/lib", + "--ro-bind-try", try resolve(allocator, "/usr/lib32"), "/usr/lib32", + "--ro-bind-try", try resolve(allocator, "/usr/lib64"), "/usr/lib64", + + "--setenv", "APPDIR", + try std.fmt.allocPrint( + allocator, + "/tmp/.mount_{s}", + .{ai_md5}, + ), + + "--setenv", "APPIMAGE", + try std.fmt.allocPrint( + allocator, + "/app/{s}", + .{fs.path.basename(ai.path)}, + ), + + // Set generic paths for all XDG standard dirs + "--setenv", "XDG_DESKTOP_DIR", + try std.fmt.allocPrint( + allocator, + "{s}/Desktop", + .{home}, + ), + "--setenv", "XDG_DOWNLOAD_DIR", + try std.fmt.allocPrint( + allocator, + "{s}/Downloads", + .{home}, + ), + "--setenv", "XDG_DOCUMENTS_DIR", + try std.fmt.allocPrint( + allocator, + "{s}/Documents", + .{home}, + ), + "--setenv", "XDG_MUSIC_DIR", + try std.fmt.allocPrint( + allocator, + "{s}/Music", + .{home}, + ), + "--setenv", "XDG_PICTURES_DIR", + try std.fmt.allocPrint( + allocator, + "{s}/Pictures", + .{home}, + ), + "--setenv", "XDG_VIDEOS_DIR", + try std.fmt.allocPrint( + allocator, + "{s}/Videos", + .{home}, + ), + "--setenv", "XDG_TEMPLATES_DIR", + try std.fmt.allocPrint( + allocator, + "{s}/Templates", + .{home}, + ), + "--setenv", "XDG_PUBLICSHARE_DIR", + try std.fmt.allocPrint( + allocator, + "{s}/Share", + .{home}, + ), + "--setenv", "XDG_DATA_HOME", + try std.fmt.allocPrint( + allocator, + "{s}/.local/share", + .{home}, + ), + "--setenv", "XDG_CONFIG_HOME", + try std.fmt.allocPrint( + allocator, + "{s}/.config", + .{home}, + ), + "--setenv", "XDG_CACHE_HOME", + try std.fmt.allocPrint( + allocator, + "{s}/.cache", + .{home}, + ), + "--setenv", "XDG_STATE_HOME", + try std.fmt.allocPrint( + allocator, + "{s}/.local/state", + .{home}, + ), + "--setenv", "XDG_RUNTIME_DIR", + try std.fmt.allocPrint( + allocator, + "/run/user/{d}", + .{user.uid}, + ), + "--perms", "0700", // + "--dir", + try std.fmt.allocPrint( + allocator, + "/run/user/{d}", + .{user.uid}, + ), + "--dev", "/dev", + "--proc", "/proc", + "--dir", "/app", + + "--bind", ai.path, + try std.fmt.allocPrint( + allocator, + "/app/{s}", + .{fs.path.basename(ai.path)}, + ), + + // TODO: fallback if no cache + "--bind-try", + try std.fmt.allocPrint( + allocator, + "{s}/appimage/{s}", + .{ + (try known_folders.getPath(allocator, .cache)).?, + ai_md5, + }, + ), + try std.fmt.allocPrint( + allocator, + "{s}/.cache", + .{home}, + ), + + "--die-with-parent", + }); + + const config = (try known_folders.getPath(allocator, .local_configuration)).?; + + // Per-level arguments + try list.appendSlice(switch (perms.level) { + 0 => unreachable, + 1 => &[_][]const u8{ + "--dev-bind", "/dev", "/dev", + "--ro-bind", "/sys", "/sys", + "--ro-bind-try", "/usr", "/usr", + "--ro-bind-try", "/etc", "/etc", + "--ro-bind-try", "/run/systemd", "/run/systemd", + // TODO: do this a better way + "--ro-bind-try", try std.fmt.allocPrint( allocator, - "/tmp/.mount_{s}", - .{ai_md5}, + "{s}/.fonts", + .{home}, ), - - "--setenv", "APPIMAGE", try std.fmt.allocPrint( allocator, - "/app/{s}", - .{fs.path.basename(ai.path)}, + "{s}/.fonts", + .{home}, ), - - // Set generic paths for all XDG standard dirs - "--setenv", "XDG_DESKTOP_DIR", + "--ro-bind-try", try std.fmt.allocPrint( allocator, - "{s}/Desktop", - .{home}, + "{s}/fontconfig", + .{config}, ), - "--setenv", "XDG_DOWNLOAD_DIR", try std.fmt.allocPrint( allocator, - "{s}/Downloads", - .{home}, + "{s}/fontconfig", + .{config}, ), - "--setenv", "XDG_DOCUMENTS_DIR", + "--ro-bind-try", try std.fmt.allocPrint( allocator, - "{s}/Documents", - .{home}, + "{s}/gtk-3.0", + .{config}, ), - "--setenv", "XDG_MUSIC_DIR", try std.fmt.allocPrint( allocator, - "{s}/Music", - .{home}, + "{s}/gtk-3.0", + .{config}, ), - "--setenv", "XDG_PICTURES_DIR", + "--ro-bind-try", try std.fmt.allocPrint( allocator, - "{s}/Pictures", - .{home}, + "{s}/kdeglobals", + .{config}, ), - "--setenv", "XDG_VIDEOS_DIR", try std.fmt.allocPrint( allocator, - "{s}/Videos", - .{home}, + "{s}/kdeglobals", + .{config}, ), - "--setenv", "XDG_TEMPLATES_DIR", + "--ro-bind-try", try std.fmt.allocPrint( allocator, - "{s}/Templates", - .{home}, + "{s}/lxde/lxde.conf", + .{config}, ), - "--setenv", "XDG_PUBLICSHARE_DIR", try std.fmt.allocPrint( allocator, - "{s}/Share", - .{home}, + "{s}/lxde/lxde.conf", + .{config}, ), - "--setenv", "XDG_DATA_HOME", + }, + 2 => &[_][]const u8{ + "--ro-bind-try", "/etc/fonts", "/etc/fonts", + "--ro-bind-try", "/etc/ld.so.cache", "/etc/ld.so.cache", + "--ro-bind-try", "/etc/mime.types", "/etc/mime.types", + "--ro-bind-try", "/usr/share/fontconfig", "/usr/share/fontconfig", + "--ro-bind-try", "/usr/share/fonts", "/usr/share/fonts", + "--ro-bind-try", "/usr/share/icons", "/usr/share/icons", + "--ro-bind-try", "/usr/share/applications", "/usr/share/applications", + "--ro-bind-try", "/usr/share/mime", "/usr/share/mime", + "--ro-bind-try", "/usr/share/libdrm", "/usr/share/libdrm", + "--ro-bind-try", "/usr/share/glvnd", "/usr/share/glvnd", + "--ro-bind-try", "/usr/share/glib-2.0", "/usr/share/glib-2.0", + "--ro-bind-try", "/usr/share/terminfo", "/usr/share/terminfo", + "--ro-bind-try", try std.fmt.allocPrint( allocator, - "{s}/.local/share", + "{s}/.fonts", .{home}, ), - "--setenv", "XDG_CONFIG_HOME", try std.fmt.allocPrint( allocator, - "{s}/.config", + "{s}/.fonts", .{home}, ), - "--setenv", "XDG_CACHE_HOME", + "--ro-bind-try", try std.fmt.allocPrint( allocator, - "{s}/.cache", - .{home}, + "{s}/fontconfig", + .{config}, ), - "--setenv", "XDG_STATE_HOME", try std.fmt.allocPrint( allocator, - "{s}/.local/state", - .{home}, + "{s}/fontconfig", + .{config}, ), - "--setenv", "XDG_RUNTIME_DIR", + "--ro-bind-try", try std.fmt.allocPrint( allocator, - "/run/user/{d}", - .{user.uid}, + "{s}/gtk-3.0", + .{config}, ), - "--perms", "0700", // - "--dir", try std.fmt.allocPrint( allocator, - "/run/user/{d}", - .{user.uid}, + "{s}/gtk-3.0", + .{config}, ), - "--dev", "/dev", - "--proc", "/proc", - "--dir", "/app", - - "--bind", ai.path, + "--ro-bind-try", try std.fmt.allocPrint( allocator, - "/app/{s}", - .{fs.path.basename(ai.path)}, + "{s}/kdeglobals", + .{config}, ), - - // TODO: fallback if no cache - "--bind-try", try std.fmt.allocPrint( allocator, - "{s}/appimage/{s}", - .{ - (try known_folders.getPath(allocator, .cache)).?, - ai_md5, - }, + "{s}/kdeglobals", + .{config}, ), + "--ro-bind-try", try std.fmt.allocPrint( allocator, - "{s}/.cache", - .{home}, + "{s}/lxde/lxde.conf", + .{config}, ), + try std.fmt.allocPrint( + allocator, + "{s}/lxde/lxde.conf", + .{config}, + ), + }, + 3 => &[_][]const u8{}, + }); - "--die-with-parent", - }); - - const config = (try known_folders.getPath(allocator, .local_configuration)).?; - - // Per-level arguments - try list.appendSlice(switch (prms.level) { - 0 => unreachable, - 1 => &[_][]const u8{ - "--dev-bind", "/dev", "/dev", - "--ro-bind", "/sys", "/sys", - "--ro-bind-try", "/usr", "/usr", - "--ro-bind-try", "/etc", "/etc", - "--ro-bind-try", "/run/systemd", "/run/systemd", - // TODO: do this a better way - "--ro-bind-try", - try std.fmt.allocPrint( - allocator, - "{s}/.fonts", - .{home}, - ), - try std.fmt.allocPrint( - allocator, - "{s}/.fonts", - .{home}, - ), - "--ro-bind-try", - try std.fmt.allocPrint( - allocator, - "{s}/fontconfig", - .{config}, - ), - try std.fmt.allocPrint( - allocator, - "{s}/fontconfig", - .{config}, - ), - "--ro-bind-try", - try std.fmt.allocPrint( - allocator, - "{s}/gtk-3.0", - .{config}, - ), - try std.fmt.allocPrint( - allocator, - "{s}/gtk-3.0", - .{config}, - ), - "--ro-bind-try", - try std.fmt.allocPrint( - allocator, - "{s}/kdeglobals", - .{config}, - ), - try std.fmt.allocPrint( - allocator, - "{s}/kdeglobals", - .{config}, - ), - "--ro-bind-try", - try std.fmt.allocPrint( - allocator, - "{s}/lxde/lxde.conf", - .{config}, - ), - try std.fmt.allocPrint( - allocator, - "{s}/lxde/lxde.conf", - .{config}, - ), - }, - 2 => &[_][]const u8{ - "--ro-bind-try", "/etc/fonts", "/etc/fonts", - "--ro-bind-try", "/etc/ld.so.cache", "/etc/ld.so.cache", - "--ro-bind-try", "/etc/mime.types", "/etc/mime.types", - "--ro-bind-try", "/usr/share/fontconfig", "/usr/share/fontconfig", - "--ro-bind-try", "/usr/share/fonts", "/usr/share/fonts", - "--ro-bind-try", "/usr/share/icons", "/usr/share/icons", - "--ro-bind-try", "/usr/share/applications", "/usr/share/applications", - "--ro-bind-try", "/usr/share/mime", "/usr/share/mime", - "--ro-bind-try", "/usr/share/libdrm", "/usr/share/libdrm", - "--ro-bind-try", "/usr/share/glvnd", "/usr/share/glvnd", - "--ro-bind-try", "/usr/share/glib-2.0", "/usr/share/glib-2.0", - "--ro-bind-try", "/usr/share/terminfo", "/usr/share/terminfo", - "--ro-bind-try", - try std.fmt.allocPrint( - allocator, - "{s}/.fonts", - .{home}, - ), - try std.fmt.allocPrint( - allocator, - "{s}/.fonts", - .{home}, - ), - "--ro-bind-try", - try std.fmt.allocPrint( - allocator, - "{s}/fontconfig", - .{config}, - ), - try std.fmt.allocPrint( - allocator, - "{s}/fontconfig", - .{config}, - ), - "--ro-bind-try", - try std.fmt.allocPrint( - allocator, - "{s}/gtk-3.0", - .{config}, - ), - try std.fmt.allocPrint( - allocator, - "{s}/gtk-3.0", - .{config}, - ), - "--ro-bind-try", - try std.fmt.allocPrint( - allocator, - "{s}/kdeglobals", - .{config}, - ), - try std.fmt.allocPrint( - allocator, - "{s}/kdeglobals", - .{config}, - ), - "--ro-bind-try", - try std.fmt.allocPrint( - allocator, - "{s}/lxde/lxde.conf", - .{config}, - ), - try std.fmt.allocPrint( - allocator, - "{s}/lxde/lxde.conf", - .{config}, - ), - }, - 3 => &[_][]const u8{}, + if (perms.data_dir) { + // try list.appendSlice( + // "--bind", + // ai. + // ); + } else { + try list.appendSlice(&[_][]const u8{ + "--tmpfs", + home, }); + } - if (prms.data_dir) { - // try list.appendSlice( - // "--bind", - // ai. - // ); - } else { - try list.appendSlice(&[_][]const u8{ - "--tmpfs", - home, - }); - } - - if (prms.filesystem) |files| { - for (files) |*file| { - try list.appendSlice( - try file.toBwrapArgs(allocator), - ); - } - } - - if (ai.mount_dir) |mount_dir| { - try list.appendSlice(&[_][]const u8{ - "--bind", - mount_dir, - try std.fmt.allocPrint( - allocator, - "/tmp/.mount_{s}", - .{ - ai_md5, - }, - ), - }); + if (perms.filesystem) |files| { + for (files) |*file| { + try list.appendSlice( + try file.toBwrapArgs(allocator), + ); } + } + if (ai.mount_dir) |mount_dir| { try list.appendSlice(&[_][]const u8{ - "--", + "--bind", + mount_dir, try std.fmt.allocPrint( allocator, - "/tmp/.mount_{s}/AppRun", - .{ai_md5}, + "/tmp/.mount_{s}", + .{ + ai_md5, + }, ), }); } - return list.toOwnedSlice(); + try list.appendSlice(&[_][]const u8{ + "--", + try std.fmt.allocPrint( + allocator, + "/tmp/.mount_{s}/AppRun", + .{ai_md5}, + ), + }); } - return &[_][]const u8{}; + return list.toOwnedSlice(); } /// Returns a `char**` for use in C @@ -987,7 +989,8 @@ pub const AppImage = struct { var in_stream = buf_reader.reader(); // Wait until we see the directory as a mountpoint - // This will need to be redone, but it works for the time being + // This will need to be redone for the sake of not being a hacky + // block of shit but it works for the time being while (waited < wait_max) { time.sleep(wait_step); @@ -1030,16 +1033,30 @@ pub const AppImage = struct { } } + pub const SandboxOptions = struct { + args: ?[]const []const u8 = null, + }; + // This can't be finished until AppImage.wrapArgs works correctly - // pub fn sandbox(ai: *AppImage, allocator: *std.mem.Allocator) !void { - // const cmd = [_][]const u8 { - // "--ro-bind", "/", "/", - // "sh", - // }; - // - // _ = ai; - // _ = try bwrap(allocator, &cmd); - // } + pub fn sandbox(ai: *AppImage, opts: SandboxOptions) !void { + var arena = std.heap.ArenaAllocator.init(ai.allocator); + var arena_allocator = arena.allocator(); + defer arena.deinit(); + + const wrap_args = try ai.wrapArgs(arena_allocator); + + if (opts.args) |args| { + var list = std.ArrayList([]const u8).init(arena_allocator); + defer list.deinit(); + + try list.appendSlice(wrap_args); + try list.appendSlice(args); + + try bwrap(arena_allocator, list.items); + } else { + try bwrap(arena_allocator, wrap_args); + } + } }; // If path is a symlink, return its target. Otherwise, just return the path @@ -1159,23 +1176,16 @@ pub fn kindFromHeaderData(header_data: []const u8) !AppImage.Kind { } // TODO: eventually build bwrap as a library +// This shouldn't typically be called on its own. Instead, `AppImage.sandbox` +// should be preferred pub fn bwrap(allocator: std.mem.Allocator, args: []const []const u8) !void { - var result = try allocator.alloc([]const u8, args.len + 1); - defer allocator.free(result); - - result[0] = "bwrap"; - - // Set ARGV0 then iterate through the slice and convert it to a C char** - for (args, 1..) |arg, idx| { - result[idx] = arg; - } - - var exec_result = try ChildProcess.exec(.{ - .allocator = allocator, - .argv = result, - }); + var stdout = std.io.getStdOut(); + var stderr = std.io.getStdErr(); - std.debug.print("{s}\n", .{exec_result.stdout}); + var child = ChildProcess.init(args, allocator); + child.stdout = stdout; + child.stderr = stderr; + _ = try child.spawnAndWait(); } // TODO: get bundle's supported architecture list diff --git a/zig/lib/c_api.zig b/zig/lib/c_api.zig index 0f09b96..93aafd5 100644 --- a/zig/lib/c_api.zig +++ b/zig/lib/c_api.zig @@ -29,11 +29,19 @@ export fn aisap_appimage_new(path: [*:0]const u8, err: *CAppImageError) c.aisap_ return aisap_appimage_newn(path, path_len, err); } -export fn aisap_appimage_newn(path: [*]const u8, path_len: usize, err: *CAppImageError) c.aisap_appimage { +export fn aisap_appimage_newn( + path: [*]const u8, + path_len: usize, + err: *CAppImageError, +) c.aisap_appimage { var allocator = std.heap.c_allocator; var ai: c.aisap_appimage = undefined; - var zig_ai = allocator.create(AppImage) catch unreachable; + var zig_ai = allocator.create(AppImage) catch { + err.* = .err; + return ai; + }; + zig_ai.* = AppImage.init(allocator, path[0..path_len]) catch { err.* = .err; return ai; @@ -50,9 +58,9 @@ export fn aisap_appimage_newn(path: [*]const u8, path_len: usize, err: *CAppImag } export fn aisap_appimage_mount_dir(ai: *c.aisap_appimage) ?[*:0]const u8 { - var zai = getParent(ai); + var zig_ai = getParent(ai); - if (zai.mount_dir) |mount_dir| { + if (zig_ai.mount_dir) |mount_dir| { return mount_dir; } @@ -60,14 +68,23 @@ export fn aisap_appimage_mount_dir(ai: *c.aisap_appimage) ?[*:0]const u8 { } // TODO: error handling -export fn aisap_appimage_mount(ai: *c.aisap_appimage, path: ?[*:0]const u8, err: *CAppImageError) void { +export fn aisap_appimage_mount( + ai: *c.aisap_appimage, + path: ?[*:0]const u8, + err: *CAppImageError, +) void { const path_len = if (path) |p| std.mem.len(p) else 0; aisap_appimage_mountn(ai, path, path_len, err); } // TODO: error handling -export fn aisap_appimage_mountn(ai: *c.aisap_appimage, path: ?[*]const u8, path_len: usize, err: *CAppImageError) void { +export fn aisap_appimage_mountn( + ai: *c.aisap_appimage, + path: ?[*]const u8, + path_len: usize, + err: *CAppImageError, +) void { _ = err; if (path) |p| { @@ -87,8 +104,16 @@ export fn aisap_appimage_destroy(ai: *c.aisap_appimage) void { getParent(ai).deinit(); } -export fn aisap_appimage_md5(ai: *c.aisap_appimage, buf: [*]u8, buf_len: usize, errno: *CAppImageError) [*:0]const u8 { - return (aisap.md5FromPath(ai.path[0..ai.path_len], buf[0..buf_len]) catch |err| { +export fn aisap_appimage_md5( + ai: *c.aisap_appimage, + buf: [*]u8, + buf_len: usize, + errno: *CAppImageError, +) [*:0]const u8 { + return (aisap.md5FromPath( + ai.path[0..ai.path_len], + buf[0..buf_len], + ) catch |err| { errno.* = switch (err) { // This should be the only error ever given from this function aisap.AppImageError.NoSpaceLeft => .no_space_left, @@ -105,8 +130,13 @@ export fn aisap_appimage_md5(ai: *c.aisap_appimage, buf: [*]u8, buf_len: usize, //} // +// This function typically shouldn't be called on its own. Instead, +// `aisap_appimage_sandbox` should be preferred // Returned memory must be freed -export fn aisap_appimage_wrapargs(ai: *c.aisap_appimage, err: *CAppImageError) [*:null]?[*:0]const u8 { +export fn aisap_appimage_wrapargs( + ai: *c.aisap_appimage, + err: *CAppImageError, +) [*:null]?[*:0]const u8 { return getParent(ai).wrapArgsZ(std.heap.c_allocator) catch { err.* = .err; return undefined; @@ -114,49 +144,50 @@ export fn aisap_appimage_wrapargs(ai: *c.aisap_appimage, err: *CAppImageError) [ } // TODO: Re-implement wrap.go in Zig +//extern fn bwrap_main(argc: c_int, argv: [*]const [*:0]const u8) i32; + +export fn aisap_appimage_sandbox( + ai: *c.aisap_appimage, + argc: usize, + argv: [*:null]const ?[*:0]const u8, + err: *CAppImageError, +) void { + var zig_ai = getParent(ai); + + if (argc > 0) { + var args = zig_ai.allocator.alloc([]const u8, argc) catch { + err.* = .err; + return; + }; + defer zig_ai.allocator.free(args); -extern fn bwrap_main(argc: c_int, argv: [*]const [*:0]const u8) i32; + var i: usize = 0; + while (i < argc) { + const arg = argv[i] orelse { + // TODO: argc should never be longer than the length of argv, + // so maybe a panic would be better here? + args.len = i; -//fn aisap_appimage_sandbox(ai: *c.aisap_appimage, argc: i32, args: [*c]const [*c]const u8) i32 { -// var buf: [10000]u8 = undefined; -// var fba = std.heap.FixedBufferAllocator.init(&buf); -// var allocator = fba.allocator(); -// -// _ = argc; -// _ = args; -// -// // Build char** from the aisap-Go `WrapArgs` method. This will be replaced -// // once I can re-implement it in Zig -// var list = ArrayList([]const u8).init(allocator); -// var len: i32 = undefined; -// defer list.deinit(); -// -// // Since this is just bwrap's main() function renamed and built into a lib, -// // argv[0] should be set to `bwrap` -// var it: i32 = 0; -// -// list.append("bwrap") catch return 3; -// while (aisap_appimage_wraparg_next_go(ai, &len)) |arg| { -// var str: []const u8 = undefined; -// str.len = @intCast(len); -// str.ptr = arg; -// -// list.append(str) catch return 3; -// it += 1; -// } -// -// for (list.items) |str| { -// std.debug.print("{s} {d}", .{ str, it }); -// } -// -// // TODO: add args to command before executing -// _ = args; -// _ = argc; -// -// _ = std.ChildProcess.exec(.{ .allocator = allocator, .argv = list.items }) catch return 127; -// -// return 0; -//} + break; + }; + + args[i] = std.mem.sliceTo(arg, '\x00'); + i += 1; + } + + zig_ai.sandbox(.{ + .args = args, + }) catch { + err.* = .err; + }; + + return; + } + + zig_ai.sandbox(.{}) catch { + err.* = .err; + }; +} /// Get the SquashFS image offset of the AppImage /// Offset is stored in `off`, returns error code