"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.7/popen.c" (16 Feb 2018, 34222 Bytes) of package /linux/misc/s-nail-14.9.7.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.6_vs_14.9.7.

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