"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.22/src/mx/child.c" (24 Feb 2021, 17914 Bytes) of package /linux/misc/s-nail-14.9.22.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "child.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 14.9.21_vs_14.9.22.

    1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
    2  *@ Implementation of child.h.
    3  *@ TODO . argument and environment space constraints not tested.
    4  *@ TODO . use a SU child, offer+use our own stuff for "wait status" checks.
    5  *@ TODO   (requires event loop then, likely).
    6  *@ TODO   Conditionally use waitid(2) instead of waitpid(2) (Joerg Schilling).
    7  *@ TODO . STDERR is always "passed", yet not taken care of regarding termios!
    8  *@ TODO . we would need full and true job control handling
    9  *@ TODO   But at least notion of background and foreground, see termios.c!
   10  *
   11  * Copyright (c) 2012 - 2020 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
   12  * SPDX-License-Identifier: ISC
   13  *
   14  * Permission to use, copy, modify, and/or distribute this software for any
   15  * purpose with or without fee is hereby granted, provided that the above
   16  * copyright notice and this permission notice appear in all copies.
   17  *
   18  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   19  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   20  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   21  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   22  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   23  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   24  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   25  */
   26 #undef su_FILE
   27 #define su_FILE child
   28 #define mx_SOURCE
   29 #define mx_SOURCE_CHILD
   30 
   31 #ifndef mx_HAVE_AMALGAMATION
   32 # include "mx/nail.h"
   33 #endif
   34 
   35 #include <sys/wait.h>
   36 
   37 #include <su/cs.h>
   38 #include <su/mem.h>
   39 
   40 #include "mx/cmd.h"
   41 #include "mx/file-streams.h"
   42 #include "mx/sigs.h"
   43 #include "mx/termcap.h"
   44 #include "mx/termios.h"
   45 
   46 #include "mx/child.h"
   47 #include "su/code-in.h"
   48 
   49 struct a_child_ent{
   50    struct a_child_ent *ce_link;
   51    s32 ce_pid; /* -1: struct can be gc'd */
   52    s32 ce_status; /* wait status */
   53    boole ce_done; /* has terminated */
   54    boole ce_forget; /* will not be wait()ed upon */
   55    boole ce_tios; /* Counts against child_termios_users */
   56    boole ce_tios_suspended; /* Suspended via TERMIOS */
   57    u8 ce__pad[4];
   58 };
   59 
   60 static struct a_child_ent *a_child_head;
   61 
   62 /* Cleanup internal structures which have been rendered obsolete (children have
   63  * terminated) in the meantime; returns list to be freed.
   64  * Note: signals including SIGCHLD need to be blocked when calling this */
   65 static struct a_child_ent *a_child_manager_cleanup(void);
   66 
   67 /* It or NIL is returned; if ceppp is set then it will point to the linked
   68  * storage of the return value: signals need to be blocked in this case! */
   69 SINLINE struct a_child_ent *a_child_find(s32 pid,
   70       struct a_child_ent ***ceppp_or_nil);
   71 
   72 /* Handle SIGCHLD */
   73 static void a_child__sigchld(int signo);
   74 
   75 /* Handle job control signals */
   76 static boole a_child__on_termios_state_change(up cookie, u32 tiossc, s32 sig);
   77 
   78 static struct a_child_ent *
   79 a_child_manager_cleanup(void){
   80    struct a_child_ent *nlp, **nlpp, **cepp, *cep;
   81    NYD_IN;
   82 
   83    nlp = NIL;
   84    nlpp = &nlp;
   85 
   86    for(cepp = &a_child_head; *cepp != NIL;){
   87       if((*cepp)->ce_pid == -1){
   88          cep = *cepp;
   89          *cepp = cep->ce_link;
   90 
   91          *nlpp = cep;
   92          nlpp = &cep->ce_link;
   93       }else
   94          cepp = &(*cepp)->ce_link;
   95    }
   96    NYD_OU;
   97    return nlp;
   98 }
   99 
  100 SINLINE struct a_child_ent *
  101 a_child_find(s32 pid, struct a_child_ent ***ceppp_or_nil){
  102    struct a_child_ent **cepp, *cep;
  103    NYD2_IN;
  104 
  105    for(cepp = &a_child_head; (cep = *cepp) != NIL; cepp = &(*cepp)->ce_link)
  106       if(cep->ce_pid == pid)
  107          break;
  108 
  109    if(ceppp_or_nil != NIL)
  110       *ceppp_or_nil = cepp;
  111    NYD2_OU;
  112    return cep;
  113 }
  114 
  115 static void
  116 a_child__sigchld(int signo){
  117    struct a_child_ent *cep;
  118    int status;
  119    pid_t pid;
  120    UNUSED(signo);
  121 
  122    for(;;){
  123       pid = waitpid(-1, &status, WNOHANG);
  124       if(pid <= 0){
  125          if(pid == -1 && su_err_no() == su_ERR_INTR)
  126             continue;
  127          break;
  128       }
  129 
  130       if((cep = a_child_find(S(s32,pid), NIL)) != NIL){
  131          cep->ce_done = TRU1;
  132          cep->ce_status = status;
  133          if(cep->ce_forget)
  134             cep->ce_pid = -1;
  135       }
  136    }
  137 }
  138 
  139 static boole
  140 a_child__on_termios_state_change(up cookie, u32 tiossc, s32 sig){/* TODO bad */
  141    struct a_child_ent *cep;
  142 
  143    if((cep = a_child_find(S(s32,cookie), NIL)) != NIL){
  144       if(cep->ce_done)
  145          ;
  146       else if(tiossc & mx_TERMIOS_STATE_POP){
  147          /* TODO this is bad - we should have a reaper timer in the
  148           * TODO (yet non-existing) event loop and shut this thing down
  149           * TODO gracefully */
  150          n_err("Reaping child process %d\n", cep->ce_pid);
  151          cep->ce_tios = FAL0;
  152          kill(cep->ce_pid,
  153             (tiossc & mx_TERMIOS_STATE_SIGNAL ? sig : SIGTERM));
  154          /* C99 */{
  155             uz i;
  156 
  157             for(i = 0; i < 10; ++i){
  158                n_msleep(100, FAL0);
  159                if(cep->ce_done)
  160                   break;
  161             }
  162             if(!cep->ce_done)
  163                kill(cep->ce_pid, SIGKILL);
  164          }
  165       }else if(tiossc & mx_TERMIOS_STATE_SUSPEND){
  166          if(!cep->ce_tios_suspended){
  167             cep->ce_tios_suspended = TRU1;
  168             if(!(tiossc & mx_TERMIOS_STATE_SIGNAL)){
  169                int wstat;
  170                pid_t wpid;
  171 
  172                kill(cep->ce_pid, SIGTSTP);
  173                wpid = waitpid(cep->ce_pid, &wstat, WUNTRACED);
  174                UNUSED(wpid);
  175             }
  176          }
  177       }else if(tiossc & mx_TERMIOS_STATE_RESUME){
  178          if(cep->ce_tios_suspended){
  179             cep->ce_tios_suspended = FAL0;
  180             /* TODO Sigh.  We do not handle process groups and have a bg/fg
  181              * TODO notion, so we do handle the terminal even if kids have it.
  182              * TODO Since job control sigs are sent to all processes in
  183              * TODO a process group, we race with the child.
  184              * TODO Synchronize that is impossible; for now we and termios
  185              * TODO assume au */
  186             if(!(tiossc & mx_TERMIOS_STATE_SIGNAL))
  187                kill(cep->ce_pid, SIGCONT);
  188          }
  189       }
  190    }
  191 
  192    return FAL0;
  193 }
  194 
  195 void
  196 mx_child_controller_setup(void){
  197    struct sigaction nact, oact;
  198    NYD_IN;
  199 
  200    nact.sa_handler = &a_child__sigchld;
  201    sigemptyset(&nact.sa_mask);
  202    nact.sa_flags = SA_RESTART
  203 #ifdef SA_NOCLDSTOP
  204          | SA_NOCLDSTOP
  205 #endif
  206          ;
  207 
  208    if(sigaction(SIGCHLD, &nact, &oact) != 0)
  209       n_panic(_("Cannot install signal handler for child process controller"));
  210    NYD_OU;
  211 }
  212 
  213 void
  214 mx_child_ctx_setup(struct mx_child_ctx *ccp){
  215    NYD2_IN;
  216    ASSERT(ccp);
  217 
  218    su_mem_set(ccp, 0, sizeof *ccp);
  219    ccp->cc_fds[0] = ccp->cc_fds[1] = mx_CHILD_FD_PASS;
  220    NYD2_OU;
  221 }
  222 
  223 boole
  224 mx_child_run(struct mx_child_ctx *ccp){
  225    s32 e;
  226    NYD_IN;
  227 
  228    ASSERT(ccp);
  229    ASSERT(ccp->cc_pid == 0);
  230    ASSERT(!(ccp->cc_flags & mx_CHILD_SPAWN_CONTROL_LINGER) ||
  231       (ccp->cc_flags & mx_CHILD_SPAWN_CONTROL));
  232 
  233    e = su_ERR_NONE;
  234 
  235    if(!mx_child_fork(ccp))
  236       e = ccp->cc_error;
  237    else if(ccp->cc_pid == 0)
  238       goto jchild;
  239    else if(ccp->cc_flags & mx_CHILD_RUN_WAIT_LIFE){
  240       if(!mx_child_wait(ccp))
  241          e = ccp->cc_error;
  242 
  243       if((e != su_ERR_NONE || ccp->cc_exit_status < 0) &&
  244             (ok_blook(bsdcompat) || ok_blook(bsdmsgs)))
  245          n_err(_("Fatal error in process\n"));
  246    }
  247 
  248    if(e != su_ERR_NONE){
  249       n_perr(_("child_run()"), e);
  250       su_err_set_no(ccp->cc_error = e);
  251    }
  252 
  253    NYD_OU;
  254    return (e == su_ERR_NONE);
  255 
  256 jchild:{
  257    char *argv[128 + 4]; /* TODO magic constant, fixed size -> su_vector */
  258    int i;
  259 
  260    if(ccp->cc_env_addon != NIL){
  261       extern char **environ;
  262       uz ei, ei_orig, ai, ai_orig;
  263       char **env;
  264       char const **env_addon;
  265 
  266       env_addon = ccp->cc_env_addon;
  267 
  268       /* TODO note we don't check the POSIX limit:
  269        * the total space used to store the environment and the arguments to
  270        * the process is limited to {ARG_MAX} bytes */
  271       for(ei = 0; environ[ei] != NIL; ++ei)
  272          ;
  273       ei_orig = ei;
  274       for(ai = 0; env_addon[ai] != NIL; ++ai)
  275          ;
  276       ai_orig = ai;
  277       env = n_lofi_alloc(sizeof(*env) * (ei + ai +1));
  278       su_mem_copy(env, environ, sizeof(*env) * ei);
  279 
  280       /* Replace all those keys that yet exist */
  281       while(ai-- > 0){
  282          char const *ee, *kvs;
  283          uz kl;
  284 
  285          ee = env_addon[ai];
  286          kvs = su_cs_find_c(ee, '=');
  287          ASSERT(kvs != NIL);
  288          kl = P2UZ(kvs - ee);
  289          ASSERT(kl > 0);
  290          for(ei = ei_orig; ei-- > 0;){
  291             char const *ekvs;
  292 
  293             if((ekvs = su_cs_find_c(env[ei], '=')) != NIL &&
  294                   kl == P2UZ(ekvs - env[ei]) && !su_mem_cmp(ee, env[ei], kl)){
  295                env[ei] = UNCONST(char*,ee);
  296                env_addon[ai] = NIL;
  297                break;
  298             }
  299          }
  300       }
  301 
  302       /* And append the rest */
  303       for(ei = ei_orig, ai = ai_orig; ai-- > 0;)
  304          if(env_addon[ai] != NIL)
  305             env[ei++] = UNCONST(char*,env_addon[ai]);
  306 
  307       env[ei] = NIL;
  308       environ = env;
  309    }
  310 
  311    i = (int)getrawlist(TRU1, argv, NELEM(argv) - 4, ccp->cc_cmd,
  312          su_cs_len(ccp->cc_cmd));
  313    if(i >= 0){
  314       if((argv[i++] = UNCONST(char*,ccp->cc_args[0])) != NIL &&
  315             (argv[i++] = UNCONST(char*,ccp->cc_args[1])) != NIL &&
  316             (argv[i++] = UNCONST(char*,ccp->cc_args[2])) != NIL)
  317          argv[i] = NIL;
  318 
  319       mx_child_in_child_setup(ccp);
  320 
  321       execvp(argv[0], argv);
  322       perror(argv[0]);
  323    }
  324    for(;;)
  325       _exit(n_EXIT_ERR);
  326    }
  327 }
  328 
  329 boole
  330 mx_child_fork(struct mx_child_ctx *ccp){
  331    struct a_child_ent *nlp, *cep;
  332    NYD_IN;
  333 
  334    ASSERT(ccp);
  335    ASSERT(ccp->cc_pid == 0);
  336    ASSERT(!(ccp->cc_flags & mx_CHILD_SPAWN_CONTROL_LINGER) ||
  337       (ccp->cc_flags & mx_CHILD_SPAWN_CONTROL));
  338    ASSERT(ccp->cc_error == su_ERR_NONE);
  339 
  340    if(n_poption & n_PO_D_VV)
  341       n_err(_("Forking child%s: %s %s %s %s\n"),
  342          (ccp->cc_flags & mx_CHILD_SPAWN_CONTROL ? _(" with spawn control")
  343             : su_empty),
  344          n_shexp_quote_cp(((ccp->cc_cmd != R(char*,-1)
  345             ? (ccp->cc_cmd != NIL ? ccp->cc_cmd : _("exec handled by caller"))
  346             : _("forked concurrent \"in-image\" code"))), FAL0),
  347          (ccp->cc_args[0] != NIL ? n_shexp_quote_cp(ccp->cc_args[0], FAL0)
  348             : su_empty),
  349          (ccp->cc_args[1] != NIL ? n_shexp_quote_cp(ccp->cc_args[1], FAL0)
  350             : su_empty),
  351          (ccp->cc_args[2] != NIL ? n_shexp_quote_cp(ccp->cc_args[2], FAL0)
  352             : su_empty));
  353 
  354    if((ccp->cc_flags & mx_CHILD_SPAWN_CONTROL) &&
  355          !mx_fs_pipe_cloexec(&ccp->cc__cpipe[0])){
  356       ccp->cc_error = su_err_no();
  357       goto jleave;
  358    }
  359 
  360    cep = su_TCALLOC(struct a_child_ent, 1);
  361 
  362    /* Does this child take the terminal? */
  363    if(ccp->cc_fds[0] == mx_CHILD_FD_PASS ||
  364          ccp->cc_fds[1] == mx_CHILD_FD_PASS){
  365       /* We strip that in started children.. */
  366       if(n_psonce & n_PSO_TTYANY){
  367          ccp->cc_flags |= mx__CHILD_JOBCTL;
  368          cep->ce_tios = TRU1;
  369       }
  370    }
  371 
  372    /* TODO It is actually very bad to block all the signals for such a long
  373     * TODO time, especially when taking into account on what is done in here */
  374    mx_sigs_all_hold(SIGCHLD, 0);
  375 
  376    nlp = a_child_manager_cleanup();
  377 
  378    /* If this child takes the terminal, adjust termios now, but do not yet
  379     * install our handler */
  380    if(cep->ce_tios)
  381       mx_termios_cmdx(mx_TERMIOS_CMD_PUSH | mx_TERMIOS_CMD_HANDS_OFF);
  382 
  383    switch((ccp->cc_pid = cep->ce_pid = fork())){
  384    case 0:
  385       goto jkid;
  386    case -1:
  387       ccp->cc_error = su_err_no();
  388 
  389       /* Link in cleanup list on failure */
  390       cep->ce_link = nlp;
  391       nlp = cep;
  392 
  393       /* And shutdown our termios environment */
  394       if(cep->ce_tios)
  395          mx_termios_cmdx(mx_TERMIOS_CMD_POP | mx_TERMIOS_CMD_HANDS_OFF);
  396 
  397       mx_sigs_all_rele();
  398 
  399       if(ccp->cc_flags & mx_CHILD_SPAWN_CONTROL){
  400          close(S(int,ccp->cc__cpipe[0]));
  401          close(S(int,ccp->cc__cpipe[1]));
  402       }
  403       n_perr(_("child_fork(): fork failure"), ccp->cc_error);
  404       break;
  405    default:
  406       /* In the parent, conditionally wait on the control pipe */
  407       if(ccp->cc_flags & mx_CHILD_SPAWN_CONTROL){
  408          char ebuf[sizeof(ccp->cc_error)];
  409          sz r;
  410 
  411          close(S(int,ccp->cc__cpipe[1]));
  412          r = read(S(int,ccp->cc__cpipe[0]), ebuf, sizeof ebuf);
  413          close(S(int,ccp->cc__cpipe[0]));
  414 
  415          switch(r){
  416          case 0:
  417             goto jlink_child;
  418          case sizeof(ccp->cc_error):
  419             su_mem_copy(&ccp->cc_error, ebuf, sizeof(ccp->cc_error));
  420             break;
  421          default:
  422             ccp->cc_error = su_ERR_CHILD;
  423             break;
  424          }
  425 
  426          /* Link in cleanup list on failure */
  427          cep->ce_link = nlp;
  428          nlp = cep;
  429 
  430          /* And shutdown our termios environment */
  431          if(cep->ce_tios)
  432             mx_termios_cmdx(mx_TERMIOS_CMD_POP | mx_TERMIOS_CMD_HANDS_OFF);
  433       }else{
  434 jlink_child:
  435          cep->ce_link = a_child_head;
  436          a_child_head = cep;
  437 
  438          /* Time to install our termios handler */
  439          if(cep->ce_tios)
  440             mx_termios_on_state_change_set(&a_child__on_termios_state_change,
  441                cep->ce_pid);
  442       }
  443 
  444       /* in_child_setup() will procmask() for the child */
  445       mx_sigs_all_rele();
  446       break;
  447    }
  448 
  449    /* Free all stale children */
  450    while(nlp != NIL){
  451       cep = nlp;
  452       nlp = nlp->ce_link;
  453       su_FREE(cep);
  454    }
  455 
  456 jleave:
  457    NYD_OU;
  458    return (ccp->cc_error == su_ERR_NONE);
  459 
  460 jkid:
  461    a_child_head = NIL;
  462    /* Strip tty bits, our children will not care from our point of view */
  463    n_psonce &= ~(n_PSO_TTYANY | n_PSO_INTERACTIVE);
  464 
  465    /* Close the unused end of the control pipe right now */
  466    if(ccp->cc_flags & mx_CHILD_SPAWN_CONTROL)
  467       close(S(int,ccp->cc__cpipe[0]));
  468    goto jleave;
  469 }
  470 
  471 void
  472 mx_child_in_child_setup(struct mx_child_ctx *ccp){
  473    sigset_t fset;
  474    s32 fd, i;
  475    ASSERT(ccp);
  476    ASSERT(ccp->cc_pid == 0);
  477 
  478    /* All file descriptors other than 0, 1, and 2 are supposed to be cloexec */
  479    /* TODO WHAT IS WITH STDERR_FILENO DAMMIT? */
  480    if((i = ((fd = S(s32,ccp->cc_fds[0])) == mx_CHILD_FD_NULL)))
  481       ccp->cc_fds[0] = fd = open(n_path_devnull, O_RDONLY);
  482    if(fd >= 0){
  483       dup2(fd, STDIN_FILENO);
  484       if(i)
  485          close(fd);
  486    }
  487 
  488    if((i = ((fd = S(s32,ccp->cc_fds[1])) == mx_CHILD_FD_NULL)))
  489       ccp->cc_fds[1] = fd = open(n_path_devnull, O_WRONLY);
  490    if(fd >= 0){
  491       dup2(fd, STDOUT_FILENO);
  492       if(i)
  493          close(fd);
  494    }
  495 
  496    /* Close our side of the control pipe, unless we should wait for cloexec to
  497     * do that for us */
  498    if((ccp->cc_flags & (mx_CHILD_SPAWN_CONTROL | mx_CHILD_SPAWN_CONTROL_LINGER)
  499          ) == mx_CHILD_SPAWN_CONTROL)
  500       close(S(int,ccp->cc__cpipe[1]));
  501 
  502    if(ccp->cc_mask != NIL){
  503       sigset_t *ssp;
  504 
  505       ssp = S(sigset_t*,ccp->cc_mask);
  506       for(i = 1; i < NSIG; ++i)
  507          if(sigismember(ssp, i))
  508             safe_signal(i, SIG_IGN);
  509       if(!sigismember(ssp, SIGINT))
  510          safe_signal(SIGINT, SIG_DFL);
  511    }
  512 
  513    sigemptyset(&fset);
  514    sigprocmask(SIG_SETMASK, &fset, NIL);
  515 }
  516 
  517 void
  518 mx_child_in_child_exec_failed(struct mx_child_ctx *ccp, s32 err){
  519    ASSERT(ccp);
  520    ASSERT(ccp->cc_pid == 0);
  521    ASSERT(ccp->cc_flags & mx_CHILD_SPAWN_CONTROL_LINGER);
  522 
  523    ccp->cc_error = err;
  524    (void)write(S(int,ccp->cc__cpipe[1]), &ccp->cc_error,
  525       sizeof(ccp->cc_error));
  526    close(S(int,ccp->cc__cpipe[1]));
  527 }
  528 
  529 s32
  530 mx_child_signal(struct mx_child_ctx *ccp, s32 sig){
  531    s32 rv;
  532    struct a_child_ent *cep;
  533    NYD_IN;
  534 
  535    ASSERT(ccp);
  536    ASSERT(ccp->cc_pid > 0);
  537 
  538    if((cep = a_child_find(ccp->cc_pid, NIL)) != NIL){
  539       ASSERT(!cep->ce_forget);
  540       if(cep->ce_done)
  541          rv = -1;
  542       else if((rv = kill(S(pid_t,ccp->cc_pid), sig)) != 0)
  543          rv = su_err_no();
  544    }else
  545       ccp->cc_pid = rv = -1;
  546 
  547    NYD_OU;
  548    return rv;
  549 }
  550 
  551 void
  552 mx_child_forget(struct mx_child_ctx *ccp){
  553    struct a_child_ent *cep, **cepp;
  554    NYD_IN;
  555 
  556    ASSERT(ccp);
  557    ASSERT(ccp->cc_pid > 0);
  558 
  559    mx_sigs_all_hold(SIGCHLD, 0);
  560 
  561    if((cep = a_child_find(ccp->cc_pid, &cepp)) != NIL){
  562       /* XXX ASSERT sigprocmask blocked, need debug wrapper */
  563       ASSERT(!cep->ce_forget);
  564       ASSERT(!(ccp->cc_flags & mx__CHILD_JOBCTL));  ASSERT(!cep->ce_tios);
  565       cep->ce_forget = TRU1;
  566       if(cep->ce_done)
  567          *cepp = cep->ce_link;
  568    }
  569 
  570    mx_sigs_all_rele();
  571 
  572    if(cep != NIL && cep->ce_done)
  573       su_FREE(cep);
  574 
  575    ccp->cc_pid = -1;
  576    NYD_OU;
  577 }
  578 
  579 boole
  580 mx_child_wait(struct mx_child_ctx *ccp){
  581    s32 ws;
  582    boole ok;
  583    struct a_child_ent *cep, **cepp;
  584    NYD_IN;
  585 
  586    ASSERT(ccp);
  587    ASSERT(ccp->cc_pid > 0);
  588 
  589    /* TODO Unless we place children which take the terminal in their own
  590     * TODO thing (setsid();setlogin();ioctl(fd, TIOCSCTTY)), that is
  591     * TODO CHLD:setpgid(0,0); PAREN:setpgid(CHILD,0),
  592     * TODO tcsetpgrp(STDIN_FILENO,CHILD)
  593     * TODO We need to ensure job control signals get through */
  594    mx_sigs_all_hold(SIGCHLD, -SIGTSTP, -SIGTTIN, -SIGTTOU, 0);
  595 
  596    ok = TRU1;
  597    if((cep = a_child_find(ccp->cc_pid, &cepp)) != NIL){
  598       /* XXX It would actually be better to sigsuspend on SIGCHLD */
  599       while(!cep->ce_done &&
  600             waitpid(S(pid_t,ccp->cc_pid), &cep->ce_status, 0) == -1){
  601          if((ws = su_err_no()) != su_ERR_INTR){
  602             ok = FAL0;
  603             break;
  604          }
  605       }
  606 
  607       ws = cep->ce_status;
  608       *cepp = cep->ce_link;
  609 
  610       /* This must be the one which holds that level, no? */
  611       if(cep->ce_tios)
  612          mx_termios_cmdx(mx_TERMIOS_CMD_POP | mx_TERMIOS_CMD_HANDS_OFF);
  613    }else{
  614       cepp = R(struct a_child_ent**,-1);
  615       ws = 0;
  616    }
  617 
  618    mx_sigs_all_rele();
  619 
  620    if(cep != NIL)
  621       su_FREE(cep);
  622 
  623    if(ok){
  624       ccp->cc_exit_status = WEXITSTATUS(ws);
  625       if(!WIFEXITED(ws) && ccp->cc_exit_status > 0)
  626          ccp->cc_exit_status = -ccp->cc_exit_status;
  627    }else{
  628       cep = NIL;
  629       ccp->cc_exit_status = -255;
  630    }
  631 
  632    ccp->cc_pid = -1;
  633    NYD_OU;
  634    return (cep != NIL);
  635 }
  636 
  637 #include "su/code-ou.h"
  638 /* s-it-mode */