"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.10/popen.c" (25 Mar 2018, 35061 Bytes) of package /linux/misc/s-nail-14.9.10.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 "popen.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 14.9.9_vs_14.9.10.

    1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
    2  *@ Handling of pipes, child processes, temporary files, file enwrapping.
    3  *
    4  * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
    5  * Copyright (c) 2012 - 2018 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
    6  */
    7 /*
    8  * Copyright (c) 1980, 1993
    9  *      The Regents of the University of California.  All rights reserved.
   10  *
   11  * Redistribution and use in source and binary forms, with or without
   12  * modification, are permitted provided that the following conditions
   13  * are met:
   14  * 1. Redistributions of source code must retain the above copyright
   15  *    notice, this list of conditions and the following disclaimer.
   16  * 2. Redistributions in binary form must reproduce the above copyright
   17  *    notice, this list of conditions and the following disclaimer in the
   18  *    documentation and/or other materials provided with the distribution.
   19  * 3. Neither the name of the University nor the names of its contributors
   20  *    may be used to endorse or promote products derived from this software
   21  *    without specific prior written permission.
   22  *
   23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
   24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   33  * SUCH DAMAGE.
   34  */
   35 #undef n_FILE
   36 #define n_FILE popen
   37 
   38 #ifndef HAVE_AMALGAMATION
   39 # include "nail.h"
   40 #endif
   41 
   42 #define READ               0
   43 #define WRITE              1
   44 
   45 struct fp {
   46    struct fp   *link;
   47    int         omode;
   48    int         pid;
   49    enum {
   50       FP_RAW,
   51 
   52 FP_IMAP = 1u<<3,
   53       FP_MAILDIR = 1u<<4,
   54       FP_HOOK = 1u<<5,
   55       FP_PIPE = 1u<<6,
   56       FP_MASK = (1u<<7) - 1,
   57       /* TODO FP_UNLINK: should be in a separated process so that unlinking
   58        * TODO the temporary "garbage" is "safe"(r than it is like that) */
   59       FP_UNLINK = 1u<<9,
   60       FP_TERMIOS = 1u<<10
   61    }           flags;
   62    long        offset;
   63    FILE        *fp;
   64    char        *realfile;
   65    char        *save_cmd;
   66    struct termios *fp_tios;
   67    n_sighdl_t fp_osigint;     /* Only if FP_TERMIOS */
   68 };
   69 
   70 struct child {
   71    int         pid;
   72    char        done;
   73    char        free;
   74    int         status;
   75    struct child *link;
   76 };
   77 
   78 static struct fp     *fp_head;
   79 static struct child  *_popen_child;
   80 
   81 /* TODO Rather temporary: deal with job control with FD_PASS */
   82 static struct termios a_popen_tios;
   83 static sighandler_type a_popen_otstp, a_popen_ottin, a_popen_ottou;
   84 static volatile int a_popen_hadsig;
   85 
   86 static int a_popen_scan_mode(char const *mode, int *omode);
   87 static void          register_file(FILE *fp, int omode, int pid,
   88                         int flags, char const *realfile, long offset,
   89                         char const *save_cmd, struct termios *tiosp,
   90                         n_sighdl_t osigint);
   91 static enum okay _file_save(struct fp *fpp);
   92 static int a_popen_file_load(int flags, int infd, int outfd,
   93             char const *load_cmd);
   94 static enum okay     unregister_file(FILE *fp, struct termios **tiosp,
   95                         n_sighdl_t *osigint);
   96 static int           file_pid(FILE *fp);
   97 
   98 /* TODO Rather temporary: deal with job control with FD_PASS */
   99 static void a_popen_jobsigs_up(void);
  100 static void a_popen_jobsigs_down(void);
  101 static void a_popen_jobsig(int sig);
  102 
  103 /* Handle SIGCHLD */
  104 static void a_popen_sigchld(int signo);
  105 
  106 static struct child *a_popen_child_find(int pid, bool_t create);
  107 static void a_popen_child_del(struct child *cp);
  108 
  109 static int
  110 a_popen_scan_mode(char const *mode, int *omode){
  111    static struct{
  112       char const mode[4];
  113       int omode;
  114    } const maps[] = {
  115       {"r", O_RDONLY},
  116       {"w", O_WRONLY | O_CREAT | n_O_NOXY_BITS | O_TRUNC},
  117       {"wx", O_WRONLY | O_CREAT | O_EXCL},
  118       {"a", O_WRONLY | O_APPEND | O_CREAT | n_O_NOXY_BITS},
  119       {"a+", O_RDWR | O_APPEND | O_CREAT | n_O_NOXY_BITS},
  120       {"r+", O_RDWR},
  121       {"w+", O_RDWR | O_CREAT | O_EXCL}
  122    };
  123    int i;
  124    NYD2_ENTER;
  125 
  126    for(i = 0; UICMP(z, i, <, n_NELEM(maps)); ++i)
  127       if(!strcmp(maps[i].mode, mode)){
  128          *omode = maps[i].omode;
  129          i = 0;
  130          goto jleave;
  131       }
  132 
  133    DBG( n_alert(_("Internal error: bad stdio open mode %s"), mode); )
  134    n_err_no = n_ERR_INVAL;
  135    *omode = 0; /* (silence CC) */
  136    i = -1;
  137 jleave:
  138    NYD2_LEAVE;
  139    return i;
  140 }
  141 
  142 static void
  143 register_file(FILE *fp, int omode, int pid, int flags,
  144    char const *realfile, long offset, char const *save_cmd,
  145    struct termios *tiosp, n_sighdl_t osigint)
  146 {
  147    struct fp *fpp;
  148    NYD_ENTER;
  149 
  150    assert(!(flags & FP_UNLINK) || realfile != NULL);
  151    assert(!(flags & FP_TERMIOS) || tiosp != NULL);
  152 
  153    fpp = smalloc(sizeof *fpp);
  154    fpp->fp = fp;
  155    fpp->omode = omode;
  156    fpp->pid = pid;
  157    fpp->link = fp_head;
  158    fpp->flags = flags;
  159    fpp->realfile = (realfile != NULL) ? sstrdup(realfile) : NULL;
  160    fpp->save_cmd = (save_cmd != NULL) ? sstrdup(save_cmd) : NULL;
  161    fpp->fp_tios = tiosp;
  162    fpp->fp_osigint = osigint;
  163    fpp->offset = offset;
  164    fp_head = fpp;
  165    NYD_LEAVE;
  166 }
  167 
  168 static enum okay
  169 _file_save(struct fp *fpp)
  170 {
  171    char const *cmd[3];
  172    int outfd;
  173    enum okay rv;
  174    NYD_ENTER;
  175 
  176    if (fpp->omode == O_RDONLY) {
  177       rv = OKAY;
  178       goto jleave;
  179    }
  180    rv = STOP;
  181 
  182    fflush(fpp->fp);
  183    clearerr(fpp->fp);
  184 
  185    /* Ensure the I/O library doesn't optimize the fseek(3) away! */
  186    if(!n_real_seek(fpp->fp, fpp->offset, SEEK_SET)){
  187       outfd = n_err_no;
  188       n_err(_("Fatal: cannot restore file position and save %s: %s\n"),
  189          n_shexp_quote_cp(fpp->realfile, FAL0), n_err_to_doc(outfd));
  190       goto jleave;
  191    }
  192 
  193 #ifdef HAVE_IMAP
  194    if ((fpp->flags & FP_MASK) == FP_IMAP) {
  195       rv = imap_append(fpp->realfile, fpp->fp, fpp->offset);
  196       goto jleave;
  197    }
  198 #endif
  199 
  200    if ((fpp->flags & FP_MASK) == FP_MAILDIR) {
  201       rv = maildir_append(fpp->realfile, fpp->fp, fpp->offset);
  202       goto jleave;
  203    }
  204 
  205    outfd = open(fpp->realfile,
  206          ((fpp->omode | O_CREAT | (fpp->omode & O_APPEND ? 0 : O_TRUNC) |
  207             n_O_NOXY_BITS) & ~O_EXCL), 0666);
  208    if (outfd == -1) {
  209       outfd = n_err_no;
  210       n_err(_("Fatal: cannot create %s: %s\n"),
  211          n_shexp_quote_cp(fpp->realfile, FAL0), n_err_to_doc(outfd));
  212       goto jleave;
  213    }
  214 
  215    cmd[2] = NULL;
  216    switch(fpp->flags & FP_MASK){
  217    case FP_HOOK:
  218       if(n_poption & n_PO_D_V)
  219          n_err(_("Using `filetype' handler %s to save %s\n"),
  220             n_shexp_quote_cp(fpp->save_cmd, FAL0),
  221             n_shexp_quote_cp(fpp->realfile, FAL0));
  222       cmd[0] = ok_vlook(SHELL);
  223       cmd[1] = "-c";
  224       cmd[2] = fpp->save_cmd;
  225       break;
  226    default:
  227       cmd[0] = "cat";
  228       cmd[1] = NULL;
  229       break;
  230    }
  231    if (n_child_run(cmd[0], 0, fileno(fpp->fp), outfd,
  232          cmd[1], cmd[2], NULL, NULL, NULL) >= 0)
  233       rv = OKAY;
  234 
  235    close(outfd);
  236 jleave:
  237    NYD_LEAVE;
  238    return rv;
  239 }
  240 
  241 static int
  242 a_popen_file_load(int flags, int infd, int outfd, char const *load_cmd){
  243    char const *cmd[3];
  244    int rv;
  245    NYD2_ENTER;
  246 
  247    cmd[2] = NULL;
  248    switch(flags & FP_MASK){
  249    case FP_IMAP:
  250    case FP_MAILDIR:
  251       rv = 0;
  252       goto jleave;
  253    case FP_HOOK:
  254       cmd[0] = ok_vlook(SHELL);
  255       cmd[1] = "-c";
  256       cmd[2] = load_cmd;
  257       break;
  258    default:
  259       cmd[0] = "cat";
  260       cmd[1] = NULL;
  261       break;
  262    }
  263 
  264    rv = n_child_run(cmd[0], 0, infd, outfd, cmd[1], cmd[2], NULL, NULL, NULL);
  265 jleave:
  266    NYD2_LEAVE;
  267    return rv;
  268 }
  269 
  270 static enum okay
  271 unregister_file(FILE *fp, struct termios **tiosp, n_sighdl_t *osigint)
  272 {
  273    struct fp **pp, *p;
  274    enum okay rv = OKAY;
  275    NYD_ENTER;
  276 
  277    if (tiosp)
  278       *tiosp = NULL;
  279 
  280    for (pp = &fp_head; (p = *pp) != NULL; pp = &p->link)
  281       if (p->fp == fp) {
  282          switch (p->flags & FP_MASK) {
  283          case FP_RAW:
  284          case FP_PIPE:
  285             break;
  286          default:
  287             rv = _file_save(p);
  288             break;
  289          }
  290          if ((p->flags & FP_UNLINK) && unlink(p->realfile))
  291             rv = STOP;
  292 
  293          *pp = p->link;
  294          if (p->save_cmd != NULL)
  295             free(p->save_cmd);
  296          if (p->realfile != NULL)
  297             free(p->realfile);
  298          if (p->flags & FP_TERMIOS) {
  299             if (tiosp != NULL) {
  300                *tiosp = p->fp_tios;
  301                *osigint = p->fp_osigint;
  302             } else
  303                free(p->fp_tios);
  304          }
  305          free(p);
  306          goto jleave;
  307       }
  308    DBGOR(n_panic, n_alert)(_("Invalid file pointer"));
  309    rv = STOP;
  310 jleave:
  311    NYD_LEAVE;
  312    return rv;
  313 }
  314 
  315 static int
  316 file_pid(FILE *fp)
  317 {
  318    int rv;
  319    struct fp *p;
  320    NYD2_ENTER;
  321 
  322    rv = -1;
  323    for (p = fp_head; p; p = p->link)
  324       if (p->fp == fp) {
  325          rv = p->pid;
  326          break;
  327       }
  328    NYD2_LEAVE;
  329    return rv;
  330 }
  331 
  332 static void
  333 a_popen_jobsigs_up(void){
  334    sigset_t nset, oset;
  335    NYD2_ENTER;
  336 
  337    sigfillset(&nset);
  338 
  339    sigprocmask(SIG_BLOCK, &nset, &oset);
  340    a_popen_otstp = safe_signal(SIGTSTP, &a_popen_jobsig);
  341    a_popen_ottin = safe_signal(SIGTTIN, &a_popen_jobsig);
  342    a_popen_ottou = safe_signal(SIGTTOU, &a_popen_jobsig);
  343 
  344    /* This assumes oset contains nothing but SIGCHLD, so to say */
  345    sigdelset(&oset, SIGTSTP);
  346    sigdelset(&oset, SIGTTIN);
  347    sigdelset(&oset, SIGTTOU);
  348    sigprocmask(SIG_SETMASK, &oset, NULL);
  349    NYD2_LEAVE;
  350 }
  351 
  352 static void
  353 a_popen_jobsigs_down(void){
  354    sigset_t nset, oset;
  355    NYD2_ENTER;
  356 
  357    sigfillset(&nset);
  358 
  359    sigprocmask(SIG_BLOCK, &nset, &oset);
  360    safe_signal(SIGTSTP, a_popen_otstp);
  361    safe_signal(SIGTTIN, a_popen_ottin);
  362    safe_signal(SIGTTOU, a_popen_ottou);
  363 
  364    sigaddset(&oset, SIGTSTP);
  365    sigaddset(&oset, SIGTTIN);
  366    sigaddset(&oset, SIGTTOU);
  367    sigprocmask(SIG_SETMASK, &oset, NULL);
  368    NYD2_LEAVE;
  369 }
  370 
  371 static void
  372 a_popen_jobsig(int sig){
  373    sighandler_type oldact;
  374    sigset_t nset;
  375    bool_t hadsig;
  376    NYD_X; /* Signal handler */
  377 
  378    hadsig = (a_popen_hadsig != 0);
  379    a_popen_hadsig = 1;
  380    if(!hadsig)
  381       n_TERMCAP_SUSPEND(TRU1);
  382 
  383    oldact = safe_signal(sig, SIG_DFL);
  384 
  385    sigemptyset(&nset);
  386    sigaddset(&nset, sig);
  387    sigprocmask(SIG_UNBLOCK, &nset, NULL);
  388    n_raise(sig);
  389    sigprocmask(SIG_BLOCK, &nset, NULL);
  390 
  391    safe_signal(sig, oldact);
  392 }
  393 
  394 static void
  395 a_popen_sigchld(int signo){
  396    pid_t pid;
  397    int status;
  398    struct child *cp;
  399    NYD_X; /* Signal handler */
  400    n_UNUSED(signo);
  401 
  402    for (;;) {
  403       pid = waitpid(-1, &status, WNOHANG);
  404       if (pid <= 0) {
  405          if (pid == -1 && n_err_no == n_ERR_INTR)
  406             continue;
  407          break;
  408       }
  409 
  410       if ((cp = a_popen_child_find(pid, FAL0)) != NULL) {
  411          cp->done = 1;
  412          if (cp->free)
  413             cp->pid = -1; /* XXX Was _delchild(cp);# */
  414          else {
  415             cp->status = status;
  416          }
  417       }
  418    }
  419 }
  420 
  421 static struct child *
  422 a_popen_child_find(int pid, bool_t create){
  423    struct child **cpp, *cp;
  424    NYD2_ENTER;
  425 
  426    for(cpp = &_popen_child; (cp = *cpp) != NULL && cp->pid != pid;
  427          cpp = &(*cpp)->link)
  428       ;
  429 
  430    if(cp == NULL && create)
  431       (*cpp = cp = scalloc(1, sizeof *cp))->pid = pid;
  432    NYD2_LEAVE;
  433    return cp;
  434 }
  435 
  436 static void
  437 a_popen_child_del(struct child *cp){
  438    struct child **cpp;
  439    NYD2_ENTER;
  440 
  441    cpp = &_popen_child;
  442 
  443    for(;;){
  444       if(*cpp == cp){
  445          *cpp = cp->link;
  446          free(cp);
  447          break;
  448       }
  449       if(*(cpp = &(*cpp)->link) == NULL){
  450          DBG( n_err("! a_popen_child_del(): implementation error\n"); )
  451          break;
  452       }
  453    }
  454    NYD2_LEAVE;
  455 }
  456 
  457 FL void
  458 n_child_manager_start(void)
  459 {
  460    struct sigaction nact, oact;
  461    NYD_ENTER;
  462 
  463    nact.sa_handler = &a_popen_sigchld;
  464    sigemptyset(&nact.sa_mask);
  465    nact.sa_flags = SA_RESTART
  466 #ifdef SA_NOCLDSTOP
  467          | SA_NOCLDSTOP
  468 #endif
  469          ;
  470    if (sigaction(SIGCHLD, &nact, &oact) != 0)
  471       n_panic(_("Cannot install signal handler for child process management"));
  472    NYD_LEAVE;
  473 }
  474 
  475 FL FILE *
  476 safe_fopen(char const *file, char const *oflags, int *xflags)
  477 {
  478    int osflags, fd;
  479    FILE *fp = NULL;
  480    NYD2_ENTER; /* (only for Fopen() and once in go.c) */
  481 
  482    if (a_popen_scan_mode(oflags, &osflags) < 0)
  483       goto jleave;
  484    osflags |= _O_CLOEXEC;
  485    if (xflags != NULL)
  486       *xflags = osflags;
  487 
  488    if ((fd = open(file, osflags, 0666)) == -1)
  489       goto jleave;
  490    _CLOEXEC_SET(fd);
  491 
  492    fp = fdopen(fd, oflags);
  493 jleave:
  494    NYD2_LEAVE;
  495    return fp;
  496 }
  497 
  498 FL FILE *
  499 Fopen(char const *file, char const *oflags)
  500 {
  501    FILE *fp;
  502    int osflags;
  503    NYD_ENTER;
  504 
  505    if ((fp = safe_fopen(file, oflags, &osflags)) != NULL)
  506       register_file(fp, osflags, 0, FP_RAW, NULL, 0L, NULL, NULL,NULL);
  507    NYD_LEAVE;
  508    return fp;
  509 }
  510 
  511 FL FILE *
  512 Fdopen(int fd, char const *oflags, bool_t nocloexec)
  513 {
  514    FILE *fp;
  515    int osflags;
  516    NYD_ENTER;
  517 
  518    a_popen_scan_mode(oflags, &osflags);
  519    if (!nocloexec)
  520       osflags |= _O_CLOEXEC; /* Ensured to be set by caller as documented! */
  521 
  522    if ((fp = fdopen(fd, oflags)) != NULL)
  523       register_file(fp, osflags, 0, FP_RAW, NULL, 0L, NULL, NULL,NULL);
  524    NYD_LEAVE;
  525    return fp;
  526 }
  527 
  528 FL int
  529 Fclose(FILE *fp)
  530 {
  531    int i = 0;
  532    NYD_ENTER;
  533 
  534    if (unregister_file(fp, NULL, NULL) == OKAY)
  535       i |= 1;
  536    if (fclose(fp) == 0)
  537       i |= 2;
  538    NYD_LEAVE;
  539    return (i == 3 ? 0 : EOF);
  540 }
  541 
  542 FL FILE *
  543 n_fopen_any(char const *file, char const *oflags, /* TODO should take flags */
  544       enum n_fopen_state *fs_or_null){ /* TODO as bits, return state */
  545    /* TODO Support file locking upon open time */
  546    long offset;
  547    enum protocol p;
  548    enum oflags rof;
  549    int osflags, flags, omode, infd;
  550    char const *cload, *csave;
  551    enum n_fopen_state fs;
  552    FILE *rv;
  553    NYD_ENTER;
  554 
  555    rv = NULL;
  556    fs = n_FOPEN_STATE_NONE;
  557    cload = csave = NULL;
  558 
  559    if(a_popen_scan_mode(oflags, &osflags) < 0)
  560       goto jleave;
  561 
  562    flags = 0;
  563    rof = OF_RDWR | OF_UNLINK;
  564    if(osflags & O_APPEND)
  565       rof |= OF_APPEND;
  566    omode = (osflags == O_RDONLY) ? R_OK : R_OK | W_OK;
  567 
  568    /* We don't want to find mbox.bz2 when doing "copy * mbox", but only for
  569     * "file mbox", so don't try hooks when writing */
  570    p = which_protocol(csave = file, TRU1, ((omode & W_OK) == 0), &file);
  571    fs = (enum n_fopen_state)p;
  572    switch(p){
  573    default:
  574       goto jleave;
  575    case n_PROTO_IMAP:
  576 #ifdef HAVE_IMAP
  577       file = csave;
  578       flags |= FP_IMAP;
  579       osflags = O_RDWR | O_APPEND | O_CREAT | n_O_NOXY_BITS;
  580       infd = -1;
  581       break;
  582 #else
  583       n_err_no = n_ERR_OPNOTSUPP;
  584       goto jleave;
  585 #endif
  586    case n_PROTO_MAILDIR:
  587       if(fs_or_null != NULL && !access(file, F_OK))
  588          fs |= n_FOPEN_STATE_EXISTS;
  589       flags |= FP_MAILDIR;
  590       osflags = O_RDWR | O_APPEND | O_CREAT | n_O_NOXY_BITS;
  591       infd = -1;
  592       break;
  593    case n_PROTO_FILE:{
  594       struct n_file_type ft;
  595 
  596       if(!(osflags & O_EXCL) && fs_or_null != NULL && !access(file, F_OK))
  597          fs |= n_FOPEN_STATE_EXISTS;
  598 
  599       if(n_filetype_exists(&ft, file)){
  600          flags |= FP_HOOK;
  601          cload = ft.ft_load_dat;
  602          csave = ft.ft_save_dat;
  603          /* Cause truncation for compressor/hook output files */
  604          osflags &= ~O_APPEND;
  605          rof &= ~OF_APPEND;
  606          if((infd = open(file, (omode & W_OK ? O_RDWR : O_RDONLY))) != -1){
  607             fs |= n_FOPEN_STATE_EXISTS;
  608             if(n_poption & n_PO_D_V)
  609                n_err(_("Using `filetype' handler %s to load %s\n"),
  610                   n_shexp_quote_cp(cload, FAL0), n_shexp_quote_cp(file, FAL0));
  611          }else if(!(osflags & O_CREAT) || n_err_no != n_ERR_NOENT)
  612             goto jleave;
  613       }else{
  614          /*flags |= FP_RAW;*/
  615          rv = Fopen(file, oflags);
  616          if((osflags & O_EXCL) && rv == NULL)
  617             fs |= n_FOPEN_STATE_EXISTS;
  618          goto jleave;
  619       }
  620       }break;
  621    }
  622 
  623    /* Note rv is not yet register_file()d, fclose() it in error path! */
  624    if((rv = Ftmp(NULL, "fopenany", rof)) == NULL){
  625       n_perr(_("tmpfile"), 0);
  626       goto jerr;
  627    }
  628 
  629    if(flags & (FP_IMAP | FP_MAILDIR))
  630       ;
  631    else if(infd >= 0){
  632       if(a_popen_file_load(flags, infd, fileno(rv), cload) < 0){
  633 jerr:
  634          if(rv != NULL)
  635             fclose(rv);
  636          rv = NULL;
  637          if(infd >= 0)
  638             close(infd);
  639          goto jleave;
  640       }
  641    }else{
  642       if((infd = creat(file, 0666)) == -1){
  643          fclose(rv);
  644          rv = NULL;
  645          goto jleave;
  646       }
  647    }
  648 
  649    if(infd >= 0)
  650       close(infd);
  651    fflush(rv);
  652 
  653    if(!(osflags & O_APPEND))
  654       rewind(rv);
  655    if((offset = ftell(rv)) == -1){
  656       Fclose(rv);
  657       rv = NULL;
  658       goto jleave;
  659    }
  660 
  661    register_file(rv, osflags, 0, flags, file, offset, csave, NULL,NULL);
  662 jleave:
  663    if(fs_or_null != NULL)
  664       *fs_or_null = fs;
  665    NYD_LEAVE;
  666    return rv;
  667 }
  668 
  669 FL FILE *
  670 Ftmp(char **fn, char const *namehint, enum oflags oflags)
  671 {
  672    /* The 8 is arbitrary but leaves room for a six character suffix (the
  673     * POSIX minimum path length is 14, though we don't check that XXX).
  674     * 8 should be more than sufficient given that we use base64url encoding
  675     * for our random string */
  676    enum {_RANDCHARS = 8u};
  677 
  678    char *cp_base, *cp;
  679    size_t maxname, xlen, i;
  680    char const *tmpdir;
  681    int osoflags, fd, e;
  682    bool_t relesigs;
  683    FILE *fp;
  684    NYD_ENTER;
  685 
  686    assert(namehint != NULL);
  687    assert((oflags & OF_WRONLY) || (oflags & OF_RDWR));
  688    assert(!(oflags & OF_RDONLY));
  689    assert(!(oflags & OF_REGISTER_UNLINK) || (oflags & OF_REGISTER));
  690 
  691    fp = NULL;
  692    relesigs = FAL0;
  693    e = 0;
  694    tmpdir = ok_vlook(TMPDIR);
  695    maxname = NAME_MAX;
  696 #ifdef HAVE_PATHCONF
  697    {  long pc;
  698 
  699       if ((pc = pathconf(tmpdir, _PC_NAME_MAX)) != -1)
  700          maxname = (size_t)pc;
  701    }
  702 #endif
  703 
  704    if ((oflags & OF_SUFFIX) && *namehint != '\0') {
  705       if ((xlen = strlen(namehint)) > maxname - _RANDCHARS) {
  706          n_err_no = n_ERR_NAMETOOLONG;
  707          goto jleave;
  708       }
  709    } else
  710       xlen = 0;
  711 
  712    /* Prepare the template string once, then iterate over the random range */
  713    cp_base =
  714    cp = smalloc(strlen(tmpdir) + 1 + maxname +1);
  715    cp = sstpcpy(cp, tmpdir);
  716    *cp++ = '/';
  717    {
  718       char *x = sstpcpy(cp, VAL_UAGENT);
  719       *x++ = '-';
  720       if (!(oflags & OF_SUFFIX))
  721          x = sstpcpy(x, namehint);
  722 
  723       i = PTR2SIZE(x - cp);
  724       if (i > maxname - xlen - _RANDCHARS) {
  725          size_t j = maxname - xlen - _RANDCHARS;
  726          x -= i - j;
  727       }
  728 
  729       if ((oflags & OF_SUFFIX) && xlen > 0)
  730          memcpy(x + _RANDCHARS, namehint, xlen);
  731 
  732       x[xlen + _RANDCHARS] = '\0';
  733       cp = x;
  734    }
  735 
  736    osoflags = O_CREAT | O_EXCL | _O_CLOEXEC;
  737    osoflags |= (oflags & OF_WRONLY) ? O_WRONLY : O_RDWR;
  738    if (oflags & OF_APPEND)
  739       osoflags |= O_APPEND;
  740 
  741    for(relesigs = TRU1, i = 0;; ++i){
  742       memcpy(cp, n_random_create_cp(_RANDCHARS, NULL), _RANDCHARS);
  743 
  744       hold_all_sigs();
  745 
  746       if((fd = open(cp_base, osoflags, 0600)) != -1){
  747          _CLOEXEC_SET(fd);
  748          break;
  749       }
  750       if(i >= FTMP_OPEN_TRIES){
  751          e = n_err_no;
  752          goto jfree;
  753       }
  754       rele_all_sigs();
  755    }
  756 
  757    if (oflags & OF_REGISTER) {
  758       char const *osflags = (oflags & OF_RDWR ? "w+" : "w");
  759       int osflagbits;
  760 
  761       a_popen_scan_mode(osflags, &osflagbits); /* TODO osoflags&xy ?!!? */
  762       if ((fp = fdopen(fd, osflags)) != NULL)
  763          register_file(fp, osflagbits | _O_CLOEXEC, 0,
  764             (FP_RAW | (oflags & OF_REGISTER_UNLINK ? FP_UNLINK : 0)),
  765             cp_base, 0L, NULL, NULL,NULL);
  766    } else
  767       fp = fdopen(fd, (oflags & OF_RDWR ? "w+" : "w"));
  768 
  769    if (fp == NULL || (oflags & OF_UNLINK)) {
  770       e = n_err_no;
  771       unlink(cp_base);
  772       goto jfree;
  773    }else if(fp != NULL){
  774       /* We will succeed and keep the file around for further usage, likely
  775        * another stream will be opened for pure reading purposes (this is true
  776        * at the time of this writing.  A restrictive umask(2) settings may have
  777        * turned the path inaccessible, so ensure it may be read at least!
  778        * TODO once ok_vlook() can return an integer, look up *umask* first! */
  779       (void)fchmod(fd, S_IWUSR | S_IRUSR);
  780    }
  781 
  782    if (fn != NULL)
  783       *fn = cp_base;
  784    else
  785       free(cp_base);
  786 jleave:
  787    if (relesigs && (fp == NULL || !(oflags & OF_HOLDSIGS)))
  788       rele_all_sigs();
  789    if (fp == NULL)
  790       n_err_no = e;
  791    NYD_LEAVE;
  792    return fp;
  793 jfree:
  794    if ((cp = cp_base) != NULL)
  795       free(cp);
  796    goto jleave;
  797 }
  798 
  799 FL void
  800 Ftmp_release(char **fn)
  801 {
  802    char *cp;
  803    NYD_ENTER;
  804 
  805    cp = *fn;
  806    *fn = NULL;
  807    if (cp != NULL) {
  808       unlink(cp);
  809       rele_all_sigs();
  810       free(cp);
  811    }
  812    NYD_LEAVE;
  813 }
  814 
  815 FL void
  816 Ftmp_free(char **fn) /* TODO DROP: OF_REGISTER_FREEPATH! */
  817 {
  818    char *cp;
  819    NYD_ENTER;
  820 
  821    cp = *fn;
  822    *fn = NULL;
  823    if (cp != NULL)
  824       free(cp);
  825    NYD_LEAVE;
  826 }
  827 
  828 FL bool_t
  829 pipe_cloexec(int fd[2]){
  830    bool_t rv;
  831    NYD_ENTER;
  832 
  833    rv = FAL0;
  834 
  835 #ifdef HAVE_PIPE2
  836    if(pipe2(fd, O_CLOEXEC) != -1)
  837       rv = TRU1;
  838 #else
  839    if(pipe(fd) != -1){
  840       n_fd_cloexec_set(fd[0]);
  841       n_fd_cloexec_set(fd[1]);
  842       rv = TRU1;
  843    }
  844 #endif
  845    NYD_LEAVE;
  846    return rv;
  847 }
  848 
  849 FL FILE *
  850 Popen(char const *cmd, char const *mode, char const *sh,
  851    char const **env_addon, int newfd1)
  852 {
  853    int p[2], myside, hisside, fd0, fd1, pid;
  854    sigset_t nset;
  855    char mod[2];
  856    n_sighdl_t osigint;
  857    struct termios *tiosp;
  858    FILE *rv;
  859    NYD_ENTER;
  860 
  861    /* First clean up child structures */
  862    /* C99 */{
  863       struct child **cpp, *cp;
  864 
  865       hold_all_sigs();
  866       for (cpp = &_popen_child; *cpp != NULL;) {
  867          if ((*cpp)->pid == -1) {
  868             cp = *cpp;
  869             *cpp = cp->link;
  870             free(cp);
  871          } else
  872             cpp = &(*cpp)->link;
  873       }
  874       rele_all_sigs();
  875    }
  876 
  877    rv = NULL;
  878    tiosp = NULL;
  879    n_UNINIT(osigint, SIG_ERR);
  880    mod[0] = '0', mod[1] = '\0';
  881 
  882    if (!pipe_cloexec(p))
  883       goto jleave;
  884 
  885    if (*mode == 'r') {
  886       myside = p[READ];
  887       fd0 = n_CHILD_FD_PASS;
  888       hisside = fd1 = p[WRITE];
  889       mod[0] = *mode;
  890    } else if (*mode == 'W') {
  891       myside = p[WRITE];
  892       hisside = fd0 = p[READ];
  893       fd1 = newfd1;
  894       mod[0] = 'w';
  895    } else {
  896       myside = p[WRITE];
  897       hisside = fd0 = p[READ];
  898       fd1 = n_CHILD_FD_PASS;
  899       mod[0] = 'w';
  900    }
  901 
  902    /* In interactive mode both STDIN and STDOUT point to the terminal.  If we
  903     * pass through the TTY restore terminal attributes after pipe returns.
  904     * XXX It shouldn't matter which FD we actually use in this case */
  905    if ((n_psonce & n_PSO_INTERACTIVE) && (fd0 == n_CHILD_FD_PASS ||
  906          fd1 == n_CHILD_FD_PASS)) {
  907       osigint = n_signal(SIGINT, SIG_IGN);
  908       tiosp = smalloc(sizeof *tiosp);
  909       tcgetattr(STDIN_FILENO, tiosp);
  910       n_TERMCAP_SUSPEND(TRU1);
  911    }
  912 
  913    sigemptyset(&nset);
  914 
  915    if (cmd == (char*)-1) {
  916       if ((pid = n_child_fork()) == -1)
  917          n_perr(_("fork"), 0);
  918       else if (pid == 0) {
  919          union {char const *ccp; int (*ptf)(void); int es;} u;
  920          n_child_prepare(&nset, fd0, fd1);
  921          close(p[READ]);
  922          close(p[WRITE]);
  923          /* TODO should close all other open FDs except stds and reset memory */
  924          /* Standard I/O drives me insane!  All we need is a sync operation
  925           * that causes n_stdin to forget about any read buffer it may have.
  926           * We cannot use fflush(3), this works with Musl and Solaris, but not
  927           * with GlibC.  (For at least pipes.)  We cannot use fdreopen(),
  928           * because this function does not exist!  Luckily (!!!) we only use
  929           * n_stdin not stdin in our child, otherwise all bets were off!
  930           * TODO (Unless we would fiddle around with FILE* directly:
  931           * TODO #ifdef __GLIBC__
  932           * TODO   n_stdin->_IO_read_ptr = n_stdin->_IO_read_end;
  933           * TODO #elif *BSD*
  934           * TODO   n_stdin->_r = 0;
  935           * TODO #elif n_OS_SOLARIS || n_OS_SUNOS
  936           * TODO   n_stdin->_cnt = 0;
  937           * TODO #endif
  938           * TODO ) which should have additional config test for sure! */
  939          n_stdin = fdopen(STDIN_FILENO, "r");
  940          /*n_stdout = fdopen(STDOUT_FILENO, "w");*/
  941          /*n_stderr = fdopen(STDERR_FILENO, "w");*/
  942          u.ccp = sh;
  943          u.es = (*u.ptf)();
  944          /*fflush(NULL);*/
  945          _exit(u.es);
  946       }
  947    } else if (sh == NULL) {
  948       pid = n_child_start(cmd, &nset, fd0, fd1, NULL, NULL, NULL, env_addon);
  949    } else {
  950       pid = n_child_start(sh, &nset, fd0, fd1, "-c", cmd, NULL, env_addon);
  951    }
  952    if (pid < 0) {
  953       close(p[READ]);
  954       close(p[WRITE]);
  955       goto jleave;
  956    }
  957    close(hisside);
  958    if ((rv = fdopen(myside, mod)) != NULL)
  959       register_file(rv, 0, pid,
  960          (tiosp == NULL ? FP_PIPE : FP_PIPE | FP_TERMIOS),
  961          NULL, 0L, NULL, tiosp, osigint);
  962    else
  963       close(myside);
  964 jleave:
  965    if(rv == NULL && tiosp != NULL){
  966       n_TERMCAP_RESUME(TRU1);
  967       tcsetattr(STDIN_FILENO, TCSAFLUSH, tiosp);
  968       n_free(tiosp);
  969       n_signal(SIGINT, osigint);
  970    }
  971    NYD_LEAVE;
  972    return rv;
  973 }
  974 
  975 FL bool_t
  976 Pclose(FILE *ptr, bool_t dowait)
  977 {
  978    n_sighdl_t osigint;
  979    struct termios *tiosp;
  980    int pid;
  981    bool_t rv = FAL0;
  982    NYD_ENTER;
  983 
  984    pid = file_pid(ptr);
  985    if(pid < 0)
  986       goto jleave;
  987 
  988    unregister_file(ptr, &tiosp, &osigint);
  989    fclose(ptr);
  990 
  991    if(dowait){
  992       hold_all_sigs();
  993       rv = n_child_wait(pid, NULL);
  994       if(tiosp != NULL){
  995          n_TERMCAP_RESUME(TRU1);
  996          tcsetattr(STDIN_FILENO, TCSAFLUSH, tiosp);
  997          n_signal(SIGINT, osigint);
  998       }
  999       rele_all_sigs();
 1000    }else{
 1001       n_child_free(pid);
 1002       rv = TRU1;
 1003    }
 1004 
 1005    if(tiosp != NULL)
 1006       free(tiosp);
 1007 jleave:
 1008    NYD_LEAVE;
 1009    return rv;
 1010 }
 1011 
 1012 VL int
 1013 n_psignal(FILE *fp, int sig){
 1014    int rv;
 1015    NYD2_ENTER;
 1016 
 1017    if((rv = file_pid(fp)) >= 0){
 1018       struct child *cp;
 1019 
 1020       if((cp = a_popen_child_find(rv, FAL0)) != NULL){
 1021          if((rv = kill(rv, sig)) != 0)
 1022             rv = n_err_no;
 1023       }else
 1024          rv = -1;
 1025    }
 1026    NYD2_LEAVE;
 1027    return rv;
 1028 }
 1029 
 1030 FL FILE *
 1031 n_pager_open(void)
 1032 {
 1033    char const *env_add[2], *pager;
 1034    FILE *rv;
 1035    NYD_ENTER;
 1036 
 1037    assert(n_psonce & n_PSO_INTERACTIVE);
 1038 
 1039    pager = n_pager_get(env_add + 0);
 1040    env_add[1] = NULL;
 1041 
 1042    if ((rv = Popen(pager, "w", NULL, env_add, n_CHILD_FD_PASS)) == NULL)
 1043       n_perr(pager, 0);
 1044    NYD_LEAVE;
 1045    return rv;
 1046 }
 1047 
 1048 FL bool_t
 1049 n_pager_close(FILE *fp)
 1050 {
 1051    sighandler_type sh;
 1052    bool_t rv;
 1053    NYD_ENTER;
 1054 
 1055    sh = safe_signal(SIGPIPE, SIG_IGN);
 1056    rv = Pclose(fp, TRU1);
 1057    safe_signal(SIGPIPE, sh);
 1058    NYD_LEAVE;
 1059    return rv;
 1060 }
 1061 
 1062 FL void
 1063 close_all_files(void)
 1064 {
 1065    NYD_ENTER;
 1066    while (fp_head != NULL)
 1067       if ((fp_head->flags & FP_MASK) == FP_PIPE)
 1068          Pclose(fp_head->fp, TRU1);
 1069       else
 1070          Fclose(fp_head->fp);
 1071    NYD_LEAVE;
 1072 }
 1073 
 1074 /* TODO The entire n_child_ series should be replaced with an object, but
 1075  * TODO at least have carrier arguments.  We anyway need a command manager
 1076  * TODO that keeps track and knows how to handle job control ++++! */
 1077 
 1078 FL int
 1079 n_child_run(char const *cmd, sigset_t *mask_or_null, int infd, int outfd,
 1080    char const *a0_or_null, char const *a1_or_null, char const *a2_or_null,
 1081    char const **env_addon_or_null, int *wait_status_or_null)
 1082 {
 1083    sigset_t nset, oset;
 1084    sighandler_type soldint;
 1085    int rv, e;
 1086    enum {a_NONE = 0, a_INTIGN = 1<<0, a_TTY = 1<<1} f;
 1087    NYD_ENTER;
 1088 
 1089    f = a_NONE;
 1090    n_UNINIT(soldint, SIG_ERR);
 1091 
 1092    /* TODO Of course this is a joke given that during a "p*" the PAGER may
 1093     * TODO be up and running while we play around like this... but i guess
 1094     * TODO this can't be helped at all unless we perform complete and true
 1095     * TODO process group separation and ensure we don't deadlock us out
 1096     * TODO via TTY jobcontrol signal storms (could this really happen?).
 1097     * TODO Or have a built-in pager.  Or query any necessity BEFORE we start
 1098     * TODO any action, and shall we find we need to run programs dump it
 1099     * TODO all into a temporary file which is then passed through to the
 1100     * TODO PAGER.  Ugh.  That still won't help for "needsterminal" anyway */
 1101    if(infd == n_CHILD_FD_PASS || outfd == n_CHILD_FD_PASS){
 1102       soldint = safe_signal(SIGINT, SIG_IGN);
 1103       f = a_INTIGN;
 1104 
 1105       if(n_psonce & n_PSO_INTERACTIVE){
 1106          f |= a_TTY;
 1107          tcgetattr((n_psonce & n_PSO_TTYIN ? STDIN_FILENO : STDOUT_FILENO),
 1108             &a_popen_tios);
 1109          n_TERMCAP_SUSPEND(FAL0);
 1110          sigfillset(&nset);
 1111          sigdelset(&nset, SIGCHLD);
 1112          sigdelset(&nset, SIGINT);
 1113          /* sigdelset(&nset, SIGPIPE); TODO would need a handler */
 1114          sigprocmask(SIG_BLOCK, &nset, &oset);
 1115          a_popen_hadsig = 0;
 1116          a_popen_jobsigs_up();
 1117       }
 1118    }
 1119 
 1120    if((rv = n_child_start(cmd, mask_or_null, infd, outfd, a0_or_null,
 1121          a1_or_null, a2_or_null, env_addon_or_null)) < 0){
 1122       e = n_err_no;
 1123       rv = -1;
 1124    }else{
 1125       int ws;
 1126 
 1127       e = 0;
 1128       if(n_child_wait(rv, &ws))
 1129          rv = 0;
 1130       else if(wait_status_or_null == NULL || !WIFEXITED(ws)){
 1131          if(ok_blook(bsdcompat) || ok_blook(bsdmsgs))
 1132             n_err(_("Fatal error in process\n"));
 1133          e = n_ERR_CHILD;
 1134          rv = -1;
 1135       }
 1136       if(wait_status_or_null != NULL)
 1137          *wait_status_or_null = ws;
 1138    }
 1139 
 1140    if(f & a_TTY){
 1141       a_popen_jobsigs_down();
 1142       n_TERMCAP_RESUME(a_popen_hadsig ? TRU1 : FAL0);
 1143       tcsetattr(((n_psonce & n_PSO_TTYIN) ? STDIN_FILENO : STDOUT_FILENO),
 1144          ((n_psonce & n_PSO_TTYIN) ? TCSAFLUSH : TCSADRAIN), &a_popen_tios);
 1145       sigprocmask(SIG_SETMASK, &oset, NULL);
 1146    }
 1147    if(f & a_INTIGN){
 1148       if(soldint != SIG_IGN)
 1149          safe_signal(SIGINT, soldint);
 1150    }
 1151 
 1152    if(e != 0)
 1153       n_err_no = e;
 1154    NYD_LEAVE;
 1155    return rv;
 1156 }
 1157 
 1158 FL int
 1159 n_child_start(char const *cmd, sigset_t *mask_or_null, int infd, int outfd,
 1160    char const *a0_or_null, char const *a1_or_null, char const *a2_or_null,
 1161    char const **env_addon_or_null)
 1162 {
 1163    int rv, e;
 1164    NYD_ENTER;
 1165 
 1166    if ((rv = n_child_fork()) == -1) {
 1167       e = n_err_no;
 1168       n_perr(_("fork"), 0);
 1169       n_err_no = e;
 1170       rv = -1;
 1171    } else if (rv == 0) {
 1172       char *argv[128];
 1173       int i;
 1174 
 1175       if (env_addon_or_null != NULL) {
 1176          extern char **environ;
 1177          size_t ei, ei_orig, ai, ai_orig;
 1178          char **env;
 1179 
 1180          /* TODO note we don't check the POSIX limit:
 1181           * the total space used to store the environment and the arguments to
 1182           * the process is limited to {ARG_MAX} bytes */
 1183          for (ei = 0; environ[ei] != NULL; ++ei)
 1184             ;
 1185          ei_orig = ei;
 1186          for (ai = 0; env_addon_or_null[ai] != NULL; ++ai)
 1187             ;
 1188          ai_orig = ai;
 1189          env = ac_alloc(sizeof(*env) * (ei + ai +1));
 1190          memcpy(env, environ, sizeof(*env) * ei);
 1191 
 1192          /* Replace all those keys that yet exist */
 1193          while (ai-- > 0) {
 1194             char const *ee, *kvs;
 1195             size_t kl;
 1196 
 1197             ee = env_addon_or_null[ai];
 1198             kvs = strchr(ee, '=');
 1199             assert(kvs != NULL);
 1200             kl = PTR2SIZE(kvs - ee);
 1201             assert(kl > 0);
 1202             for (ei = ei_orig; ei-- > 0;) {
 1203                char const *ekvs = strchr(env[ei], '=');
 1204                if (ekvs != NULL && kl == PTR2SIZE(ekvs - env[ei]) &&
 1205                      !memcmp(ee, env[ei], kl)) {
 1206                   env[ei] = n_UNCONST(ee);
 1207                   env_addon_or_null[ai] = NULL;
 1208                   break;
 1209                }
 1210             }
 1211          }
 1212 
 1213          /* And append the rest */
 1214          for (ei = ei_orig, ai = ai_orig; ai-- > 0;)
 1215             if (env_addon_or_null[ai] != NULL)
 1216                env[ei++] = n_UNCONST(env_addon_or_null[ai]);
 1217 
 1218          env[ei] = NULL;
 1219          environ = env;
 1220       }
 1221 
 1222       i = (int)getrawlist(TRU1, argv, n_NELEM(argv), cmd, strlen(cmd));
 1223 
 1224       if ((argv[i++] = n_UNCONST(a0_or_null)) != NULL &&
 1225             (argv[i++] = n_UNCONST(a1_or_null)) != NULL &&
 1226             (argv[i++] = n_UNCONST(a2_or_null)) != NULL)
 1227          argv[i] = NULL;
 1228       n_child_prepare(mask_or_null, infd, outfd);
 1229       execvp(argv[0], argv);
 1230       perror(argv[0]);
 1231       _exit(n_EXIT_ERR);
 1232    }
 1233    NYD_LEAVE;
 1234    return rv;
 1235 }
 1236 
 1237 FL int
 1238 n_child_fork(void){
 1239    /* Strictly speaking we should do so in the waitpid(2) case too, but since
 1240     * we explicitly waitpid(2) on the pid if just the structure exists, which
 1241     * n_child_wait() does in the parent, all is fine */
 1242 #if n_SIGSUSPEND_NOT_WAITPID
 1243    sigset_t nset, oset;
 1244 #endif
 1245    struct child *cp;
 1246    int pid;
 1247    NYD2_ENTER;
 1248 
 1249 #if n_SIGSUSPEND_NOT_WAITPID
 1250    sigfillset(&nset);
 1251    sigprocmask(SIG_BLOCK, &nset, &oset);
 1252 #endif
 1253 
 1254    cp = a_popen_child_find(0, TRU1);
 1255 
 1256    if((cp->pid = pid = fork()) == -1){
 1257       a_popen_child_del(cp);
 1258       n_perr(_("fork"), 0);
 1259    }
 1260 
 1261 #if n_SIGSUSPEND_NOT_WAITPID
 1262    sigprocmask(SIG_SETMASK, &oset, NULL);
 1263 #endif
 1264    NYD2_LEAVE;
 1265    return pid;
 1266 }
 1267 
 1268 FL void
 1269 n_child_prepare(sigset_t *nset_or_null, int infd, int outfd)
 1270 {
 1271    int i;
 1272    sigset_t fset;
 1273    NYD_ENTER;
 1274 
 1275    /* All file descriptors other than 0, 1, and 2 are supposed to be cloexec */
 1276    /* TODO WHAT IS WITH STDERR_FILENO DAMN? */
 1277    if ((i = (infd == n_CHILD_FD_NULL)))
 1278       infd = open(n_path_devnull, O_RDONLY);
 1279    if (infd >= 0) {
 1280       dup2(infd, STDIN_FILENO);
 1281       if (i)
 1282          close(infd);
 1283    }
 1284 
 1285    if ((i = (outfd == n_CHILD_FD_NULL)))
 1286       outfd = open(n_path_devnull, O_WRONLY);
 1287    if (outfd >= 0) {
 1288       dup2(outfd, STDOUT_FILENO);
 1289       if (i)
 1290          close(outfd);
 1291    }
 1292 
 1293    if (nset_or_null != NULL) {
 1294       for (i = 1; i < NSIG; ++i)
 1295          if (sigismember(nset_or_null, i))
 1296             safe_signal(i, SIG_IGN);
 1297       if (!sigismember(nset_or_null, SIGINT))
 1298          safe_signal(SIGINT, SIG_DFL);
 1299    }
 1300 
 1301    sigemptyset(&fset);
 1302    sigprocmask(SIG_SETMASK, &fset, NULL);
 1303    NYD_LEAVE;
 1304 }
 1305 
 1306 FL void
 1307 n_child_free(int pid){
 1308    sigset_t nset, oset;
 1309    struct child *cp;
 1310    NYD2_ENTER;
 1311 
 1312    sigemptyset(&nset);
 1313    sigaddset(&nset, SIGCHLD);
 1314    sigprocmask(SIG_BLOCK, &nset, &oset);
 1315 
 1316    if((cp = a_popen_child_find(pid, FAL0)) != NULL){
 1317       if(cp->done)
 1318          a_popen_child_del(cp);
 1319       else
 1320          cp->free = TRU1;
 1321    }
 1322 
 1323    sigprocmask(SIG_SETMASK, &oset, NULL);
 1324    NYD2_LEAVE;
 1325 }
 1326 
 1327 FL bool_t
 1328 n_child_wait(int pid, int *wait_status_or_null){
 1329 #if !n_SIGSUSPEND_NOT_WAITPID
 1330    sigset_t oset;
 1331 #endif
 1332    sigset_t nset;
 1333    struct child *cp;
 1334    int ws;
 1335    bool_t rv;
 1336    NYD_ENTER;
 1337 
 1338 #if !n_SIGSUSPEND_NOT_WAITPID
 1339    sigemptyset(&nset);
 1340    sigaddset(&nset, SIGCHLD);
 1341    sigprocmask(SIG_BLOCK, &nset, &oset);
 1342 #endif
 1343 
 1344    if((cp = a_popen_child_find(pid, FAL0)) != NULL){
 1345 #if n_SIGSUSPEND_NOT_WAITPID
 1346       sigfillset(&nset);
 1347       sigdelset(&nset, SIGCHLD);
 1348       while(!cp->done)
 1349          sigsuspend(&nset); /* TODO we should allow more than SIGCHLD!! */
 1350       ws = cp->status;
 1351 #else
 1352       waitpid(pid, &ws, 0);
 1353 #endif
 1354       a_popen_child_del(cp);
 1355    }else
 1356       ws = 0;
 1357 
 1358 #if !n_SIGSUSPEND_NOT_WAITPID
 1359    sigprocmask(SIG_SETMASK, &oset, NULL);
 1360 #endif
 1361 
 1362    if(wait_status_or_null != NULL)
 1363       *wait_status_or_null = ws;
 1364    rv = (WIFEXITED(ws) && WEXITSTATUS(ws) == 0);
 1365    NYD_LEAVE;
 1366    return rv;
 1367 }
 1368 
 1369 /* s-it-mode */