From e265a44d213dd415584d59b978b6daba76fe17da Mon Sep 17 00:00:00 2001 From: Rudi Grinberg Date: Mon, 3 Jul 2023 14:26:39 +0100 Subject: [PATCH] chore: update spawn bindings (#8086) Signed-off-by: Rudi Grinberg --- vendor/spawn/src/spawn.ml | 6 ++- vendor/spawn/src/spawn.mli | 11 ++++- vendor/spawn/src/spawn_stubs.c | 80 +++++++++++++++++++++++++++++----- vendor/update-spawn.sh | 4 +- 4 files changed, 86 insertions(+), 15 deletions(-) diff --git a/vendor/spawn/src/spawn.ml b/vendor/spawn/src/spawn.ml index 8a766d37013..c7dcee07d0a 100644 --- a/vendor/spawn/src/spawn.ml +++ b/vendor/spawn/src/spawn.ml @@ -100,6 +100,7 @@ external spawn_unix : -> stderr:Unix.file_descr -> use_vfork:bool -> setpgid:int option + -> sigprocmask:(Unix.sigprocmask_command * int list) option -> int = "spawn_unix_byte" "spawn_unix" external spawn_windows : @@ -122,7 +123,7 @@ let maybe_quote f = f let spawn_windows ~env ~cwd ~prog ~argv ~stdin ~stdout ~stderr ~use_vfork:_ - ~setpgid:_ = + ~setpgid:_ ~sigprocmask:_ = let cwd = match (cwd : Working_dir.t) with | Path p -> Some p @@ -146,7 +147,7 @@ let no_null s = let spawn ?env ?(cwd = Working_dir.Inherit) ~prog ~argv ?(stdin = Unix.stdin) ?(stdout = Unix.stdout) ?(stderr = Unix.stderr) - ?(unix_backend = Unix_backend.default) ?setpgid () = + ?(unix_backend = Unix_backend.default) ?setpgid ?sigprocmask () = (match cwd with | Path s -> no_null s | Fd _ @@ -166,6 +167,7 @@ let spawn ?env ?(cwd = Working_dir.Inherit) ~prog ~argv ?(stdin = Unix.stdin) | Fork -> false in backend ~env ~cwd ~prog ~argv ~stdin ~stdout ~stderr ~use_vfork ~setpgid + ~sigprocmask external safe_pipe : unit -> Unix.file_descr * Unix.file_descr = "spawn_pipe" diff --git a/vendor/spawn/src/spawn.mli b/vendor/spawn/src/spawn.mli index ca2bc1ddfa2..e4d2454b395 100644 --- a/vendor/spawn/src/spawn.mli +++ b/vendor/spawn/src/spawn.mli @@ -96,7 +96,14 @@ end {b Signals} - On Unix, the sub-process will have all its signals unblocked. + On Unix, by default, the sub-process will have all its signals unblocked. If + [sigprocmask] is passed, the sub-process will have its sigprocmask modified + with the given [sigprocmask_command], relative to the calling thread. At no + point will any OCaml function observe any intermediate signal mask. + + Attempts to unblock a signal that is not blocked, to block a signal that is + already blocked, or to block a signal that cannot be blocked (e.g., SIGSTOP, + SIGKILL) are allowed and will be silently ignored. {b Implementation} @@ -113,6 +120,8 @@ val spawn : -> ?stderr:Unix.file_descr -> ?unix_backend:Unix_backend.t (* default: [Unix_backend.default] *) -> ?setpgid:Pgid.t + -> ?sigprocmask:Unix.sigprocmask_command * int list + (** default: unblock all signals in child *) -> unit -> int diff --git a/vendor/spawn/src/spawn_stubs.c b/vendor/spawn/src/spawn_stubs.c index 870baa0b7bc..1693b58ccd1 100644 --- a/vendor/spawn/src/spawn_stubs.c +++ b/vendor/spawn/src/spawn_stubs.c @@ -4,11 +4,13 @@ #include #include #include -#include #include #include +#include +CAMLextern int caml_convert_signal_number(int); + #if defined(__APPLE__) CAMLprim value spawn_is_osx() @@ -46,6 +48,13 @@ CAMLprim value spawn_is_osx() #if defined(__APPLE__) +/* vfork(2) is deprecated on macOS >= 12, so we use fork(2) instead. */ +# if defined(__MAC_OS_X_VERSION_MAX_ALLOWED) +# if __MAC_OS_X_VERSION_MAX_ALLOWED >= 120000 +# define vfork fork +# endif +# endif + static int safe_pipe(int fd[2]) { int i; @@ -170,7 +179,7 @@ static void subprocess_failure(int failure_fd, /* Block all signals to avoid being interrupted in write. Although most of the call sites of [subprocess_failure] already block - signals, the one after the [exec] does not. */ + signals, the one after the [exec] might not. */ sigfillset(&sigset); pthread_sigmask(SIG_SETMASK, &sigset, NULL); @@ -212,13 +221,13 @@ struct spawn_info { int std_fds[3]; int set_pgid; pid_t pgid; + sigset_t child_sigmask; }; static void subprocess(int failure_fd, struct spawn_info *info) { int i, fd, tmp_fds[3]; struct sigaction sa; - sigset_t sigset; if (info->set_pgid) { if (setpgid(0, info->pgid) == -1) { @@ -227,8 +236,9 @@ static void subprocess(int failure_fd, struct spawn_info *info) } } - /* Restore all signals to their default behavior before unblocking - them, to avoid invoking handlers from the parent */ + /* Restore all signals to their default behavior before setting the + desired signal mask for the subprocess to avoid invoking handlers + from the parent */ sa.sa_handler = SIG_DFL; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; @@ -264,8 +274,7 @@ static void subprocess(int failure_fd, struct spawn_info *info) close(tmp_fds[fd]); } - sigemptyset(&sigset); - pthread_sigmask(SIG_SETMASK, &sigset, NULL); + pthread_sigmask(SIG_SETMASK, &info->child_sigmask, NULL); execve(info->prog, info->argv, info->env); subprocess_failure(failure_fd, "execve", PROG); @@ -353,6 +362,12 @@ static void free_spawn_info(struct spawn_info *info) extern char ** environ; +enum caml_unix_sigprocmask_command { + CAML_SIG_SETMASK, + CAML_SIG_BLOCK, + CAML_SIG_UNBLOCK, +}; + CAMLprim value spawn_unix(value v_env, value v_cwd, value v_prog, @@ -361,7 +376,8 @@ CAMLprim value spawn_unix(value v_env, value v_stdout, value v_stderr, value v_use_vfork, - value v_setpgid) + value v_setpgid, + value v_sigprocmask) { CAMLparam4(v_env, v_cwd, v_prog, v_argv); pid_t ret; @@ -436,6 +452,47 @@ CAMLprim value spawn_unix(value v_env, sigfillset(&sigset); pthread_sigmask(SIG_SETMASK, &sigset, &saved_procmask); + if (v_sigprocmask == Val_long(0)) { + sigemptyset(&info.child_sigmask); + } else { + v_sigprocmask = Field(v_sigprocmask, 0); + value v_sigprocmask_command = Field(v_sigprocmask, 0); + enum caml_unix_sigprocmask_command sigprocmask_command = Long_val(v_sigprocmask_command); + + switch (sigprocmask_command) { + case CAML_SIG_SETMASK: + sigemptyset(&info.child_sigmask); + break; + + case CAML_SIG_BLOCK: + case CAML_SIG_UNBLOCK: + info.child_sigmask = saved_procmask; + break; + + default: + caml_failwith("Unknown sigprocmask action"); + } + + for (value v_signals_list = Field(v_sigprocmask, 1); + v_signals_list != Val_emptylist; + v_signals_list = Field(v_signals_list, 1)) { + int signal = caml_convert_signal_number(Long_val(Field(v_signals_list, 0))); + switch (sigprocmask_command) { + case CAML_SIG_SETMASK: + case CAML_SIG_BLOCK: + sigaddset(&info.child_sigmask, signal); + break; + + case CAML_SIG_UNBLOCK: + sigdelset(&info.child_sigmask, signal); + break; + + default: + assert(0); + } + } + } + ret = Bool_val(v_use_vfork) ? vfork() : fork(); if (ret == 0) { @@ -525,7 +582,8 @@ CAMLprim value spawn_unix(value v_env, value v_stdout, value v_stderr, value v_use_vfork, - value v_setpgid) + value v_setpgid, + value v_sigprocmask) { (void)v_env; (void)v_cwd; @@ -536,6 +594,7 @@ CAMLprim value spawn_unix(value v_env, (void)v_stderr; (void)v_use_vfork; (void)v_setpgid; + (void)v_sigprocmask; unix_error(ENOSYS, "spawn_unix", Nothing); } @@ -617,7 +676,8 @@ CAMLprim value spawn_unix_byte(value * argv) argv[5], argv[6], argv[7], - argv[8]); + argv[8], + argv[9]); } CAMLprim value spawn_windows_byte(value * argv) diff --git a/vendor/update-spawn.sh b/vendor/update-spawn.sh index 13a4c80169a..aff2c385836 100755 --- a/vendor/update-spawn.sh +++ b/vendor/update-spawn.sh @@ -1,6 +1,6 @@ #!/bin/bash -version=e159092e9941ec3972c836927232f5680b6cb487 +version=48a7145ca41e60d7124e6215bb19139b846985be set -e -o pipefail @@ -12,7 +12,7 @@ mkdir -p spawn/src ( cd $TMP - git clone https://github.com/janestreet/spawn.git + git clone https://github.com/ocaml-dune/spawn.git cd spawn git checkout $version )