mirror of
https://github.com/oven-sh/bun
synced 2026-02-15 05:12:29 +00:00
macOS
This commit is contained in:
@@ -10,6 +10,7 @@ src/bun.js/bindings/Base64Helpers.cpp
|
||||
src/bun.js/bindings/bindings.cpp
|
||||
src/bun.js/bindings/blob.cpp
|
||||
src/bun.js/bindings/bun-simdutf.cpp
|
||||
src/bun.js/bindings/bun-spawn-darwin.cpp
|
||||
src/bun.js/bindings/bun-spawn.cpp
|
||||
src/bun.js/bindings/BunClientData.cpp
|
||||
src/bun.js/bindings/BunCommonStrings.cpp
|
||||
|
||||
@@ -121,6 +121,8 @@ src/bun.js/bindings/Exception.zig
|
||||
src/bun.js/bindings/FetchHeaders.zig
|
||||
src/bun.js/bindings/FFI.zig
|
||||
src/bun.js/bindings/generated_classes_list.zig
|
||||
src/bun.js/bindings/GeneratedBindings.zig
|
||||
src/bun.js/bindings/GeneratedJS2Native.zig
|
||||
src/bun.js/bindings/GetterSetter.zig
|
||||
src/bun.js/bindings/HTTPServerAgent.zig
|
||||
src/bun.js/bindings/JSArray.zig
|
||||
|
||||
@@ -1237,8 +1237,11 @@ pub fn spawnProcessPosix(
|
||||
if (comptime Environment.isLinux) {
|
||||
attr.uid = options.uid;
|
||||
attr.gid = options.gid;
|
||||
} else if (comptime Environment.isMac) {
|
||||
// On macOS, uid/gid are handled separately in the custom spawn implementation
|
||||
// We don't set them on the attr here
|
||||
} else {
|
||||
// On non-Linux platforms, throw an error if uid/gid are specified
|
||||
// On other platforms, throw an error if uid/gid are specified
|
||||
if (options.uid != null or options.gid != null) {
|
||||
return .{ .err = bun.sys.Error.fromCode(.PERM, .posix_spawn) };
|
||||
}
|
||||
@@ -1472,6 +1475,66 @@ pub fn spawnProcessPosix(
|
||||
}
|
||||
|
||||
const argv0 = options.argv0 orelse argv[0].?;
|
||||
|
||||
// On macOS, if uid/gid are specified, we need to use a custom spawn implementation
|
||||
// because posix_spawn doesn't support uid/gid changes
|
||||
if (comptime Environment.isMac) {
|
||||
if (options.uid != null or options.gid != null) {
|
||||
// We need to use our custom fork+exec implementation
|
||||
var chdir_buf: ?[:0]u8 = null;
|
||||
defer if (chdir_buf) |buf| bun.default_allocator.free(buf);
|
||||
|
||||
if (options.cwd.len > 0) {
|
||||
chdir_buf = try bun.default_allocator.dupeZ(u8, options.cwd);
|
||||
}
|
||||
|
||||
const spawn_request = PosixSpawn.BunSpawnRequest{
|
||||
.chdir_buf = if (chdir_buf) |buf| buf.ptr else null,
|
||||
.detached = options.detached,
|
||||
.actions = .{
|
||||
.ptr = null,
|
||||
.len = 0,
|
||||
},
|
||||
.uid = options.uid orelse 0,
|
||||
.gid = options.gid orelse 0,
|
||||
.has_uid = options.uid != null,
|
||||
.has_gid = options.gid != null,
|
||||
};
|
||||
|
||||
const spawn_result = PosixSpawn.BunSpawnRequest.spawn(
|
||||
argv0,
|
||||
spawn_request,
|
||||
argv,
|
||||
envp,
|
||||
);
|
||||
|
||||
// Continue with the rest of the function
|
||||
var failed_after_spawn = false;
|
||||
defer {
|
||||
if (failed_after_spawn) {
|
||||
for (to_close_on_error.items) |fd| {
|
||||
fd.close();
|
||||
}
|
||||
to_close_on_error.clearAndFree();
|
||||
}
|
||||
}
|
||||
|
||||
switch (spawn_result) {
|
||||
.err => {
|
||||
failed_after_spawn = true;
|
||||
return .{ .err = spawn_result.err };
|
||||
},
|
||||
.result => |pid| {
|
||||
spawned.pid = pid;
|
||||
spawned.extra_pipes = extra_fds;
|
||||
extra_fds = std.ArrayList(bun.FileDescriptor).init(bun.default_allocator);
|
||||
|
||||
return .{ .result = spawned };
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const spawn_result = PosixSpawn.spawnZ(
|
||||
argv0,
|
||||
actions,
|
||||
|
||||
@@ -280,7 +280,7 @@ pub const PosixSpawn = struct {
|
||||
pub const Actions = if (Environment.isLinux) BunSpawn.Actions else PosixSpawnActions;
|
||||
pub const Attr = if (Environment.isLinux) BunSpawn.Attr else PosixSpawnAttr;
|
||||
|
||||
const BunSpawnRequest = extern struct {
|
||||
pub const BunSpawnRequest = extern struct {
|
||||
chdir_buf: ?[*:0]u8 = null,
|
||||
detached: bool = false,
|
||||
actions: ActionsList = .{},
|
||||
@@ -295,9 +295,8 @@ pub const PosixSpawn = struct {
|
||||
};
|
||||
|
||||
extern fn posix_spawn_bun(
|
||||
pid: *c_int,
|
||||
path: [*:0]const u8,
|
||||
request: *const BunSpawnRequest,
|
||||
path: [*:0]const u8,
|
||||
argv: [*:null]?[*:0]const u8,
|
||||
envp: [*:null]?[*:0]const u8,
|
||||
) isize;
|
||||
@@ -309,23 +308,21 @@ pub const PosixSpawn = struct {
|
||||
envp: [*:null]?[*:0]const u8,
|
||||
) Maybe(pid_t) {
|
||||
var req = req_;
|
||||
var pid: c_int = 0;
|
||||
|
||||
const rc = posix_spawn_bun(&pid, path, &req, argv, envp);
|
||||
const rc = posix_spawn_bun(&req, path, argv, envp);
|
||||
if (comptime bun.Environment.allow_assert)
|
||||
bun.sys.syslog("posix_spawn_bun({s}) = {d} ({d})", .{
|
||||
bun.sys.syslog("posix_spawn_bun({s}) = {d}", .{
|
||||
bun.span(argv[0] orelse ""),
|
||||
rc,
|
||||
pid,
|
||||
});
|
||||
|
||||
if (rc == 0) {
|
||||
return Maybe(pid_t){ .result = @intCast(pid) };
|
||||
if (rc > 0) {
|
||||
return Maybe(pid_t){ .result = @intCast(rc) };
|
||||
}
|
||||
|
||||
return Maybe(pid_t){
|
||||
.err = .{
|
||||
.errno = @as(bun.sys.Error.Int, @truncate(@intFromEnum(@as(std.c.E, @enumFromInt(rc))))),
|
||||
.errno = @as(bun.sys.Error.Int, @truncate(@intFromEnum(@as(std.c.E, @enumFromInt(-rc))))),
|
||||
.syscall = .posix_spawn,
|
||||
.path = bun.span(argv[0] orelse ""),
|
||||
},
|
||||
@@ -362,6 +359,7 @@ pub const PosixSpawn = struct {
|
||||
envp,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
var pid: pid_t = undefined;
|
||||
const rc = system.posix_spawn(
|
||||
|
||||
189
src/bun.js/bindings/bun-spawn-darwin.cpp
Normal file
189
src/bun.js/bindings/bun-spawn-darwin.cpp
Normal file
@@ -0,0 +1,189 @@
|
||||
#include "root.h"
|
||||
|
||||
#if OS(DARWIN)
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <cstring>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/resource.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
extern char** environ;
|
||||
|
||||
enum FileActionType : uint8_t {
|
||||
None,
|
||||
Close,
|
||||
Dup2,
|
||||
Open,
|
||||
};
|
||||
|
||||
typedef struct bun_spawn_request_file_action_t {
|
||||
FileActionType type;
|
||||
const char* path;
|
||||
int fds[2];
|
||||
int flags;
|
||||
int mode;
|
||||
} bun_spawn_request_file_action_t;
|
||||
|
||||
typedef struct bun_spawn_file_action_list_t {
|
||||
const bun_spawn_request_file_action_t* ptr;
|
||||
size_t len;
|
||||
} bun_spawn_file_action_list_t;
|
||||
|
||||
typedef struct bun_spawn_request_t {
|
||||
const char* chdir;
|
||||
bool detached;
|
||||
bun_spawn_file_action_list_t actions;
|
||||
uint32_t uid;
|
||||
uint32_t gid;
|
||||
bool has_uid;
|
||||
bool has_gid;
|
||||
} bun_spawn_request_t;
|
||||
|
||||
extern "C" ssize_t posix_spawn_bun(
|
||||
const bun_spawn_request_t* request,
|
||||
const char* path,
|
||||
char* const argv[],
|
||||
char* const envp[])
|
||||
{
|
||||
// Check permissions before forking
|
||||
if (request->has_uid && request->uid != geteuid()) {
|
||||
if (geteuid() != 0) {
|
||||
errno = EPERM;
|
||||
return -EPERM;
|
||||
}
|
||||
}
|
||||
|
||||
if (request->has_gid && request->gid != getegid()) {
|
||||
if (geteuid() != 0) {
|
||||
errno = EPERM;
|
||||
return -EPERM;
|
||||
}
|
||||
}
|
||||
|
||||
pid_t pid;
|
||||
int saved_errno;
|
||||
sigset_t oldmask;
|
||||
sigset_t newmask;
|
||||
|
||||
// Block all signals during fork to prevent signal handlers from running
|
||||
sigfillset(&newmask);
|
||||
sigprocmask(SIG_SETMASK, &newmask, &oldmask);
|
||||
|
||||
pid = fork();
|
||||
saved_errno = errno;
|
||||
|
||||
if (pid == 0) {
|
||||
// Child process
|
||||
|
||||
// Restore signal mask in child
|
||||
sigprocmask(SIG_SETMASK, &oldmask, NULL);
|
||||
|
||||
// Reset signal handlers to default
|
||||
struct sigaction sa;
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sa_handler = SIG_DFL;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
|
||||
for (int i = 1; i < NSIG; i++) {
|
||||
// Skip SIGKILL and SIGSTOP as they can't be changed
|
||||
if (i == SIGKILL || i == SIGSTOP) continue;
|
||||
sigaction(i, &sa, NULL);
|
||||
}
|
||||
|
||||
// Set up process session if detached
|
||||
if (request->detached) {
|
||||
setsid();
|
||||
}
|
||||
|
||||
// Change directory if requested
|
||||
if (request->chdir) {
|
||||
if (chdir(request->chdir) != 0) {
|
||||
_exit(127);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply file actions
|
||||
for (size_t i = 0; i < request->actions.len; i++) {
|
||||
const bun_spawn_request_file_action_t* action = &request->actions.ptr[i];
|
||||
|
||||
switch (action->type) {
|
||||
case Close:
|
||||
close(action->fds[0]);
|
||||
break;
|
||||
|
||||
case Dup2:
|
||||
if (dup2(action->fds[0], action->fds[1]) < 0) {
|
||||
_exit(127);
|
||||
}
|
||||
break;
|
||||
|
||||
case Open: {
|
||||
int fd = open(action->path, action->flags, action->mode);
|
||||
if (fd < 0) {
|
||||
_exit(127);
|
||||
}
|
||||
if (fd != action->fds[0]) {
|
||||
if (dup2(fd, action->fds[0]) < 0) {
|
||||
_exit(127);
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Close all file descriptors above stderr except those we just set up
|
||||
int max_fd = getdtablesize();
|
||||
for (int fd = 3; fd < max_fd; fd++) {
|
||||
int flags = fcntl(fd, F_GETFD);
|
||||
if (flags >= 0 && (flags & FD_CLOEXEC)) {
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
// Set group id before user id (required order)
|
||||
if (request->has_gid) {
|
||||
if (setgid(request->gid) != 0) {
|
||||
_exit(127);
|
||||
}
|
||||
}
|
||||
|
||||
if (request->has_uid) {
|
||||
if (setuid(request->uid) != 0) {
|
||||
_exit(127);
|
||||
}
|
||||
}
|
||||
|
||||
// Execute the program
|
||||
if (!envp) {
|
||||
envp = environ;
|
||||
}
|
||||
|
||||
execve(path, argv, envp);
|
||||
|
||||
// If we get here, execve failed
|
||||
_exit(127);
|
||||
}
|
||||
|
||||
// Parent process
|
||||
sigprocmask(SIG_SETMASK, &oldmask, NULL);
|
||||
|
||||
if (pid < 0) {
|
||||
// Fork failed
|
||||
errno = saved_errno;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user