1 #include "rktio.h"
2 #include "rktio_private.h"
3 #include <stdlib.h>
4 #include <string.h>
5 #include <stdio.h>
6 #if defined(RKTIO_SYSTEM_UNIX)
7 # include <signal.h>
8 # include <sys/types.h>
9 # include <sys/wait.h>
10 # include <errno.h>
11 # include <unistd.h>
12 # ifdef USE_ULIMIT
13 # include <ulimit.h>
14 # endif
15 #endif
16
17 #if defined(RKTIO_SYSTEM_UNIX) && defined(RKTIO_USE_PTHREADS)
18 #define CENTRALIZED_SIGCHILD
19 #endif
20
21 #ifdef RKTIO_SYSTEM_UNIX
22 static void reliably_copy_or_move_fd(int src_fd, int target_fd, int move);
23 static void close_non_standard_fd(int fd);
24 #endif
25
26 /*========================================================================*/
27 /* Process data structure */
28 /*========================================================================*/
29
30 struct rktio_process_t {
31 void *handle;
32 int pid;
33 int is_group, in_group;
34 #ifdef CENTRALIZED_SIGCHILD
35 short done;
36 int status;
37 #endif
38 #ifdef RKTIO_SYSTEM_WINDOWS
39 int got_time;
40 #endif
41 };
42
43 /*========================================================================*/
44 /* Status and signal helpers */
45 /*========================================================================*/
46
47 #if defined(RKTIO_SYSTEM_UNIX)
48
extract_child_status(int status)49 static int extract_child_status(int status)
50 {
51 if (WIFEXITED(status))
52 status = WEXITSTATUS(status);
53 else if (WIFSIGNALED(status))
54 status = WTERMSIG(status) + 128;
55 else
56 status = -1;
57
58 return status;
59 }
60
61 #endif
62
63 /*========================================================================*/
64 /* SIGCHLD management for a multi-threaded environment */
65 /*========================================================================*/
66
67 /* If SIGCHLD is unblocked, it gets delivered to a random thread
68 --- not necessarily on in the right place for the subprocess.
69 To avoid that problem, we centralize SIGCHLD handling here, and
70 then dispatch back out to specific places as they request
71 information. */
72
73 #ifdef CENTRALIZED_SIGCHILD
74
75 typedef struct Child_Status {
76 int pid;
77 int status;
78 char done;
79 char unneeded; /* not in a group; result not needed */
80 char in_group;
81 rktio_signal_handle_t *signal_fd;
82 struct Child_Status *next;
83 struct Child_Status *next_unused; /* see unused_pid_statuses */
84 } Child_Status;
85
86 static Child_Status *child_statuses = NULL;
87 static pthread_mutex_t child_status_lock;
88 static pthread_mutex_t child_wait_lock; /* ordered before status lock */
89 static int status_lock_initialized;
90
91 static int started_thread, pending_children;
92
93 /* When the process value for a process in a different group
94 is released before a waitpid() on the process, then we
95 need to keep waiting on the pid to let the OS gc the process.
96 This list is especially needed for processes that we create in
97 their own group, but it's also needed for processes that put
98 themselves in their own group (which we conservatively assume
99 can be any child process).
100 This list is protected by the wait lock. */
101 static Child_Status *unused_pid_statuses = NULL;
102
103 static void add_group_signal_fd(rktio_signal_handle_t *signal_fd);
104 static void remove_group_signal_fd(rktio_signal_handle_t *signal_fd);
105 static void do_group_signal_fds();
106 static int centralized_get_child_status(int pid, int in_group, int can_check_group, int *status);
107 static int raw_get_child_status(int pid, int *status, int done_only, int do_remove, int do_free);
108
add_child_status(int pid,int status)109 static void add_child_status(int pid, int status)
110 {
111 Child_Status *st;
112
113 /* Search for existing record, which will have a signal_fd: */
114 pthread_mutex_lock(&child_status_lock);
115 for (st = child_statuses; st; st = st->next) {
116 if (st->pid == pid)
117 break;
118 }
119
120 if (!st) {
121 /* must have terminated before it was registered
122 (and since we detected it, it must not be in a group) */
123 st = malloc(sizeof(Child_Status));
124 st->pid = pid;
125 st->signal_fd = NULL;
126 st->next = child_statuses;
127 child_statuses = st;
128 st->next_unused = NULL;
129 st->unneeded = 0;
130 st->in_group = 0;
131 }
132 st->status = status;
133 st->done = 1;
134
135 if (st->signal_fd && st->in_group)
136 remove_group_signal_fd(st->signal_fd);
137
138
139 if (st->signal_fd)
140 rktio_signal_received_at(st->signal_fd);
141 if (st->unneeded)
142 (void)raw_get_child_status(st->pid, NULL, 1, 1, 1);
143
144 pthread_mutex_unlock(&child_status_lock);
145 }
146
raw_get_child_status(int pid,int * status,int done_only,int do_remove,int do_free)147 static int raw_get_child_status(int pid, int *status, int done_only, int do_remove, int do_free)
148 {
149 Child_Status *st;
150 Child_Status *prev;
151 int found = 0;
152
153 for (st = child_statuses, prev = NULL; st; prev = st, st = st->next) {
154 if (st->pid == pid) {
155 if (!done_only || st->done) {
156 if (status)
157 *status = st->status;
158 found = 1;
159 if (do_remove) {
160 if (prev)
161 prev->next = st->next;
162 else
163 child_statuses = st->next;
164 }
165 if (do_free)
166 free(st);
167 }
168 break;
169 }
170 }
171 return found;
172 }
173
centralized_get_child_status(int pid,int in_group,int can_check_group,int * status)174 int centralized_get_child_status(int pid, int in_group, int can_check_group, int *status)
175 {
176 int found = 0;
177
178 /* Check specific pid, in case the child has its own group
179 (either given by Racket or given to itself): */
180 if (can_check_group) {
181 pid_t pid2;
182 int status;
183
184 do {
185 pid2 = waitpid((pid_t)pid, &status, WNOHANG);
186 } while ((pid2 == -1) && (errno == EINTR));
187
188 if (pid2 > 0)
189 add_child_status(pid, extract_child_status(status));
190 }
191
192 pthread_mutex_lock(&child_status_lock);
193 found = raw_get_child_status(pid, status, 1, 1, 1);
194 pthread_mutex_unlock(&child_status_lock);
195 /* printf("centralized_get_child_status found %i pid %i status %i\n", found, pid, *status); */
196
197 return found;
198 }
199
centralized_register_child(int pid,int in_group,rktio_signal_handle_t * signal_fd,int * status)200 static int centralized_register_child(int pid, int in_group, rktio_signal_handle_t *signal_fd, int *status)
201 {
202 int found = 0;
203
204 pthread_mutex_lock(&child_status_lock);
205
206 /* The child may have terminated already: */
207 found = raw_get_child_status(pid, status, 0, 0, 0);
208
209 if (!found) {
210 /* Create a record for the child: */
211 Child_Status *st;
212 st = malloc(sizeof(Child_Status));
213 st->pid = pid;
214 st->signal_fd = signal_fd;
215 st->status = 0;
216 st->unneeded = 0;
217 st->done = 0;
218 st->in_group = in_group;
219
220 st->next = child_statuses;
221 child_statuses = st;
222 st->next_unused = NULL;
223
224 if (in_group)
225 add_group_signal_fd(signal_fd);
226 }
227
228 pthread_mutex_unlock(&child_status_lock);
229 return found;
230 }
231
thread_signal_worker(void * data)232 static void *thread_signal_worker(void *data)
233 {
234 int status;
235 int pid, check_pid, in_group;
236 sigset_t set;
237 Child_Status *unused_status, *prev_unused, *next;
238
239 sigemptyset(&set);
240 sigaddset(&set, SIGCHLD);
241
242 while (1) {
243 int rc;
244 int signalid;
245
246 do {
247 rc = sigwait(&set, &signalid);
248 if (rc == -1) {
249 if (errno != EINTR) {
250 fprintf(stderr, "unexpected error from sigwait(): %d\n", errno);
251 }
252 }
253 } while (rc == -1 && errno == EINTR);
254
255 pthread_mutex_lock(&child_status_lock);
256 do_group_signal_fds();
257 pthread_mutex_unlock(&child_status_lock);
258
259 pthread_mutex_lock(&child_wait_lock);
260
261 unused_status = unused_pid_statuses;
262 prev_unused = NULL;
263
264 do {
265 if (unused_status) {
266 /* See unused_pid_statuses above */
267 check_pid = unused_status->pid;
268 in_group = 1;
269 } else {
270 /* We wait only on processes in the same group as the current process,
271 because detecting the termination of a group's main process
272 disables our ability to terminate all processes in the group. */
273 if (pending_children)
274 check_pid = 0; /* => processes in the same group as the current process */
275 else
276 check_pid = -1; /* don't check */
277 in_group = 0;
278 }
279
280 if (check_pid == -1) {
281 pid = -1;
282 errno = ECHILD;
283 } else
284 pid = waitpid(check_pid, &status, WNOHANG);
285
286 if (pid == -1) {
287 if (errno == EINTR) {
288 /* try again */
289 pid = 1;
290 } else if (!in_group && (errno == ECHILD)) {
291 /* no more to check */
292 } else {
293 fprintf(stderr, "unexpected error from waitpid(%d[%d]): %d\n",
294 check_pid, in_group, errno);
295 if (in_group) {
296 prev_unused = unused_status;
297 unused_status = unused_status->next;
298 }
299 }
300 } else if (pid > 0) {
301 /* printf("SIGCHILD pid %i with status %i %i\n", pid, status, WEXITSTATUS(status)); */
302 if (in_group) {
303 next = unused_status->next_unused;
304 if (prev_unused)
305 prev_unused->next_unused = next;
306 else
307 unused_pid_statuses = next;
308 free(unused_status);
309 unused_status = next;
310 } else {
311 /* Double-check for pid in unused_pid_statuses, since
312 it may have completed between the pid-specific waitpid and the
313 non-group waitpid: */
314 prev_unused = NULL;
315 for (unused_status = unused_pid_statuses; unused_status; unused_status = unused_status->next_unused) {
316 if (unused_status->pid == pid)
317 break;
318 prev_unused = unused_status;
319 }
320 if (!unused_status) {
321 /* not in unused_pid_statuses: */
322 add_child_status(pid, extract_child_status(status));
323 } else {
324 if (prev_unused)
325 prev_unused->next_unused = unused_status->next_unused;
326 else
327 unused_pid_statuses = unused_status->next_unused;
328 free(unused_status);
329 unused_status = NULL;
330 }
331 }
332 } else {
333 if (in_group) {
334 prev_unused = unused_status;
335 unused_status = unused_status->next_unused;
336 }
337 }
338 } while ((pid > 0) || in_group);
339
340 pthread_mutex_unlock(&child_wait_lock);
341 }
342
343 return NULL;
344 }
345
centralized_done_with_process_id(int pid,int in_group)346 void centralized_done_with_process_id(int pid, int in_group)
347 {
348 Child_Status *st;
349 int keep_unused = 1; /* assume that any process can be in a new group */
350
351 pthread_mutex_lock(&child_wait_lock); /* protects unused_pid_statuses */
352 pthread_mutex_lock(&child_status_lock);
353
354 for (st = child_statuses; st; st = st->next) {
355 if (st->pid == pid) {
356 if (!st->done) {
357 if (keep_unused) {
358 st->next_unused = unused_pid_statuses;
359 unused_pid_statuses = st;
360 if (st->signal_fd)
361 remove_group_signal_fd(st->signal_fd);
362 } else
363 st->unneeded = 1;
364 st->signal_fd = NULL;
365 }
366 break;
367 }
368 }
369
370 if (st && (keep_unused || st->done)) {
371 /* remove it from normal list: */
372 raw_get_child_status(pid, NULL, 0, 1, st->done);
373 }
374
375 pthread_mutex_unlock(&child_status_lock);
376 pthread_mutex_unlock(&child_wait_lock);
377 }
378
got_sigchld()379 static void got_sigchld()
380 {
381 /* handle doesn't need to to anything, since sigwait()
382 in a thread does the work. */
383 }
384
block_sigchld()385 static void block_sigchld()
386 {
387 sigset_t set;
388 sigemptyset(&set);
389 sigaddset(&set, SIGCHLD);
390 sigprocmask(SIG_BLOCK, &set, NULL);
391 }
392
centralized_block_child_signal()393 void centralized_block_child_signal()
394 {
395 /* SIGCHLD is always blocked, since it's managed via sigwait() */
396 }
397
centralized_unblock_child_signal()398 void centralized_unblock_child_signal()
399 {
400 sigset_t set;
401 sigemptyset(&set);
402 sigaddset(&set, SIGCHLD);
403 sigprocmask(SIG_UNBLOCK, &set, NULL);
404 }
405
centralized_start_child_signal_handler()406 void centralized_start_child_signal_handler()
407 {
408 if (!status_lock_initialized) {
409 pthread_mutex_init(&child_status_lock, NULL);
410 pthread_mutex_init(&child_wait_lock, NULL);
411 status_lock_initialized = 1;
412 }
413 }
414
centralized_wait_suspend()415 void centralized_wait_suspend()
416 {
417 pthread_mutex_lock(&child_wait_lock);
418 }
419
centralized_wait_resume()420 void centralized_wait_resume()
421 {
422 pthread_mutex_unlock(&child_wait_lock);
423 }
424
centralized_starting_child()425 void centralized_starting_child()
426 {
427 pthread_mutex_lock(&child_wait_lock);
428
429 if (!started_thread) {
430 pthread_t signal_thread;
431
432 /* Mac OS X seems to need a handler installed for SIGCHLD to be
433 delivered, since the default is to drop the signal. Also, this
434 handler serves as a back-up alert if some thread is created that
435 does not block SIGCHLD.
436 Solaris, meanwhile, seems to unmask SIGCHLD as a result of
437 setting a handler, so do this before masking the signal. */
438 rktio_set_signal_handler(SIGCHLD, got_sigchld);
439
440 /* Block SIGCLHD (again), because the worker thread will use sigwait(). */
441 block_sigchld();
442
443 (void)pthread_create(&signal_thread, NULL, thread_signal_worker, NULL);
444
445 (void)pthread_detach(signal_thread);
446 started_thread = 1;
447 }
448
449 pending_children++;
450
451 pthread_mutex_unlock(&child_wait_lock);
452 }
453
centralized_ended_child()454 void centralized_ended_child()
455 {
456 pthread_mutex_lock(&child_wait_lock);
457 --pending_children;
458 pthread_mutex_unlock(&child_wait_lock);
459 }
460
rktio_reap_processes(rktio_t * rktio)461 void rktio_reap_processes(rktio_t *rktio)
462 {
463 /* Not needed, since the worker thread is already reaping
464 processes. */
465 }
466
467 /* ---------------------------------------------------------------------- */
468
469 /* When a place has a process-group that it may be waiting on, the we
470 need to wake up the place whenever any SIGCHLD is received, since
471 the SIGDCHLD may apply to one of those places.
472 The list of signal_fds is protected by the status lock. */
473
474 typedef struct Group_Signal_FD {
475 rktio_signal_handle_t *signal_fd;
476 int refcount;
477 } Group_Signal_FD;
478
479 static Group_Signal_FD *signal_fds;
480 static int signal_fd_count;
481
add_group_signal_fd(rktio_signal_handle_t * signal_fd)482 static void add_group_signal_fd(rktio_signal_handle_t *signal_fd)
483 {
484 int i, count = 0;
485 Group_Signal_FD *a;
486
487 for (i = 0; i < signal_fd_count; i++) {
488 if (signal_fds[i].refcount) {
489 count++;
490 if (signal_fds[i].signal_fd == signal_fd) {
491 signal_fds[i].refcount++;
492 return;
493 }
494 }
495 }
496
497 if (count == signal_fd_count) {
498 signal_fd_count = (signal_fd_count + 4) * 2;
499 a = malloc(sizeof(Group_Signal_FD) * signal_fd_count);
500 memset(a, 0, sizeof(Group_Signal_FD) * signal_fd_count);
501 if (signal_fds) {
502 memcpy(a, signal_fds, sizeof(Group_Signal_FD) * count);
503 free(signal_fds);
504 }
505 signal_fds = a;
506 }
507
508 for (i = 0; i < signal_fd_count; i++) {
509 if (!signal_fds[i].refcount) {
510 signal_fds[i].signal_fd = signal_fd;
511 signal_fds[i].refcount = 1;
512 break;
513 }
514 }
515 }
516
remove_group_signal_fd(rktio_signal_handle_t * signal_fd)517 static void remove_group_signal_fd(rktio_signal_handle_t *signal_fd)
518 {
519 int i;
520
521 for (i = 0; i < signal_fd_count; i++) {
522 if (signal_fds[i].refcount) {
523 if (signal_fds[i].signal_fd == signal_fd) {
524 --signal_fds[i].refcount;
525 return;
526 }
527 }
528 }
529 }
530
do_group_signal_fds()531 static void do_group_signal_fds()
532 {
533 int i;
534
535 for (i = 0; i < signal_fd_count; i++) {
536 if (signal_fds[i].refcount) {
537 rktio_signal_received_at(signal_fds[i].signal_fd);
538 }
539 }
540 }
541
542 #endif
543
544
545 /*========================================================================*/
546 /* Unix signal handling (without pthreads) */
547 /*========================================================================*/
548
549 #if defined(RKTIO_SYSTEM_UNIX) && !defined(CENTRALIZED_SIGCHILD)
550
551 typedef struct System_Child {
552 pid_t id;
553 short done;
554 int status;
555 struct System_Child *next;
556 } System_Child;
557
block_child_signals(rktio_t * rktio,int block)558 static void block_child_signals(rktio_t*rktio, int block)
559 {
560 sigset_t sigs;
561
562 sigemptyset(&sigs);
563 sigaddset(&sigs, SIGCHLD);
564 sigprocmask(block ? SIG_BLOCK : SIG_UNBLOCK, &sigs, NULL);
565 }
566
567 /* See `unused_pid_statuses' for
568 a reminder of why this is needed (in both
569 implementations): */
570 static void *unused_pids;
571
572 /* We get only on signal handler for all rktio instances,
573 so we have to chain them: */
574 static rktio_t *all_rktios;
575 static int sigchld_installed = 0;
576
child_done(int ingored)577 static void child_done(int ingored)
578 {
579 rktio_t *rktio = all_rktios;
580
581 while (rktio) {
582 rktio->need_to_check_children = 1;
583 rktio_signal_received(rktio);
584 rktio = rktio->next;
585 }
586 }
587
init_sigchld(rktio_t * rktio)588 static void init_sigchld(rktio_t *rktio)
589 {
590 #if !defined(CENTRALIZED_SIGCHILD)
591 if (!sigchld_installed) {
592 rktio_set_signal_handler(SIGCHLD, child_done);
593 sigchld_installed = 1;
594 }
595
596 if (!rktio->in_sigchld_chain) {
597 /* Signals are blocked by the caller of init_sigchild */
598 rktio->in_sigchld_chain = 1;
599 rktio->next = all_rktios;
600 all_rktios = rktio;
601 }
602 #endif
603 }
604
remove_from_sigchld_chain(rktio_t * rktio)605 static void remove_from_sigchld_chain(rktio_t *rktio)
606 {
607 if (rktio->in_sigchld_chain) {
608 rktio_t *rio = all_rktios, *prev = NULL;
609 while (rio) {
610 if (rio == rktio) {
611 if (prev)
612 prev->next = rktio->next;
613 else
614 all_rktios = rktio->next;
615 return;
616 }
617 prev = rio;
618 rio = rio->next;
619 }
620 }
621 }
622
check_child_done(rktio_t * rktio,pid_t pid)623 static void check_child_done(rktio_t *rktio, pid_t pid)
624 {
625 pid_t result, check_pid;
626 int status, is_unused;
627 System_Child *sc, *prev;
628 void **unused = (void **)unused_pids, **unused_prev = NULL;
629
630 if (pid && rktio->need_to_check_children) {
631 rktio->need_to_check_children = 0;
632 check_child_done(rktio, 0);
633 }
634
635 if (rktio->system_children) {
636 do {
637 if (!pid && unused) {
638 check_pid = (pid_t)(intptr_t)unused[0];
639 is_unused = 1;
640 } else {
641 check_pid = pid;
642 is_unused = 0;
643 }
644
645 do {
646 result = waitpid(check_pid, &status, WNOHANG);
647 } while ((result == -1) && (errno == EINTR));
648
649 if (result > 0) {
650 if (is_unused) {
651 /* done with an inaccessible group id */
652 void *next;
653 next = (void **)unused[1];
654 if (unused_prev)
655 unused_prev[1] = unused[1];
656 else
657 unused_pids = unused[1];
658 free(unused);
659 unused = (void **)next;
660 }
661
662 status = extract_child_status(status);
663
664 prev = NULL;
665 for (sc = rktio->system_children; sc; prev = sc, sc = sc->next) {
666 if (sc->id == result) {
667 sc->done = 1;
668 sc->status = status;
669
670 if (prev) {
671 prev->next = sc->next;
672 } else
673 rktio->system_children = sc->next;
674 }
675 }
676 } else {
677 if (is_unused) {
678 unused_prev = unused;
679 unused = unused[1];
680 }
681 }
682 } while ((result > 0) || is_unused);
683 }
684 }
685
rktio_reap_processes(rktio_t * rktio)686 void rktio_reap_processes(rktio_t *rktio)
687 {
688 if (rktio->need_to_check_children) {
689 rktio->need_to_check_children = 0;
690 check_child_done(rktio, 0);
691 }
692 }
693
694 #endif
695
696 #ifdef RKTIO_SYSTEM_WINDOWS
rktio_reap_processes(rktio_t * rktio)697 void rktio_reap_processes(rktio_t *rktio) { }
698 #endif
699
700 /*========================================================================*/
701 /* Windows process times */
702 /*========================================================================*/
703
704 /* Unix provides a counter for time consumed by subprocesses, but
705 Windows doesn't. */
706
707 #if defined(RKTIO_SYSTEM_WINDOWS)
collect_process_time(rktio_t * rktio,DWORD w,rktio_process_t * sp)708 static void collect_process_time(rktio_t *rktio, DWORD w, rktio_process_t *sp)
709 {
710 if ((w != STILL_ACTIVE) && !sp->got_time) {
711 FILETIME cr, ex, kr, us;
712 if (GetProcessTimes(sp->handle, &cr, &ex, &kr, &us)) {
713 rktio_int64_t v;
714 uintptr_t msecs;
715 v = ((((rktio_int64_t)kr.dwHighDateTime << 32) + kr.dwLowDateTime)
716 + (((rktio_int64_t)us.dwHighDateTime << 32) + us.dwLowDateTime));
717 msecs = (uintptr_t)(v / 10000);
718
719 rktio->process_children_msecs += msecs;
720 }
721 sp->got_time = 1;
722 }
723 }
724 #endif
725
726 /*========================================================================*/
727 /* Process status functions */
728 /*========================================================================*/
729
rktio_poll_process_done(rktio_t * rktio,rktio_process_t * sp)730 int rktio_poll_process_done(rktio_t *rktio, rktio_process_t *sp)
731 {
732 #if defined(RKTIO_SYSTEM_UNIX)
733 # if defined(CENTRALIZED_SIGCHILD)
734 {
735 int status;
736 if (!sp->done) {
737 if (centralized_get_child_status(sp->pid, sp->in_group, 1, &status)) {
738 sp->done = 1;
739 sp->status = status;
740 centralized_ended_child();
741 return 1;
742 }
743 return 0;
744 }
745 else
746 return RKTIO_PROCESS_DONE;
747 }
748 # else
749 {
750 System_Child *sc;
751 sc = (System_Child *)sp->handle;
752 /* Check specific pid, in case the child has its own group
753 (either given by us or given to itself): */
754 check_child_done(rktio, sp->pid);
755 return sc->done;
756 }
757 # endif
758 #endif
759 #ifdef RKTIO_SYSTEM_WINDOWS
760 {
761 HANDLE sci = (HANDLE)sp->handle;
762 DWORD w;
763 if (sci) {
764 if (GetExitCodeProcess(sci, &w)) {
765 collect_process_time(rktio, w, sp);
766 return (w != STILL_ACTIVE);
767 } else
768 return RKTIO_PROCESS_DONE;
769 } else
770 return RKTIO_PROCESS_DONE;
771
772 get_windows_error();
773
774 return RKTIO_PROCESS_ERROR;
775 }
776 #endif
777 }
778
rktio_poll_add_process(rktio_t * rktio,rktio_process_t * sp,rktio_poll_set_t * fds)779 void rktio_poll_add_process(rktio_t *rktio, rktio_process_t *sp, rktio_poll_set_t *fds)
780 {
781 if (rktio_poll_process_done(rktio, sp)) {
782 rktio_poll_set_add_nosleep(rktio, fds);
783 return;
784 }
785
786 #ifdef RKTIO_SYSTEM_WINDOWS
787 rktio_poll_set_add_handle(rktio, (intptr_t)sp->handle, fds, 0);
788 #endif
789 }
790
rktio_process_status(rktio_t * rktio,rktio_process_t * sp)791 rktio_status_t *rktio_process_status(rktio_t *rktio, rktio_process_t *sp)
792 {
793 int going = 0, status = 0;
794 rktio_status_t *result;
795
796 #if defined(RKTIO_SYSTEM_UNIX)
797 # if defined(CENTRALIZED_SIGCHILD)
798 if (sp->done) {
799 status = sp->status;
800 } else {
801 if (!centralized_get_child_status(sp->pid, sp->in_group, 1, &status)) {
802 going = 1;
803 } else {
804 sp->done = 1;
805 sp->status = status;
806 centralized_ended_child();
807 }
808 }
809 # else
810 System_Child *sc = (System_Child *)sp->handle;
811 check_child_done(rktio, sp->pid);
812
813 if (sc->done) {
814 status = sc->status;
815 } else
816 going = 1;
817 # endif
818 #else
819 # ifdef RKTIO_SYSTEM_WINDOWS
820 DWORD w;
821 if (sp->handle) {
822 if (GetExitCodeProcess((HANDLE)sp->handle, &w)) {
823 collect_process_time(rktio, w, sp);
824 if (w == STILL_ACTIVE)
825 going = 1;
826 else
827 status = w;
828 } else {
829 get_windows_error();
830 return NULL;
831 }
832 } else
833 status = -1;
834 # endif
835 #endif
836
837 result = malloc(sizeof(rktio_status_t));
838 result->running = going;
839 result->result = (going ? 0 : status);
840 return result;
841 }
842
do_subprocess_kill(rktio_t * rktio,rktio_process_t * sp,int as_kill)843 static int do_subprocess_kill(rktio_t *rktio, rktio_process_t *sp, int as_kill)
844 {
845 #if defined(RKTIO_SYSTEM_UNIX)
846 # if defined(CENTRALIZED_SIGCHILD)
847 {
848 int status;
849
850 if (sp->done)
851 return 1;
852
853 centralized_wait_suspend();
854
855 /* Don't allow group checking, because we don't want to wait
856 on a group if we haven't already: */
857 if (centralized_get_child_status(sp->pid, 0, 0, &status)) {
858 sp->status = status;
859 sp->done = 1;
860 centralized_wait_resume();
861 centralized_ended_child();
862 return 1;
863 }
864 }
865 # else
866 {
867 System_Child *sc = (System_Child *)sp->handle;
868
869 /* Don't pass sp->pid, because we don't want to wait
870 on a group if we haven't already: */
871 check_child_done(rktio, 0);
872 if (sc->done)
873 return 1;
874 }
875 # define centralized_wait_resume() /* empty */
876 # endif
877
878 while (1) {
879
880 if (sp->is_group) {
881 if (!killpg(sp->pid, as_kill ? SIGKILL : SIGINT)) {
882 centralized_wait_resume();
883 return 1;
884 }
885 } else {
886 if (!kill(sp->pid, as_kill ? SIGKILL : SIGINT)) {
887 centralized_wait_resume();
888 return 1;
889 }
890 }
891
892 if (errno != EINTR)
893 break;
894 /* Otherwise we were interrupted. Try `kill' again. */
895 }
896
897 get_posix_error();
898
899 centralized_wait_resume();
900
901 return 0;
902 #endif
903 #if defined(RKTIO_SYSTEM_WINDOWS)
904 if (as_kill || sp->is_group) {
905 DWORD w;
906
907 if (!sp->handle)
908 return 1;
909
910 if (!as_kill) {
911 /* must be for a group; we don't care whether the
912 original process is still running */
913 if (GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, sp->pid))
914 return 1;
915 } else if (GetExitCodeProcess((HANDLE)sp->handle, &w)) {
916 collect_process_time(rktio, w, sp);
917 if (w != STILL_ACTIVE)
918 return 1;
919 if (TerminateProcess((HANDLE)sp->handle, 1))
920 return 1;
921 }
922 get_windows_error();
923
924 return 0;
925 } else
926 return 1;
927 #endif
928 }
929
rktio_process_kill(rktio_t * rktio,rktio_process_t * sp)930 int rktio_process_kill(rktio_t *rktio, rktio_process_t *sp)
931 {
932 return do_subprocess_kill(rktio, sp, 1);
933 }
934
rktio_process_interrupt(rktio_t * rktio,rktio_process_t * sp)935 int rktio_process_interrupt(rktio_t *rktio, rktio_process_t *sp)
936 {
937 return do_subprocess_kill(rktio, sp, 0);
938 }
939
rktio_process_forget(rktio_t * rktio,rktio_process_t * sp)940 void rktio_process_forget(rktio_t *rktio, rktio_process_t *sp)
941 {
942 #ifdef RKTIO_SYSTEM_UNIX
943 # if defined(CENTRALIZED_SIGCHILD)
944 if (!sp->done) {
945 centralized_done_with_process_id(sp->pid, sp->in_group);
946 centralized_ended_child();
947 }
948 # else
949 System_Child *sc = (System_Child *)sp->handle, *sc2, *prev;
950 if (!sc->done) {
951 void **unused_pid;
952 unused_pid = malloc(sizeof(void *) * 2);
953 unused_pid[0] = (void *)(intptr_t)sp->pid;
954 unused_pid[1] = unused_pids;
955 rktio->need_to_check_children = 1;
956
957 prev = NULL;
958 for (sc2 = rktio->system_children; sc2; prev = sc2, sc2 = sc2->next) {
959 if (sc2 == sc) {
960 if (prev)
961 prev->next = sc->next;
962 else
963 rktio->system_children = sc->next;
964 }
965 }
966 }
967 free(sc);
968 # endif
969 #endif
970
971 #ifdef RKTIO_SYSTEM_WINDOWS
972 CloseHandle(sp->handle);
973 #endif
974
975 free(sp);
976 }
977
rktio_process_init(rktio_t * rktio)978 int rktio_process_init(rktio_t *rktio)
979 {
980 #if defined(CENTRALIZED_SIGCHILD)
981 /* Block SIGCHLD as early as possible, because
982 it's a per-thread setting on Linux, and we want SIGCHLD blocked everywhere. */
983 block_sigchld();
984
985 /* We'll set a handler later (possibly after other threads start),
986 so register SIGCHLD now: */
987 rktio_will_modify_os_signal_handler(SIGCHLD);
988
989 centralized_start_child_signal_handler();
990 #endif
991
992 return 1;
993 }
994
rktio_process_deinit(rktio_t * rktio)995 void rktio_process_deinit(rktio_t *rktio)
996 {
997 #ifdef RKTIO_SYSTEM_WINDOWS
998 if (rktio->process_job_object) {
999 TerminateJobObject(rktio->process_job_object, 1);
1000 CloseHandle(rktio->process_job_object);
1001 rktio->process_job_object = NULL;
1002 }
1003 #endif
1004 #if defined(RKTIO_SYSTEM_UNIX) && !defined(CENTRALIZED_SIGCHILD)
1005 remove_from_sigchld_chain(rktio);
1006 #endif
1007 }
1008
1009 /*========================================================================*/
1010 /* Windows command-line construction */
1011 /*========================================================================*/
1012
1013 #ifdef RKTIO_SYSTEM_WINDOWS
cmdline_protect(const char * s)1014 static char *cmdline_protect(const char *s)
1015 {
1016 char *naya;
1017 int ds;
1018 int has_space = 0, has_quote = 0, was_slash = 0;
1019
1020 if (!*s) return MSC_IZE(strdup)("\"\""); /* quote an empty argument */
1021
1022 for (ds = 0; s[ds]; ds++) {
1023 if (isspace(s[ds]) || (s[ds] == '\'')) {
1024 has_space = 1;
1025 was_slash = 0;
1026 } else if (s[ds] == '"') {
1027 has_quote += 1 + (2 * was_slash);
1028 was_slash = 0;
1029 } else if (s[ds] == '\\') {
1030 was_slash++;
1031 } else
1032 was_slash = 0;
1033 }
1034
1035 if (has_space || has_quote) {
1036 char *p;
1037 int wrote_slash = 0;
1038
1039 naya = malloc(strlen(s) + 3 + 3*has_quote + was_slash);
1040 naya[0] = '"';
1041 for (p = naya + 1; *s; s++) {
1042 if (*s == '"') {
1043 while (wrote_slash--) {
1044 *(p++) = '\\';
1045 }
1046 *(p++) = '"'; /* endquote */
1047 *(p++) = '\\';
1048 *(p++) = '"'; /* protected */
1049 *(p++) = '"'; /* start quote again */
1050 wrote_slash = 0;
1051 } else if (*s == '\\') {
1052 *(p++) = '\\';
1053 wrote_slash++;
1054 } else {
1055 *(p++) = *s;
1056 wrote_slash = 0;
1057 }
1058 }
1059 while (wrote_slash--) {
1060 *(p++) = '\\';
1061 }
1062 *(p++) = '"';
1063 *p = 0;
1064
1065 return naya;
1066 }
1067
1068 return MSC_IZE(strdup)(s);
1069 }
1070
do_spawnv(rktio_t * rktio,const char * command,int argc,const char * const * argv,int exact_cmdline,intptr_t sin,intptr_t sout,intptr_t serr,int * pid,int new_process_group,int chain_termination_here_to_child,void * env,const char * wd)1071 static intptr_t do_spawnv(rktio_t *rktio,
1072 const char *command, int argc, const char * const *argv,
1073 int exact_cmdline, intptr_t sin, intptr_t sout, intptr_t serr, int *pid,
1074 int new_process_group, int chain_termination_here_to_child,
1075 void *env, const char *wd)
1076 {
1077 intptr_t i, l, len = 0;
1078 int use_jo;
1079 intptr_t cr_flag;
1080 char *cmdline;
1081 wchar_t *cmdline_w, *wd_w, *command_w;
1082 STARTUPINFOW startup;
1083 PROCESS_INFORMATION info;
1084
1085 if (exact_cmdline) {
1086 cmdline = (char *)argv[1];
1087 } else {
1088 for (i = 0; i < argc; i++) {
1089 len += strlen(argv[i]) + 1;
1090 }
1091
1092 cmdline = malloc(len);
1093
1094 len = 0;
1095 for (i = 0; i < argc; i++) {
1096 l = strlen(argv[i]);
1097 memcpy(cmdline + len, argv[i], l);
1098 cmdline[len + l] = ' ';
1099 len += l + 1;
1100 }
1101 --len;
1102 cmdline[len] = 0;
1103 }
1104
1105 memset(&startup, 0, sizeof(startup));
1106 startup.cb = sizeof(startup);
1107 startup.dwFlags = STARTF_USESTDHANDLES;
1108 startup.hStdInput = (HANDLE)sin;
1109 startup.hStdOutput = (HANDLE)sout;
1110 startup.hStdError = (HANDLE)serr;
1111
1112 /* If none of the stdio handles are consoles, specifically
1113 create the subprocess without a console: */
1114 if (!rktio_system_fd_is_terminal(rktio, (intptr_t)startup.hStdInput)
1115 && !rktio_system_fd_is_terminal(rktio, (intptr_t)startup.hStdOutput)
1116 && !rktio_system_fd_is_terminal(rktio, (intptr_t)startup.hStdError))
1117 cr_flag = CREATE_NO_WINDOW;
1118 else
1119 cr_flag = 0;
1120 if (new_process_group)
1121 cr_flag |= CREATE_NEW_PROCESS_GROUP;
1122 cr_flag |= CREATE_UNICODE_ENVIRONMENT;
1123
1124 use_jo = chain_termination_here_to_child;
1125 if (use_jo) {
1126 /* Use a job object to ensure that the new process will be terminated
1127 if this process ends for any reason (including a crash) */
1128 if (!rktio->process_job_object) {
1129 JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli;
1130
1131 rktio->process_job_object = CreateJobObject(NULL, NULL);
1132
1133 memset(&jeli, 0, sizeof(jeli));
1134 jeli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
1135 SetInformationJobObject(rktio->process_job_object,
1136 JobObjectExtendedLimitInformation,
1137 &jeli,
1138 sizeof(jeli));
1139 }
1140 }
1141
1142
1143 cmdline_w = WIDE_PATH_copy(cmdline);
1144 if (!exact_cmdline)
1145 free(cmdline);
1146 wd_w = WIDE_PATH_copy(wd);
1147 command_w = WIDE_PATH_temp(command);
1148
1149 if (cmdline_w
1150 && wd_w
1151 && command_w
1152 && CreateProcessW(command_w, cmdline_w,
1153 NULL, NULL, 1 /*inherit*/,
1154 cr_flag, env, wd_w,
1155 &startup, &info)) {
1156 if (use_jo)
1157 AssignProcessToJobObject(rktio->process_job_object, info.hProcess);
1158 CloseHandle(info.hThread);
1159 *pid = info.dwProcessId;
1160 free(cmdline_w);
1161 free(wd_w);
1162 return (intptr_t)info.hProcess;
1163 } else {
1164 if (cmdline_w) free(cmdline_w);
1165 if (wd_w) free(wd_w);
1166 return -1;
1167 }
1168 }
1169
CopyFileHandleForSubprocess(intptr_t * hs,int pos)1170 static void CopyFileHandleForSubprocess(intptr_t *hs, int pos)
1171 {
1172 HANDLE h2;
1173 int alt_pos = (pos ? 0 : 1);
1174
1175 if (DuplicateHandle(GetCurrentProcess(),
1176 (HANDLE)hs[pos],
1177 GetCurrentProcess(),
1178 &h2,
1179 0,
1180 TRUE,
1181 DUPLICATE_SAME_ACCESS)) {
1182 hs[pos] = (intptr_t)h2;
1183 hs[alt_pos] = 1;
1184 } else {
1185 hs[alt_pos] = 0;
1186 }
1187 }
1188
CloseFileHandleForSubprocess(intptr_t * hs,int pos)1189 static void CloseFileHandleForSubprocess(intptr_t *hs, int pos)
1190 {
1191 int alt_pos = (pos ? 0 : 1);
1192 if (hs[alt_pos]) {
1193 CloseHandle((HANDLE)hs[pos]);
1194 }
1195 }
1196
1197 #define RKTIO_COPY_FOR_SUBPROCESS(array, pos) CopyFileHandleForSubprocess(array, pos)
1198 #define RKTIO_CLOSE_SUBPROCESS_COPY(array, pos) CloseFileHandleForSubprocess(array, pos)
1199 #define RKTIO_CLOSE(fd) CloseHandle((HANDLE)fd)
1200
1201 #endif /* RKTIO_SYSTEM_WINDOWS */
1202
1203 #ifdef RKTIO_SYSTEM_UNIX
1204
1205 # define RKTIO_COPY_FOR_SUBPROCESS(array, pos) /* empty */
1206 # define RKTIO_CLOSE_SUBPROCESS_COPY(array, pos) /* empty */
1207 # define RKTIO_CLOSE(fd) rktio_reliably_close(fd)
1208
1209 #endif
1210
rktio_process_allowed_flags(rktio_t * rktio)1211 int rktio_process_allowed_flags(rktio_t *rktio)
1212 {
1213 int flags = (RKTIO_PROCESS_NEW_GROUP
1214 | RKTIO_PROCESS_STDOUT_AS_STDERR);
1215 #ifdef RKTIO_SYSTEM_WINDOWS
1216 flags |= (RKTIO_PROCESS_WINDOWS_EXACT_CMDLINE
1217 | RKTIO_PROCESS_WINDOWS_CHAIN_TERMINATION);
1218 #endif
1219 return flags;
1220 }
1221
1222 /*========================================================================*/
1223 /* Main process-creation function */
1224 /*========================================================================*/
1225
rktio_process(rktio_t * rktio,const char * command,int argc,rktio_const_string_t * argv,rktio_fd_t * stdout_fd,rktio_fd_t * stdin_fd,rktio_fd_t * stderr_fd,rktio_process_t * group_proc,const char * current_directory,rktio_envvars_t * envvars,int flags)1226 rktio_process_result_t *rktio_process(rktio_t *rktio,
1227 const char *command, int argc, rktio_const_string_t *argv,
1228 rktio_fd_t *stdout_fd, rktio_fd_t *stdin_fd, rktio_fd_t *stderr_fd,
1229 rktio_process_t *group_proc,
1230 const char *current_directory, rktio_envvars_t *envvars,
1231 int flags)
1232 {
1233 rktio_process_result_t *result;
1234 intptr_t to_subprocess[2], from_subprocess[2], err_subprocess[2];
1235 int pid;
1236 #if defined(RKTIO_SYSTEM_UNIX)
1237 # if !defined(CENTRALIZED_SIGCHILD)
1238 System_Child *sc;
1239 # endif
1240 #else
1241 void *sc = 0;
1242 #endif
1243 void *env;
1244 rktio_process_t *subproc;
1245 int close_after_len;
1246 rktio_const_string_t *new_argv;
1247 #if defined(RKTIO_SYSTEM_WINDOWS)
1248 intptr_t spawn_status;
1249 #endif
1250 int new_process_group = (flags & RKTIO_PROCESS_NEW_GROUP);
1251 int stderr_is_stdout = (flags & RKTIO_PROCESS_STDOUT_AS_STDERR);
1252 #if defined(RKTIO_SYSTEM_WINDOWS)
1253 int windows_exact_cmdline = (flags & RKTIO_PROCESS_WINDOWS_EXACT_CMDLINE);
1254 int windows_chain_termination_to_child = (flags & RKTIO_PROCESS_WINDOWS_CHAIN_TERMINATION);
1255 int i;
1256 #endif
1257
1258 /* avoid compiler warnings: */
1259 to_subprocess[0] = -1;
1260 to_subprocess[1] = -1;
1261 from_subprocess[0] = -1;
1262 from_subprocess[1] = -1;
1263 err_subprocess[0] = -1;
1264 err_subprocess[1] = -1;
1265
1266 /*--------------------------------------*/
1267 /* Create needed pipes */
1268 /*--------------------------------------*/
1269
1270 if (stdout_fd) {
1271 from_subprocess[1] = rktio_fd_system_fd(rktio, stdout_fd);
1272 RKTIO_COPY_FOR_SUBPROCESS(from_subprocess, 1);
1273 } else if (rktio_make_os_pipe(rktio, from_subprocess, RKTIO_NO_INHERIT_INPUT)) {
1274 return NULL;
1275 }
1276
1277 if (stdin_fd) {
1278 to_subprocess[0] = rktio_fd_system_fd(rktio, stdin_fd);
1279 RKTIO_COPY_FOR_SUBPROCESS(to_subprocess, 0);
1280 } else if (rktio_make_os_pipe(rktio, to_subprocess, RKTIO_NO_INHERIT_OUTPUT)) {
1281 if (stdout_fd) { RKTIO_CLOSE_SUBPROCESS_COPY(from_subprocess, 1); }
1282 return NULL;
1283 }
1284
1285 if (stderr_fd) {
1286 err_subprocess[1] = rktio_fd_system_fd(rktio, stderr_fd);
1287 RKTIO_COPY_FOR_SUBPROCESS(err_subprocess, 1);
1288 } else if (stderr_is_stdout) {
1289 err_subprocess[0] = from_subprocess[0];
1290 err_subprocess[1] = from_subprocess[1];
1291 } else if (rktio_make_os_pipe(rktio, err_subprocess, RKTIO_NO_INHERIT_INPUT)) {
1292 if (stdout_fd) { RKTIO_CLOSE_SUBPROCESS_COPY(from_subprocess, 1); }
1293 if (stdin_fd) { RKTIO_CLOSE_SUBPROCESS_COPY(to_subprocess, 0); }
1294 return NULL;
1295 }
1296
1297 if (envvars)
1298 env = rktio_envvars_to_block(rktio, envvars);
1299 else
1300 env = NULL;
1301
1302 #if defined(RKTIO_SYSTEM_WINDOWS)
1303
1304 /*--------------------------------------*/
1305 /* Execute: Windows */
1306 /*--------------------------------------*/
1307
1308 /* Windows: quasi-stdin is locked, and we'll say it doesn't matter */
1309 fflush(stdin);
1310 fflush(stdout);
1311 fflush(stderr);
1312
1313 {
1314 char **new_argv;
1315
1316 if (!windows_exact_cmdline) {
1317 /* protect spaces, etc. in the arguments: */
1318 new_argv = malloc(sizeof(char *) * argc);
1319 for (i = 0; i < argc; i++) {
1320 new_argv[i] = cmdline_protect(argv[i]);
1321 }
1322 argv = (rktio_const_string_t *)new_argv;
1323 }
1324
1325 pid = 0;
1326
1327 spawn_status = do_spawnv(rktio,
1328 command, argc, (const char * const *)argv,
1329 windows_exact_cmdline,
1330 to_subprocess[0],
1331 from_subprocess[1],
1332 err_subprocess[1],
1333 &pid,
1334 new_process_group,
1335 windows_chain_termination_to_child,
1336 env, current_directory);
1337
1338 if (!windows_exact_cmdline) {
1339 for (i = 0; i < argc; i++) {
1340 free((char *)argv[i]);
1341 }
1342 free(argv);
1343 }
1344
1345 if (spawn_status != -1)
1346 sc = (void *)spawn_status;
1347 }
1348
1349 #else
1350
1351
1352 /*--------------------------------------*/
1353 /* Execute: Unix */
1354 /*--------------------------------------*/
1355
1356 {
1357 #if defined(CENTRALIZED_SIGCHILD)
1358 centralized_starting_child();
1359 #else
1360 sc = malloc(sizeof(System_Child));
1361 sc->id = 0;
1362 sc->done = 0;
1363
1364 block_child_signals(rktio, 1);
1365
1366 /* Relies on signals blocked: */
1367 init_sigchld(rktio);
1368 #endif
1369
1370 close_after_len = rktio_close_fds_len();
1371
1372 /* add a NULL terminator */
1373 {
1374 int i;
1375 new_argv = malloc(sizeof(char *) * (argc + 1));
1376 for (i = 0; i < argc; i++) {
1377 new_argv[i] = argv[i];
1378 }
1379 new_argv[i] = NULL;
1380 }
1381
1382 #if defined(__QNX__)
1383 pid = vfork();
1384 #elif defined(SUBPROCESS_USE_FORK1)
1385 pid = fork1();
1386 #else
1387 pid = fork();
1388 #endif
1389
1390 if (pid > 0) {
1391 /* This is the original process, which needs to manage the
1392 newly created child process. */
1393
1394 if (new_process_group || group_proc) {
1395 /* there's a race condition between this use and the exec(),
1396 and there's a race condition between the other setpgid() in
1397 the child processand sending signals from the parent
1398 process; so, we set in both, and at least one will
1399 succeed; we could perform better error checking, since
1400 EACCES is the only expected error */
1401 int pgid = pid;
1402 if (group_proc)
1403 pgid = group_proc->pid;
1404 setpgid(pid, pgid); /* note: silent failure */
1405 }
1406
1407 #if defined(CENTRALIZED_SIGCHILD)
1408 {
1409 rktio_signal_handle_t *signal_fd;
1410 int status;
1411 signal_fd = rktio_get_signal_handle(rktio);
1412 centralized_register_child(pid, new_process_group || group_proc, signal_fd, &status);
1413
1414 /* printf("SUBPROCESS %i\n", pid); */
1415 }
1416 #else
1417 sc->next = rktio->system_children;
1418 rktio->system_children = sc;
1419 sc->id = pid;
1420 #endif
1421 } else if (!pid) {
1422 /* This is the new child process */
1423 if (new_process_group || group_proc) {
1424 /* see also setpgid above */
1425 int actual_pid = getpid();
1426 int pgid = actual_pid;
1427 if (group_proc)
1428 pgid = group_proc->pid;
1429 setpgid(actual_pid, pgid); /* note: silent failure */
1430 }
1431 } else {
1432 get_posix_error();
1433 }
1434
1435 #if !defined(CENTRALIZED_SIGCHILD)
1436 block_child_signals(rktio, 0);
1437 #else
1438 if (!pid)
1439 centralized_unblock_child_signal();
1440 else if (pid == -1)
1441 centralized_ended_child();
1442 #endif
1443 }
1444
1445 switch (pid)
1446 {
1447 case -1:
1448 /* Close all created descriptors */
1449 if (!stdin_fd) {
1450 rktio_reliably_close(to_subprocess[0]);
1451 rktio_reliably_close(to_subprocess[1]);
1452 } else {
1453 RKTIO_CLOSE_SUBPROCESS_COPY(to_subprocess, 0);
1454 }
1455 if (!stdout_fd) {
1456 rktio_reliably_close(from_subprocess[0]);
1457 rktio_reliably_close(from_subprocess[1]);
1458 } else {
1459 RKTIO_CLOSE_SUBPROCESS_COPY(from_subprocess, 1);
1460 }
1461 if (!stderr_fd) {
1462 if (!stderr_is_stdout) {
1463 rktio_reliably_close(err_subprocess[0]);
1464 rktio_reliably_close(err_subprocess[1]);
1465 }
1466 } else {
1467 RKTIO_CLOSE_SUBPROCESS_COPY(err_subprocess, 1);
1468 }
1469
1470 if (env)
1471 free(env);
1472 free(new_argv);
1473 #if !defined(CENTRALIZED_SIGCHILD)
1474 free(sc);
1475 #endif
1476
1477 return NULL;
1478
1479 case 0: /* child */
1480
1481 {
1482 int in_fd, out_fd, err_fd;
1483 in_fd = to_subprocess[0];
1484 out_fd = from_subprocess[1];
1485 err_fd = err_subprocess[1];
1486
1487 /* Make sure out/err descriptors don't get clobbered by moving
1488 them if they're occupying a file descriptor we need to move
1489 a different descriptor into. (Note that while it's unlikely
1490 that input/output file descriptors will be the same, it
1491 isn't impossible, so handle that, too.) */
1492 if ((err_fd == 0 && err_fd != in_fd)
1493 || (err_fd == 1 && err_fd != out_fd)) {
1494 int new_err_fd = 2;
1495 while (new_err_fd == in_fd || new_err_fd == out_fd) {
1496 new_err_fd++;
1497 }
1498 reliably_copy_or_move_fd(err_fd, new_err_fd, err_fd != in_fd);
1499 if (out_fd == err_fd) {
1500 out_fd = new_err_fd;
1501 }
1502 err_fd = new_err_fd;
1503 }
1504 if (out_fd == 0 && out_fd != in_fd) {
1505 int new_out_fd = 1;
1506 while (new_out_fd == in_fd || new_out_fd == err_fd) {
1507 new_out_fd++;
1508 }
1509 reliably_copy_or_move_fd(out_fd, new_out_fd, out_fd != in_fd);
1510 out_fd = new_out_fd;
1511 }
1512
1513 /* Assign new process stdin/stdout/stderr, closing old
1514 descriptors when not used by some other descriptor */
1515 reliably_copy_or_move_fd(in_fd, 0, in_fd != out_fd && in_fd != err_fd);
1516 reliably_copy_or_move_fd(out_fd, 1, out_fd > 0 && out_fd != err_fd);
1517 reliably_copy_or_move_fd(err_fd, 2, err_fd > 1);
1518
1519 /* Close unwanted descriptors */
1520 if (!stdin_fd) {
1521 close_non_standard_fd(to_subprocess[1]);
1522 }
1523 if (!stdout_fd) {
1524 close_non_standard_fd(from_subprocess[0]);
1525 }
1526 if (!stderr_fd && !stderr_is_stdout) {
1527 close_non_standard_fd(err_subprocess[0]);
1528 }
1529
1530 rktio_close_fds_after_fork(close_after_len, 0, 1, 2);
1531 }
1532
1533 rktio_restore_modified_signal_handlers();
1534
1535 /* Set real CWD: */
1536 if (!rktio_set_current_directory(rktio, current_directory)) {
1537 fprintf(stderr, "racket: chdir failed to: %s\n", current_directory);
1538 _exit(1);
1539 }
1540
1541 /* Exec new process */
1542
1543 {
1544 int err;
1545 char **use_env;
1546
1547 if (!env)
1548 use_env = rktio_get_environ_array();
1549 else
1550 use_env = env;
1551
1552 err = MSC_IZE(execve)(command, (char **)new_argv, use_env);
1553 if (err)
1554 err = errno;
1555
1556 if (env)
1557 free(env);
1558 free(new_argv);
1559
1560 /* If we get here it failed; give up */
1561
1562 fprintf(stderr, "exec failed (%s%serrno=%d)\n",
1563 strerror(err), "; ",
1564 err);
1565
1566 _exit(1);
1567 }
1568
1569 default: /* parent */
1570
1571 free(new_argv);
1572
1573 break;
1574 }
1575 #endif
1576
1577 /*--------------------------------------*/
1578 /* Close unneeded descriptors */
1579 /*--------------------------------------*/
1580
1581 if (env)
1582 free(env);
1583
1584 if (!stdin_fd)
1585 RKTIO_CLOSE(to_subprocess[0]);
1586 else
1587 RKTIO_CLOSE_SUBPROCESS_COPY(to_subprocess, 0);
1588
1589 if (!stdout_fd)
1590 RKTIO_CLOSE(from_subprocess[1]);
1591 else
1592 RKTIO_CLOSE_SUBPROCESS_COPY(from_subprocess, 1);
1593
1594 if (!stderr_fd) {
1595 if (!stderr_is_stdout)
1596 RKTIO_CLOSE(err_subprocess[1]);
1597 } else
1598 RKTIO_CLOSE_SUBPROCESS_COPY(err_subprocess, 1);
1599
1600 /*--------------------------------------*/
1601 /* Create new file-descriptor objects */
1602 /*--------------------------------------*/
1603
1604 result = malloc(sizeof(rktio_process_result_t));
1605
1606 if (!stdout_fd)
1607 result->stdout_fd = rktio_system_fd(rktio, from_subprocess[0], RKTIO_OPEN_READ);
1608 else
1609 result->stdout_fd = NULL;
1610 if (!stdin_fd)
1611 result->stdin_fd = rktio_system_fd(rktio, to_subprocess[1], RKTIO_OPEN_WRITE);
1612 else
1613 result->stdin_fd = NULL;
1614 if (!stderr_fd && !stderr_is_stdout)
1615 result->stderr_fd = rktio_system_fd(rktio, err_subprocess[0], RKTIO_OPEN_READ);
1616 else
1617 result->stderr_fd = NULL;
1618
1619 /*--------------------------------------*/
1620 /* Return result info */
1621 /*--------------------------------------*/
1622
1623 subproc = malloc(sizeof(rktio_process_t));
1624 memset(subproc, 0, sizeof(rktio_process_t));
1625 #if !defined(CENTRALIZED_SIGCHILD)
1626 subproc->handle = (void *)sc;
1627 #endif
1628 subproc->pid = pid;
1629 subproc->is_group = new_process_group;
1630 subproc->in_group = (new_process_group || group_proc);
1631
1632 result->process = subproc;
1633
1634 return result;
1635 }
1636
rktio_process_pid(rktio_t * rktio,rktio_process_t * sp)1637 int rktio_process_pid(rktio_t *rktio, rktio_process_t *sp)
1638 {
1639 return sp->pid;
1640 }
1641
1642 #ifdef RKTIO_SYSTEM_UNIX
reliably_copy_or_move_fd(int src_fd,int target_fd,int move)1643 static void reliably_copy_or_move_fd(int src_fd, int target_fd, int move)
1644 {
1645 if (src_fd != target_fd) {
1646 int errid;
1647 do {
1648 errid = MSC_IZE(dup2)(src_fd, target_fd);
1649 } while (errid == -1 && errno == EINTR);
1650 if (move) {
1651 rktio_reliably_close(src_fd);
1652 }
1653 }
1654 }
1655
close_non_standard_fd(int fd)1656 static void close_non_standard_fd(int fd)
1657 {
1658 if (fd != 0 && fd != 1 && fd != 2) {
1659 rktio_reliably_close(fd);
1660 }
1661 }
1662
rktio_close_fds_len()1663 int rktio_close_fds_len()
1664 {
1665 int i;
1666
1667 /* These functions are not async-signal safe, so use them before
1668 a fork: */
1669 # ifdef USE_ULIMIT
1670 i = ulimit(4, 0);
1671 # elif defined(__ANDROID__)
1672 i = sysconf(_SC_OPEN_MAX);
1673 # else
1674 i = getdtablesize();
1675 # endif
1676
1677 return i;
1678 }
1679
rktio_close_fds_after_fork(int i,int skip1,int skip2,int skip3)1680 void rktio_close_fds_after_fork(int i, int skip1, int skip2, int skip3)
1681 {
1682 /* incoming `i` should be the result of `rktio_close_fds_len` before
1683 a fork */
1684
1685 while (i--) {
1686 if ((i != skip1) && (i != skip2) && (i != skip3)) {
1687 rktio_reliably_close(i);
1688 }
1689 }
1690 }
1691 #endif
1692