"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.11/popen.c" (8 Aug 2018, 35482 Bytes) of package /linux/misc/s-nail-14.9.11.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.10_vs_14.9.11.

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