Skip to content

Commit f7b883e

Browse files
Add support for setpgid (#23)
For moving the child to a different process group, possibly new. Signed-off-by: Jeremie Dimino <[email protected]>
1 parent 7d88c7c commit f7b883e

File tree

8 files changed

+103
-6
lines changed

8 files changed

+103
-6
lines changed

src/spawn.ml

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,21 @@ module Env : Env = (val if Sys.win32 then
7575
else
7676
(module Env_unix) : Env)
7777

78+
module Pgid = struct
79+
type t = int
80+
81+
let new_process_group = 0
82+
83+
let of_pid = function
84+
| 0 ->
85+
raise (Invalid_argument "bad pid: 0 (hint: use [Pgid.new_process_group])")
86+
| t ->
87+
if t < 0 then
88+
raise (Invalid_argument ("bad pid: " ^ string_of_int t))
89+
else
90+
t
91+
end
92+
7893
external spawn_unix :
7994
env:Env.t option
8095
-> cwd:Working_dir.t
@@ -84,6 +99,7 @@ external spawn_unix :
8499
-> stdout:Unix.file_descr
85100
-> stderr:Unix.file_descr
86101
-> use_vfork:bool
102+
-> setpgid:int option
87103
-> int = "spawn_unix_byte" "spawn_unix"
88104

89105
external spawn_windows :
@@ -96,7 +112,8 @@ external spawn_windows :
96112
-> stderr:Unix.file_descr
97113
-> int = "spawn_windows_byte" "spawn_windows"
98114

99-
let spawn_windows ~env ~cwd ~prog ~argv ~stdin ~stdout ~stderr ~use_vfork:_ =
115+
let spawn_windows ~env ~cwd ~prog ~argv ~stdin ~stdout ~stderr ~use_vfork:_
116+
~setpgid:_ =
100117
let cwd =
101118
match (cwd : Working_dir.t) with
102119
| Path p -> Some p
@@ -120,7 +137,7 @@ let no_null s =
120137

121138
let spawn ?env ?(cwd = Working_dir.Inherit) ~prog ~argv ?(stdin = Unix.stdin)
122139
?(stdout = Unix.stdout) ?(stderr = Unix.stderr)
123-
?(unix_backend = Unix_backend.default) () =
140+
?(unix_backend = Unix_backend.default) ?setpgid () =
124141
(match cwd with
125142
| Path s -> no_null s
126143
| Fd _
@@ -139,7 +156,7 @@ let spawn ?env ?(cwd = Working_dir.Inherit) ~prog ~argv ?(stdin = Unix.stdin)
139156
| Vfork -> true
140157
| Fork -> false
141158
in
142-
backend ~env ~cwd ~prog ~argv ~stdin ~stdout ~stderr ~use_vfork
159+
backend ~env ~cwd ~prog ~argv ~stdin ~stdout ~stderr ~use_vfork ~setpgid
143160

144161
external safe_pipe : unit -> Unix.file_descr * Unix.file_descr = "spawn_pipe"
145162

src/spawn.mli

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,21 @@ module Env : sig
3131
val of_list : string list -> t
3232
end
3333

34+
(** Process group IDs *)
35+
module Pgid : sig
36+
(** Representation of the second parameter to [setpgid]. If a value of this
37+
type is provided to [spawn], the child will immediately set its pgid
38+
accordingly. *)
39+
type t
40+
41+
(** Sets the child's pgid to the same as its process id. Equivalent to calling
42+
[setpgid(0, 0)]. *)
43+
val new_process_group : t
44+
45+
(** Raises [Invalid_arg] if the value is not strictly positive. *)
46+
val of_pid : int -> t
47+
end
48+
3449
(** Spawn a sub-command and return its PID. This function is low-level and
3550
should be used to build higher-level APIs.
3651
@@ -73,6 +88,12 @@ end
7388
input, output and error output of the sub-process. When not specified, they
7489
default to the ones from the calling process.
7590
91+
{b Process groups}
92+
93+
If [setpgid] is provided, the child will immediately call [setpgid(0,pid)],
94+
where [pid] is a [pid_t] determined from the [Pgid.t] given (see that
95+
module). This parameter has no effect on Windows platforms.
96+
7697
{b Signals}
7798
7899
On Unix, the sub-process will have all its signals unblocked.
@@ -91,6 +112,7 @@ val spawn :
91112
-> ?stdout:Unix.file_descr
92113
-> ?stderr:Unix.file_descr
93114
-> ?unix_backend:Unix_backend.t (* default: [Unix_backend.default] *)
115+
-> ?setpgid:Pgid.t
94116
-> unit
95117
-> int
96118

src/spawn_stubs.c

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,8 @@ struct spawn_info {
208208
char *prog;
209209
char **argv;
210210
int std_fds[3];
211+
int set_pgid;
212+
pid_t pgid;
211213
};
212214

213215
static void subprocess(int failure_fd, struct spawn_info *info)
@@ -216,6 +218,13 @@ static void subprocess(int failure_fd, struct spawn_info *info)
216218
struct sigaction sa;
217219
sigset_t sigset;
218220

221+
if (info->set_pgid) {
222+
if (setpgid(0, info->pgid) == -1) {
223+
subprocess_failure(failure_fd, "setpgid", NOTHING);
224+
return;
225+
}
226+
}
227+
219228
/* Restore all signals to their default behavior before unblocking
220229
them, to avoid invoking handlers from the parent */
221230
sa.sa_handler = SIG_DFL;
@@ -349,7 +358,8 @@ CAMLprim value spawn_unix(value v_env,
349358
value v_stdin,
350359
value v_stdout,
351360
value v_stderr,
352-
value v_use_vfork)
361+
value v_use_vfork,
362+
value v_setpgid)
353363
{
354364
CAMLparam4(v_env, v_cwd, v_prog, v_argv);
355365
pid_t ret;
@@ -394,6 +404,10 @@ CAMLprim value spawn_unix(value v_env,
394404
info.env =
395405
Is_block(v_env) ?
396406
alloc_string_vect(Field(v_env, 0)) : copy_c_string_array(environ);
407+
info.set_pgid = Is_block(v_setpgid);
408+
info.pgid =
409+
Is_block(v_setpgid) ?
410+
Long_val(Field(v_setpgid, 0)) : 0;
397411

398412
caml_enter_blocking_section();
399413
enter_safe_pipe_section();
@@ -508,7 +522,8 @@ CAMLprim value spawn_unix(value v_env,
508522
value v_stdin,
509523
value v_stdout,
510524
value v_stderr,
511-
value v_use_vfork)
525+
value v_use_vfork,
526+
value v_setpgid)
512527
{
513528
(void)v_env;
514529
(void)v_cwd;
@@ -518,6 +533,7 @@ CAMLprim value spawn_unix(value v_env,
518533
(void)v_stdout;
519534
(void)v_stderr;
520535
(void)v_use_vfork;
536+
(void)v_setpgid;
521537
unix_error(ENOSYS, "spawn_unix", Nothing);
522538
}
523539

@@ -598,7 +614,8 @@ CAMLprim value spawn_unix_byte(value * argv)
598614
argv[4],
599615
argv[5],
600616
argv[6],
601-
argv[7]);
617+
argv[7],
618+
argv[8]);
602619
}
603620

604621
CAMLprim value spawn_windows_byte(value * argv)

test/dune

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
(deps
88
exe/hello.exe
99
exe/list_files.exe
10+
pgid_test/checkpgid.exe
1011
(sandbox always)))
1112
(preprocess
1213
(pps ppx_expect)))

