cli: _macos-disclaim
parent
73158249e3
commit
633bf5f021
|
|
@ -19,6 +19,7 @@ const crash_report = @import("crash_report.zig");
|
|||
const show_face = @import("show_face.zig");
|
||||
const boo = @import("boo.zig");
|
||||
const new_window = @import("new_window.zig");
|
||||
const macos_disclaim = @import("macos_disclaim.zig");
|
||||
|
||||
/// Special commands that can be invoked via CLI flags. These are all
|
||||
/// invoked by using `+<action>` as a CLI flag. The only exception is
|
||||
|
|
@ -69,6 +70,9 @@ pub const Action = enum {
|
|||
// Use IPC to tell the running Ghostty to open a new window.
|
||||
@"new-window",
|
||||
|
||||
// Internal helper for posix_spawn that performs pre-exec setup
|
||||
@"_macos-disclaim",
|
||||
|
||||
pub fn detectSpecialCase(arg: []const u8) ?SpecialCase(Action) {
|
||||
// If we see a "-e" and we haven't seen a command yet, then
|
||||
// we are done looking for commands. This special case enables
|
||||
|
|
@ -102,6 +106,9 @@ pub const Action = enum {
|
|||
// to find this action in the help strings and output that.
|
||||
help_error => err: {
|
||||
inline for (@typeInfo(Action).@"enum".fields) |field| {
|
||||
// Skip internal commands (prefixed with underscore)
|
||||
if (field.name[0] == '_') continue;
|
||||
|
||||
// Future note: for now we just output the help text directly
|
||||
// to stdout. In the future we can style this much prettier
|
||||
// for all commands by just changing this one place.
|
||||
|
|
@ -147,6 +154,7 @@ pub const Action = enum {
|
|||
.@"show-face" => try show_face.run(alloc),
|
||||
.boo => try boo.run(alloc),
|
||||
.@"new-window" => try new_window.run(alloc),
|
||||
.@"_macos-disclaim" => try macos_disclaim.run(alloc),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -186,6 +194,7 @@ pub const Action = enum {
|
|||
.@"show-face" => show_face.Options,
|
||||
.boo => boo.Options,
|
||||
.@"new-window" => new_window.Options,
|
||||
.@"_macos-disclaim" => macos_disclaim.Options,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,6 +63,8 @@ pub fn run(alloc: Allocator) !u8 {
|
|||
);
|
||||
|
||||
inline for (@typeInfo(Action).@"enum".fields) |field| {
|
||||
// Skip internal commands (prefixed with underscore)
|
||||
if (field.name[0] == '_') continue;
|
||||
try stdout.print(" +{s}\n", .{field.name});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,76 @@
|
|||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const posix = std.posix;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const posix_spawn = @import("../os/posix_spawn.zig");
|
||||
|
||||
const log = std.log.scoped(.macos_disclaim);
|
||||
|
||||
pub const Options = struct {
|
||||
pub fn deinit(self: Options) void {
|
||||
_ = self;
|
||||
}
|
||||
};
|
||||
|
||||
/// The `_macos-disclaim` command is an internal-only Ghostty command that
|
||||
/// is only available on macOS. It uses private posix_spawn APIs to
|
||||
/// make the child process the "responssible process" in macOS so it is
|
||||
/// in charge of its own TCC (permissions like Downloads folder access or
|
||||
/// camera) and resource accounting rather than Ghostty.
|
||||
pub fn run(alloc: Allocator) !u8 {
|
||||
// This helper is only for Apple systems. POSIX in general has posix_spawn
|
||||
// but we only use it on Apple platforms because it lets us shed our
|
||||
// responsible process bit.
|
||||
if (comptime builtin.os.tag != .macos) {
|
||||
log.warn("macos-disclaim is only supported on macOS", .{});
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Get the command to exec from the remaining args
|
||||
// Skip arg 0 (our program name) and arg 1 (the action "+_macos-disclaim")
|
||||
var arg_iter = try std.process.argsWithAllocator(alloc);
|
||||
defer arg_iter.deinit();
|
||||
_ = arg_iter.skip();
|
||||
_ = arg_iter.skip();
|
||||
|
||||
// Collect remaining args for exec
|
||||
var args: std.ArrayList(?[*:0]const u8) = .empty;
|
||||
defer args.deinit(alloc);
|
||||
while (arg_iter.next()) |arg| try args.append(alloc, arg);
|
||||
if (args.items.len == 0) {
|
||||
log.err("no command specified to exec", .{});
|
||||
return 1;
|
||||
}
|
||||
try args.append(alloc, null);
|
||||
|
||||
var attrs = try posix_spawn.spawn_attr.create();
|
||||
defer posix_spawn.spawn_attr.destroy(&attrs);
|
||||
{
|
||||
try posix_spawn.spawn_attr.setflags(&attrs, .{
|
||||
// Act like exec(): replace this process.
|
||||
.setexec = true,
|
||||
});
|
||||
|
||||
// This is the magical private API that makes it so that this
|
||||
// child process doesn't get looped into the TCC and resource
|
||||
// accounting of Ghostty.
|
||||
try posix_spawn.spawn_attr.disclaim(&attrs, true);
|
||||
}
|
||||
|
||||
_ = posix_spawn.spawnp(
|
||||
std.mem.span(args.items[0].?),
|
||||
null,
|
||||
&attrs,
|
||||
args.items[0 .. args.items.len - 1 :null].ptr,
|
||||
std.c.environ,
|
||||
) catch |err| {
|
||||
log.err("failed to posix_spawn command '{s}': {}", .{
|
||||
std.mem.span(args.items[0].?),
|
||||
err,
|
||||
});
|
||||
return 1;
|
||||
};
|
||||
|
||||
// We set the exec flag so we can't reach this point.
|
||||
unreachable;
|
||||
}
|
||||
|
|
@ -82,6 +82,9 @@ fn genActions(alloc: std.mem.Allocator, writer: *std.Io.Writer) !void {
|
|||
);
|
||||
|
||||
inline for (@typeInfo(Action).@"enum".fields) |field| {
|
||||
// Skip internal commands (prefixed with underscore)
|
||||
if (field.name[0] == '_') continue;
|
||||
|
||||
const action_file = comptime action_file: {
|
||||
const action = @field(Action, field.name);
|
||||
break :action_file action.file();
|
||||
|
|
|
|||
16
src/pty.zig
16
src/pty.zig
|
|
@ -83,7 +83,7 @@ const NullPty = struct {
|
|||
/// Linux PTY creation and management. This is just a thin layer on top
|
||||
/// of Linux syscalls. The caller is responsible for detail-oriented handling
|
||||
/// of the returned file handles.
|
||||
const PosixPty = struct {
|
||||
pub const PosixPty = struct {
|
||||
pub const Error = OpenError || GetModeError || GetSizeError || SetSizeError || ChildPreExecError;
|
||||
|
||||
pub const Fd = posix.fd_t;
|
||||
|
|
@ -249,6 +249,20 @@ const PosixPty = struct {
|
|||
posix.close(self.slave);
|
||||
posix.close(self.master);
|
||||
}
|
||||
|
||||
/// This is the pre-exec that needs to happen for posix_spawn on macOS
|
||||
/// because the API doesn't support all the actions/attrs we need to
|
||||
/// create a proper terminal environment.
|
||||
pub fn posixSpawnPreExec(self: PosixPty) !void {
|
||||
// Set controlling terminal
|
||||
switch (posix.errno(c.ioctl(self.slave, TIOCSCTTY, @as(c_ulong, 0)))) {
|
||||
.SUCCESS => {},
|
||||
else => |err| {
|
||||
log.err("error setting controlling terminal errno={}", .{err});
|
||||
return error.SetControllingTerminalFailed;
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// Windows PTY creation and management.
|
||||
|
|
|
|||
Loading…
Reference in New Issue