|
| 1 | +/* Note: fork actions MUST NOT allocate (either on the OCaml heap or with C malloc). |
| 2 | + * This is because e.g. we might have forked while another thread in the parent had a lock. |
| 3 | + * In the child, we inherit a copy of the locked mutex, but no corresponding thread to |
| 4 | + * release it. |
| 5 | + */ |
| 6 | + |
1 | 7 | #include <stdlib.h> |
2 | 8 | #include <unistd.h> |
3 | 9 | #include <fcntl.h> |
|
6 | 12 |
|
7 | 13 | #include <caml/mlvalues.h> |
8 | 14 | #include <caml/unixsupport.h> |
| 15 | +#include <caml/memory.h> |
| 16 | +#include <caml/custom.h> |
| 17 | +#include <caml/fail.h> |
9 | 18 |
|
10 | 19 | #include "fork_action.h" |
11 | 20 |
|
@@ -42,24 +51,61 @@ void eio_unix_fork_error(int fd, char *fn, char *buf) { |
42 | 51 | try_write_all(fd, buf); |
43 | 52 | } |
44 | 53 |
|
45 | | -static char **make_string_array(int errors, value v_array) { |
46 | | - int n = Wosize_val(v_array); |
47 | | - char **c = calloc(sizeof(char *), (n + 1)); |
48 | | - if (!c) { |
49 | | - eio_unix_fork_error(errors, "make_string_array", "out of memory"); |
50 | | - _exit(1); |
51 | | - } |
| 54 | +#define String_array_val(v) *((char ***)Data_custom_val(v)) |
| 55 | + |
| 56 | +static void finalize_string_array(value v) { |
| 57 | + free(String_array_val(v)); |
| 58 | + String_array_val(v) = NULL; |
| 59 | +} |
| 60 | + |
| 61 | +static struct custom_operations string_array_ops = { |
| 62 | + "string.array", |
| 63 | + finalize_string_array, |
| 64 | + custom_compare_default, |
| 65 | + custom_hash_default, |
| 66 | + custom_serialize_default, |
| 67 | + custom_deserialize_default, |
| 68 | + custom_compare_ext_default, |
| 69 | + custom_fixed_length_default |
| 70 | +}; |
| 71 | + |
| 72 | +CAMLprim value eio_unix_make_string_array(value v_len) { |
| 73 | + CAMLparam0(); |
| 74 | + CAMLlocal1(v_str_array); |
| 75 | + int n = Int_val(v_len); |
| 76 | + uintnat total; |
| 77 | + |
| 78 | + if (caml_umul_overflow(sizeof(char *), n + 1, &total)) |
| 79 | + caml_raise_out_of_memory(); |
| 80 | + |
| 81 | + v_str_array = caml_alloc_custom_mem(&string_array_ops, sizeof(char ***), total); |
| 82 | + |
| 83 | + char **c = calloc(sizeof(char *), n + 1); |
| 84 | + String_array_val(v_str_array) = c; |
| 85 | + if (!c) |
| 86 | + caml_raise_out_of_memory(); |
| 87 | + |
| 88 | + CAMLreturn(v_str_array); |
| 89 | +} |
| 90 | + |
| 91 | +static void fill_string_array(char **c, value v_ocaml_array) { |
| 92 | + int n = Wosize_val(v_ocaml_array); |
| 93 | + |
52 | 94 | for (int i = 0; i < n; i++) { |
53 | | - c[i] = (char *) String_val(Field(v_array, i)); |
| 95 | + c[i] = (char *) String_val(Field(v_ocaml_array, i)); |
54 | 96 | } |
| 97 | + |
55 | 98 | c[n] = NULL; |
56 | | - return c; |
57 | 99 | } |
58 | 100 |
|
59 | 101 | static void action_execve(int errors, value v_config) { |
60 | 102 | value v_exe = Field(v_config, 1); |
61 | | - char **argv = make_string_array(errors, Field(v_config, 2)); |
62 | | - char **envp = make_string_array(errors, Field(v_config, 3)); |
| 103 | + char **argv = String_array_val(Field(v_config, 2)); |
| 104 | + char **envp = String_array_val(Field(v_config, 4)); |
| 105 | + |
| 106 | + fill_string_array(argv, Field(v_config, 3)); |
| 107 | + fill_string_array(envp, Field(v_config, 5)); |
| 108 | + |
63 | 109 | execve(String_val(v_exe), argv, envp); |
64 | 110 | eio_unix_fork_error(errors, "execve", strerror(errno)); |
65 | 111 | _exit(1); |
|
0 commit comments