test/pgid_test/checkpgid.ml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
external getpgid : int -> int = "test_getpgid"
2+
3+
let main =
4+
if not Sys.win32 then
5+
let pid = Unix.getpid () in
6+
let pgid = getpgid pid in
7+
if pid <> pgid then failwith "pgid and pid not equal"

test/pgid_test/dune

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
(executable
2+
(name checkpgid)
3+
(libraries unix)
4+
(foreign_stubs
5+
(language c)
6+
(names stubs)))

test/pgid_test/stubs.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#include <sys/types.h>
2+
#include <unistd.h>
3+
4+
#include <caml/mlvalues.h>
5+
#include <caml/unixsupport.h>
6+
7+
#if !defined(_WIN32)
8+
9+
CAMLprim value test_getpgid(value pid)
10+
{
11+
return Val_int(getpgid(Int_val(pid)));
12+
}
13+
14+
#else
15+
16+
CAMLprim value test_getpgid(value pid)
17+
{
18+
unix_error(ENOSYS, "getpgid", Nothing);
19+
}
20+
21+
#endif

test/tests.ml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,3 +137,9 @@ let%expect_test "prog relative to cwd" =
137137
wait
138138
(Spawn.spawn () ~prog:"./hello.exe" ~argv:[ "hello" ] ~cwd:(Path "exe"));
139139
[%expect {| Hello, world! |}]
140+
141+
let%expect_test "pgid tests" =
142+
wait
143+
(Spawn.spawn ~setpgid:Spawn.Pgid.new_process_group ()
144+
~prog:"pgid_test/checkpgid.exe" ~argv:[]);
145+
[%expect {||}]

0 commit comments

Comments
 (0)