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