Compare commits

...

6 Commits

Author SHA1 Message Date
Jarred Sumner
dc21a407b1 Update spawn.zig 2024-11-17 20:35:15 -08:00
Jarred Sumner
db911eb96d Update bun-spawn.cpp 2024-11-17 20:02:05 -08:00
Jarred Sumner
d061d32beb Update subprocess.zig 2024-11-17 19:56:19 -08:00
Jarred Sumner
9f73c4d575 Update subprocess.zig 2024-11-17 19:46:12 -08:00
Jarred Sumner
08d467906c Add types 2024-11-17 19:45:13 -08:00
Jarred Sumner
a930fe9a5a Introduce deathSig option in Bun.spawn 2024-11-17 19:32:51 -08:00
13 changed files with 80 additions and 2 deletions

View File

@@ -4862,6 +4862,35 @@ declare module "bun" {
* ```
*/
signal?: AbortSignal;
/**
* When Bun exits, automatically send this signal to subprocesses. This is
* helpful in cases where you want to make it harder for a subprocess to
* continue running after Bun has exited. Detached processes will ignore this.
*
* **Linux-only** - internally, this uses [`prctl(PR_SET_PDEATHSIG, ...)`](https://man7.org/linux/man-pages/man2/pr_set_pdeathsig.2const.html)
* When used on other platforms, this option is silently ignored.
*
* @example
*
* ```ts
* import {spawn} from "bun";
* const subprocess = spawn({
* cmd: ["sleep", "1000000"],
* deathSig: "SIGTERM",
* });
* process.exit(0);
* ```
*
* Then, run
*
* ```sh
* ps aux | grep sleep
* ```
*
* You'll see that the `sleep` process is no longer running after the `ps` command is run.
*/
deathSig?: "SIGTERM" | "SIGINT" | "SIGKILL" | number | string;
}
type OptionsToSubprocess<Opts extends OptionsObject> =

View File

@@ -970,6 +970,11 @@ pub const PosixSpawnOptions = struct {
argv0: ?[*:0]const u8 = null,
stream: bool = true,
/// Linux-only.
///
/// Send a signal to the parent process when the child process exits.
deathsig: bun.SignalCode = @enumFromInt(0),
/// Apple Extension: If this bit is set, rather
/// than returning to the caller, posix_spawn(2)
/// and posix_spawnp(2) will behave as a more
@@ -1041,6 +1046,9 @@ pub const WindowsSpawnOptions = struct {
stream: bool = true,
use_execve_on_macos: bool = false,
/// Linux-only. Does nothing on Windows.
deathsig: bun.SignalCode = @enumFromInt(0),
pub const WindowsOptions = struct {
verbatim_arguments: bool = false,
hide_window: bool = true,
@@ -1226,6 +1234,10 @@ pub fn spawnProcessPosix(
if (options.detached) {
flags |= bun.C.POSIX_SPAWN_SETSID;
} else if (Environment.isLinux) {
if (@intFromEnum(options.deathsig) > 0) {
actions.deathsig = @intFromEnum(options.deathsig);
}
}
if (options.cwd.len > 0) {
@@ -1776,6 +1788,7 @@ pub const sync = struct {
ipc: ?bun.FileDescriptor = null,
cwd: []const u8 = "",
detached: bool = false,
deathsig: bun.SignalCode = @enumFromInt(0),
argv: []const []const u8,
/// null = inherit parent env
@@ -1809,6 +1822,7 @@ pub const sync = struct {
.stdout = this.stdout.toStdio(),
.stderr = this.stderr.toStdio(),
.ipc = this.ipc,
.deathsig = this.deathsig,
.cwd = this.cwd,
.detached = this.detached,

View File

@@ -64,6 +64,7 @@ pub const BunSpawn = struct {
chdir_buf: ?[*:0]u8 = null,
actions: std.ArrayListUnmanaged(Action) = .{},
detached: bool = false,
deathsig: i32 = 0,
pub fn init() !Actions {
return .{};
@@ -126,7 +127,7 @@ pub const BunSpawn = struct {
pub const Attr = struct {
detached: bool = false,
deathsig: i32 = 0,
pub fn init() !Attr {
return Attr{};
}
@@ -299,6 +300,7 @@ pub const PosixSpawn = struct {
const BunSpawnRequest = extern struct {
chdir_buf: ?[*:0]u8 = null,
detached: bool = false,
deathsig: i32 = 0,
actions: ActionsList = .{},
const ActionsList = extern struct {
@@ -365,6 +367,7 @@ pub const PosixSpawn = struct {
},
.chdir_buf = if (actions) |a| a.chdir_buf else null,
.detached = if (attr) |a| a.detached else false,
.deathsig = if (attr) |a| a.deathsig else 0,
},
argv,
envp,

View File

@@ -1711,6 +1711,7 @@ pub const Subprocess = struct {
var extra_fds = std.ArrayList(bun.spawn.SpawnOptions.Stdio).init(bun.default_allocator);
var argv0: ?[*:0]const u8 = null;
var ipc_channel: i32 = -1;
var death_signal: bun.SignalCode = @enumFromInt(0);
var windows_hide: bool = false;
var windows_verbatim_arguments: bool = false;
@@ -1873,6 +1874,20 @@ pub const Subprocess = struct {
}
}
if (args.getTruthy(globalThis, "deathSig")) |death_signal_val| {
if (death_signal_val.isNumber()) {
const signal_int = globalThis.validateIntegerRange(death_signal_val, u8, 0, .{ .min = 1, .max = bun.SignalCode.max }) orelse return .zero;
death_signal = @enumFromInt(signal_int);
} else if (death_signal_val.isString()) {
death_signal = bun.SignalCode.Map.fromJS(globalThis, death_signal_val) orelse {
globalThis.throwInvalidArguments("deathSignal must be a valid signal code", .{});
return .zero;
};
} else if (death_signal_val.toBoolean()) {
return globalThis.throwInvalidArgumentTypeValue("deathSignal", "number or string", death_signal_val);
}
}
if (args.getTruthy(globalThis, "onDisconnect")) |onDisconnect_| {
if (!onDisconnect_.isCell() or !onDisconnect_.isCallable(globalThis.vm())) {
globalThis.throwInvalidArguments("onDisconnect must be a function or undefined", .{});
@@ -2105,7 +2120,7 @@ pub const Subprocess = struct {
},
.extra_fds = extra_fds.items,
.argv0 = argv0,
.deathsig = death_signal,
.windows = if (Environment.isWindows) .{
.hide_window = windows_hide,
.verbatim_arguments = windows_verbatim_arguments,

View File

@@ -12,6 +12,7 @@
#include <signal.h>
#include <sys/syscall.h>
#include <sys/resource.h>
#include <sys/prctl.h>
extern char** environ;
@@ -44,6 +45,7 @@ typedef struct bun_spawn_file_action_list_t {
typedef struct bun_spawn_request_t {
const char* chdir;
bool detached;
int deathsig;
bun_spawn_file_action_list_t actions;
} bun_spawn_request_t;
@@ -85,6 +87,9 @@ extern "C" ssize_t posix_spawn_bun(
// Make "detached" work
if (request->detached) {
setsid();
} else if (request->deathsig > 0) {
// If the child dies, send the specified signal to the parent
prctl(PR_SET_PDEATHSIG, request->deathsig);
}
int current_max_fd = 0;

View File

@@ -1206,6 +1206,8 @@ pub const SignalCode = enum(u8) {
SIGSYS = 31,
_,
pub const max = @intFromEnum(SignalCode.SIGSYS);
// The `subprocess.kill()` method sends a signal to the child process. If no
// argument is given, the process will be sent the 'SIGTERM' signal.
pub const default = @intFromEnum(SignalCode.SIGTERM);

View File

@@ -637,6 +637,7 @@ pub const BunxCommand = struct {
.stderr = .inherit,
.stdout = .inherit,
.stdin = .inherit,
.deathsig = .SIGTERM,
.windows = if (Environment.isWindows) .{
.loop = bun.JSC.EventLoopHandle.init(bun.JSC.MiniEventLoop.initGlobal(this_bundler.env)),

View File

@@ -147,6 +147,7 @@ fn execTask(allocator: std.mem.Allocator, task_: string, cwd: string, _: string,
_ = bun.spawnSync(&.{
.argv = argv,
.envp = null,
.deathsig = .SIGTERM,
.cwd = cwd,
.stderr = .inherit,
@@ -1515,6 +1516,7 @@ pub const CreateCommand = struct {
.stderr = .inherit,
.stdout = .inherit,
.stdin = .inherit,
.deathsig = .SIGTERM,
.windows = if (Environment.isWindows) .{
.loop = bun.JSC.EventLoopHandle.init(bun.JSC.MiniEventLoop.initGlobal(null)),

View File

@@ -332,6 +332,7 @@ pub const RunCommand = struct {
.stdout = .inherit,
.stdin = .inherit,
.ipc = ipc_fd,
.deathsig = .SIGTERM,
.windows = if (Environment.isWindows) .{
.loop = JSC.EventLoopHandle.init(JSC.MiniEventLoop.initGlobal(env)),
@@ -497,6 +498,7 @@ pub const RunCommand = struct {
.stdout = .inherit,
.stdin = .inherit,
.use_execve_on_macos = silent,
.deathsig = .SIGTERM,
.windows = if (Environment.isWindows) .{
.loop = JSC.EventLoopHandle.init(JSC.MiniEventLoop.initGlobal(env)),

View File

@@ -684,6 +684,7 @@ pub const UpgradeCommand = struct {
.stderr = .inherit,
.stdout = .inherit,
.stdin = .inherit,
.deathsig = .SIGTERM,
.windows = if (Environment.isWindows) .{
.loop = bun.JSC.EventLoopHandle.init(bun.JSC.MiniEventLoop.initGlobal(null)),

View File

@@ -185,6 +185,8 @@ pub const LifecycleScriptSubprocess = struct {
},
.cwd = cwd,
.deathsig = .SIGTERM,
.windows = if (Environment.isWindows)
.{
.loop = JSC.EventLoopHandle.init(&manager.event_loop),

View File

@@ -36,6 +36,7 @@ pub fn openURL(url: stringZ) void {
.stderr = .inherit,
.stdout = .inherit,
.stdin = .inherit,
.deathsig = .SIGTERM,
.windows = if (Environment.isWindows) .{
.loop = bun.JSC.EventLoopHandle.init(bun.JSC.MiniEventLoop.initGlobal(null)),

View File

@@ -1291,6 +1291,7 @@ pub fn spawnOpts(
.cwd = cwd,
.envp = envp,
.argv = argv,
.deathsig = .SIGTERM,
.windows = if (bun.Environment.isWindows) .{ .loop = switch (loop.*) {
.js => |x| .{ .js = x },
.mini => |*x| .{ .mini = x },