*inbox*: if empty, only bypass *folder* to $MAIL or builtin default
[s-mailx.git] / popen.c
blobb204d63453212b82790e6cc6b72c5bc66addd2d3
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Handling of pipes, child processes, temporary files, file enwrapping.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2015 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
6 */
7 /*
8 * Copyright (c) 1980, 1993
9 * The Regents of the University of California. All rights reserved.
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.
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.
35 #undef n_FILE
36 #define n_FILE popen
38 #ifndef HAVE_AMALGAMATION
39 # include "nail.h"
40 #endif
42 #include <sys/wait.h>
44 #define READ 0
45 #define WRITE 1
47 struct fp {
48 FILE *fp;
49 struct fp *link;
50 char *realfile;
51 char *save_cmd;
52 long offset;
53 int omode;
54 int pipe;
55 int pid;
56 enum {
57 FP_RAW = 0,
58 FP_GZIP = 1<<0,
59 FP_XZ = 1<<1,
60 FP_BZIP2 = 1<<2,
61 FP_IMAP = 1<<3,
62 FP_MAILDIR = 1<<4,
63 FP_HOOK = 1<<5,
64 FP_MASK = (1<<6) - 1
65 } flags;
68 struct child {
69 int pid;
70 char done;
71 char free;
72 int status;
73 struct child *link;
76 static struct fp *fp_head;
77 static struct child *_popen_child;
79 static int scan_mode(char const *mode, int *omode);
80 static void register_file(FILE *fp, int omode, int ispipe, int pid,
81 int flags, char const *realfile, long offset,
82 char const *save_cmd);
83 static enum okay _file_save(struct fp *fpp);
84 static int _file_load(int flags, int infd, int outfd,
85 char const *load_cmd);
86 static enum okay unregister_file(FILE *fp);
87 static int file_pid(FILE *fp);
89 /* Handle SIGCHLD */
90 static void _sigchld(int signo);
92 static int wait_command(int pid);
93 static struct child *_findchild(int pid, bool_t create);
94 static void _delchild(struct child *cp);
96 static int
97 scan_mode(char const *mode, int *omode)
99 static struct {
100 char const mode[4];
101 int omode;
102 } const maps[] = {
103 {"r", O_RDONLY},
104 {"w", O_WRONLY | O_CREAT | O_TRUNC},
105 {"wx", O_WRONLY | O_CREAT | O_EXCL},
106 {"a", O_WRONLY | O_APPEND | O_CREAT},
107 {"a+", O_RDWR | O_APPEND},
108 {"r+", O_RDWR},
109 {"w+", O_RDWR | O_CREAT | O_EXCL}
112 int i;
113 NYD2_ENTER;
115 for (i = 0; UICMP(z, i, <, NELEM(maps)); ++i)
116 if (!strcmp(maps[i].mode, mode)) {
117 *omode = maps[i].omode;
118 i = 0;
119 goto jleave;
122 n_alert(_("Internal error: bad stdio open mode %s"), mode);
123 errno = EINVAL;
124 *omode = 0; /* (silence CC) */
125 i = -1;
126 jleave:
127 NYD2_LEAVE;
128 return i;
131 static void
132 register_file(FILE *fp, int omode, int ispipe, int pid, int flags,
133 char const *realfile, long offset, char const *save_cmd)
135 struct fp *fpp;
136 NYD_ENTER;
138 fpp = smalloc(sizeof *fpp);
139 fpp->fp = fp;
140 fpp->omode = omode;
141 fpp->pipe = ispipe;
142 fpp->pid = pid;
143 fpp->link = fp_head;
144 fpp->flags = flags;
145 fpp->realfile = (realfile != NULL) ? sstrdup(realfile) : NULL;
146 fpp->save_cmd = (save_cmd != NULL) ? sstrdup(save_cmd) : NULL;
147 fpp->offset = offset;
148 fp_head = fpp;
149 NYD_LEAVE;
152 static enum okay
153 _file_save(struct fp *fpp)
155 char const *cmd[3];
156 int outfd, infd;
157 enum okay rv;
158 NYD_ENTER;
160 if (fpp->omode == O_RDONLY) {
161 rv = OKAY;
162 goto jleave;
164 rv = STOP;
166 fflush(fpp->fp);
167 clearerr(fpp->fp);
168 if (fseek(fpp->fp, fpp->offset, SEEK_SET) == -1) {
169 outfd = errno;
170 n_err(_("Fatal: cannot restore file position and save %s: %s\n"),
171 fpp->realfile, strerror(outfd));
172 goto jleave;
175 #ifdef HAVE_IMAP
176 if ((fpp->flags & FP_MASK) == FP_IMAP) {
177 rv = imap_append(fpp->realfile, fpp->fp);
178 goto jleave;
180 #endif
181 if ((fpp->flags & FP_MASK) == FP_MAILDIR) {
182 rv = maildir_append(fpp->realfile, fpp->fp, fpp->offset);
183 goto jleave;
186 /* Ensure the I/O library didn't optimize the fseek(3) away! */
187 if(lseek(infd = fileno(fpp->fp), fpp->offset, SEEK_SET) == -1){
188 outfd = errno;
189 n_err(_("Fatal: cannot restore file position and save %s: %s\n"),
190 fpp->realfile, strerror(outfd));
191 goto jleave;
194 outfd = open(fpp->realfile,
195 ((fpp->omode | O_CREAT | (fpp->omode & O_APPEND ? 0 : O_TRUNC))
196 & ~O_EXCL), 0666);
197 if (outfd == -1) {
198 outfd = errno;
199 n_err(_("Fatal: cannot create %s: %s\n"),
200 fpp->realfile, strerror(outfd));
201 goto jleave;
204 cmd[2] = NULL;
205 switch (fpp->flags & FP_MASK) {
206 case FP_GZIP:
207 cmd[0] = "gzip"; cmd[1] = "-c"; break;
208 case FP_BZIP2:
209 cmd[0] = "bzip2"; cmd[1] = "-c"; break;
210 case FP_XZ:
211 cmd[0] = "xz"; cmd[1] = "-c"; break;
212 default:
213 cmd[0] = "cat"; cmd[1] = NULL; break;
214 case FP_HOOK:
215 if ((cmd[0] = ok_vlook(SHELL)) == NULL)
216 cmd[0] = XSHELL;
217 cmd[1] = "-c";
218 cmd[2] = fpp->save_cmd;
220 if (run_command(cmd[0], 0, infd, outfd, cmd[1], cmd[2], NULL) >= 0)
221 rv = OKAY;
223 close(outfd);
224 jleave:
225 NYD_LEAVE;
226 return rv;
229 static int
230 _file_load(int flags, int infd, int outfd, char const *load_cmd)
232 char const *cmd[3];
233 int rv;
234 NYD_ENTER;
236 cmd[2] = NULL;
237 switch (flags & FP_MASK) {
238 case FP_GZIP: cmd[0] = "gzip"; cmd[1] = "-cd"; break;
239 case FP_BZIP2: cmd[0] = "bzip2"; cmd[1] = "-cd"; break;
240 case FP_XZ: cmd[0] = "xz"; cmd[1] = "-cd"; break;
241 default: cmd[0] = "cat"; cmd[1] = NULL; break;
242 case FP_HOOK:
243 if ((cmd[0] = ok_vlook(SHELL)) == NULL)
244 cmd[0] = XSHELL;
245 cmd[1] = "-c";
246 cmd[2] = load_cmd;
247 break;
248 case FP_MAILDIR:
249 case FP_IMAP:
250 rv = 0;
251 goto jleave;
254 rv = run_command(cmd[0], 0, infd, outfd, cmd[1], cmd[2], NULL);
255 jleave:
256 NYD_LEAVE;
257 return rv;
260 static enum okay
261 unregister_file(FILE *fp)
263 struct fp **pp, *p;
264 enum okay rv = OKAY;
265 NYD_ENTER;
267 for (pp = &fp_head; (p = *pp) != NULL; pp = &p->link)
268 if (p->fp == fp) {
269 if ((p->flags & FP_MASK) != FP_RAW) /* TODO ;} */
270 rv = _file_save(p);
271 *pp = p->link;
272 if (p->save_cmd != NULL)
273 free(p->save_cmd);
274 if (p->realfile != NULL)
275 free(p->realfile);
276 free(p);
277 goto jleave;
279 DBGOR(n_panic, n_alert)(_("Invalid file pointer"));
280 rv = STOP;
281 jleave:
282 NYD_LEAVE;
283 return rv;
286 static int
287 file_pid(FILE *fp)
289 int rv;
290 struct fp *p;
291 NYD2_ENTER;
293 rv = -1;
294 for (p = fp_head; p; p = p->link)
295 if (p->fp == fp) {
296 rv = p->pid;
297 break;
299 NYD2_LEAVE;
300 return rv;
303 static void
304 _sigchld(int signo)
306 pid_t pid;
307 int status;
308 struct child *cp;
309 NYD_X; /* Signal handler */
310 UNUSED(signo);
312 for (;;) {
313 pid = waitpid(-1, &status, WNOHANG);
314 if (pid <= 0) {
315 if (pid == -1 && errno == EINTR)
316 continue;
317 break;
320 if ((cp = _findchild(pid, FAL0)) != NULL) {
321 if (cp->free)
322 cp->pid = -1; /* XXX Was _delchild(cp);# */
323 else {
324 cp->done = 1;
325 cp->status = status;
331 static int
332 wait_command(int pid)
334 int rv = 0;
335 NYD_ENTER;
337 if (!wait_child(pid, NULL)) {
338 if (ok_blook(bsdcompat) || ok_blook(bsdmsgs))
339 n_err(_("Fatal error in process\n"));
340 rv = -1;
342 NYD_LEAVE;
343 return rv;
346 static struct child *
347 _findchild(int pid, bool_t create)
349 struct child **cpp;
350 NYD_ENTER;
352 for (cpp = &_popen_child; *cpp != NULL && (*cpp)->pid != pid;
353 cpp = &(*cpp)->link)
356 if (*cpp == NULL && create) {
357 *cpp = smalloc(sizeof **cpp);
358 (*cpp)->pid = pid;
359 (*cpp)->done = (*cpp)->free = 0;
360 (*cpp)->link = NULL;
362 NYD_LEAVE;
363 return *cpp;
366 static void
367 _delchild(struct child *cp)
369 struct child **cpp;
370 NYD_ENTER;
372 cpp = &_popen_child;
373 for (;;) {
374 if (*cpp == cp) {
375 *cpp = cp->link;
376 free(cp);
377 break;
379 if (*(cpp = &(*cpp)->link) == NULL) {
380 DBG( n_err("! popen.c:_delchild(): implementation error\n"); )
381 break;
384 NYD_LEAVE;
387 FL void
388 command_manager_start(void)
390 struct sigaction nact, oact;
391 NYD_ENTER;
393 nact.sa_handler = &_sigchld;
394 sigemptyset(&nact.sa_mask);
395 nact.sa_flags = 0
396 #ifdef SA_RESTART
397 | SA_RESTART
398 #endif
399 #ifdef SA_NOCLDSTOP
400 | SA_NOCLDSTOP
401 #endif
403 if (sigaction(SIGCHLD, &nact, &oact) != 0)
404 n_panic(_("Cannot install signal handler for child process management"));
405 NYD_LEAVE;
408 FL FILE *
409 safe_fopen(char const *file, char const *oflags, int *xflags)
411 int osflags, fd;
412 FILE *fp = NULL;
413 NYD2_ENTER; /* (only for Fopen() and once in lex.c) */
415 if (scan_mode(oflags, &osflags) < 0)
416 goto jleave;
417 osflags |= _O_CLOEXEC;
418 if (xflags != NULL)
419 *xflags = osflags;
421 if ((fd = open(file, osflags, 0666)) == -1)
422 goto jleave;
423 _CLOEXEC_SET(fd);
425 fp = fdopen(fd, oflags);
426 jleave:
427 NYD2_LEAVE;
428 return fp;
431 FL FILE *
432 Fopen(char const *file, char const *oflags)
434 FILE *fp;
435 int osflags;
436 NYD_ENTER;
438 if ((fp = safe_fopen(file, oflags, &osflags)) != NULL)
439 register_file(fp, osflags, 0, 0, FP_RAW, NULL, 0L, NULL);
440 NYD_LEAVE;
441 return fp;
444 FL FILE *
445 Fdopen(int fd, char const *oflags, bool_t nocloexec)
447 FILE *fp;
448 int osflags;
449 NYD_ENTER;
451 scan_mode(oflags, &osflags);
452 if (!nocloexec)
453 osflags |= _O_CLOEXEC; /* Ensured to be set by caller as documented! */
455 if ((fp = fdopen(fd, oflags)) != NULL)
456 register_file(fp, osflags, 0, 0, FP_RAW, NULL, 0L, NULL);
457 NYD_LEAVE;
458 return fp;
461 FL int
462 Fclose(FILE *fp)
464 int i = 0;
465 NYD_ENTER;
467 if (unregister_file(fp) == OKAY)
468 i |= 1;
469 if (fclose(fp) == 0)
470 i |= 2;
471 NYD_LEAVE;
472 return (i == 3 ? 0 : EOF);
475 FL FILE *
476 Zopen(char const *file, char const *oflags) /* FIXME MESS! */
478 FILE *rv = NULL;
479 char const *cload = NULL, *csave = NULL;
480 int flags, osflags, mode, infd;
481 enum oflags rof;
482 long offset;
483 enum protocol p;
484 NYD_ENTER;
486 if (scan_mode(oflags, &osflags) < 0)
487 goto jleave;
489 flags = 0;
490 rof = OF_RDWR | OF_UNLINK;
491 if (osflags & O_APPEND)
492 rof |= OF_APPEND;
493 mode = (osflags == O_RDONLY) ? R_OK : R_OK | W_OK;
495 if ((osflags & O_APPEND) && ((p = which_protocol(file)) == PROTO_IMAP ||
496 p == PROTO_MAILDIR)) {
497 flags |= (p == PROTO_IMAP) ? FP_IMAP : FP_MAILDIR;
498 osflags = O_RDWR | O_APPEND | O_CREAT;
499 infd = -1;
500 } else {
501 char const *ext;
503 if ((ext = strrchr(file, '.')) != NULL) {
504 if (!asccasecmp(ext, ".gz"))
505 flags |= FP_GZIP;
506 else if (!asccasecmp(ext, ".xz")) {
507 flags |= FP_XZ;
508 osflags &= ~O_APPEND;
509 rof &= ~OF_APPEND;
510 } else if (!asccasecmp(ext, ".bz2")) {
511 flags |= FP_BZIP2;
512 osflags &= ~O_APPEND;
513 rof &= ~OF_APPEND;
514 } else {
515 #undef _X1
516 #define _X1 "file-hook-load-"
517 #undef _X2
518 #define _X2 "file-hook-save-"
519 size_t l = strlen(++ext);
520 char *vbuf = ac_alloc(l + MAX(sizeof(_X1), sizeof(_X2)));
522 memcpy(vbuf, _X1, sizeof(_X1) -1);
523 memcpy(vbuf + sizeof(_X1) -1, ext, l);
524 vbuf[sizeof(_X1) -1 + l] = '\0';
525 cload = vok_vlook(vbuf);
526 memcpy(vbuf, _X2, sizeof(_X2) -1);
527 memcpy(vbuf + sizeof(_X2) -1, ext, l);
528 vbuf[sizeof(_X2) -1 + l] = '\0';
529 csave = vok_vlook(vbuf);
530 #undef _X2
531 #undef _X1
532 ac_free(vbuf);
534 if ((csave != NULL) && (cload != NULL)) {
535 flags |= FP_HOOK;
536 osflags &= ~O_APPEND;
537 rof &= ~OF_APPEND;
538 } else if ((csave != NULL) | (cload != NULL)) {
539 n_alert(_("Only one of *mailbox-(load|save)-%s* is set! "
540 "Treating as plain text!"), ext);
541 goto jraw;
542 } else
543 goto jraw;
545 } else {
546 jraw:
547 /*flags |= FP_RAW;*/
548 rv = Fopen(file, oflags);
549 goto jleave;
552 if ((infd = open(file, (mode & W_OK) ? O_RDWR : O_RDONLY)) == -1 &&
553 (!(osflags & O_CREAT) || errno != ENOENT))
554 goto jleave;
557 /* Note rv is not yet register_file()d, fclose() it in error path! */
558 if ((rv = Ftmp(NULL, "zopen", rof, 0600)) == NULL) {
559 n_perr(_("tmpfile"), 0);
560 goto jerr;
563 if (flags & (FP_IMAP | FP_MAILDIR))
565 else if (infd >= 0) {
566 if (_file_load(flags, infd, fileno(rv), cload) < 0) {
567 jerr:
568 if (rv != NULL)
569 fclose(rv);
570 rv = NULL;
571 if (infd >= 0)
572 close(infd);
573 goto jleave;
575 } else {
576 if ((infd = creat(file, 0666)) == -1) {
577 fclose(rv);
578 rv = NULL;
579 goto jleave;
583 if (infd >= 0)
584 close(infd);
585 fflush(rv);
587 if (!(osflags & O_APPEND))
588 rewind(rv);
589 if ((offset = ftell(rv)) == -1) {
590 Fclose(rv);
591 rv = NULL;
592 goto jleave;
595 register_file(rv, osflags, 0, 0, flags, file, offset, csave);
596 jleave:
597 NYD_LEAVE;
598 return rv;
601 FL FILE *
602 Ftmp(char **fn, char const *prefix, enum oflags oflags, int mode)
604 FILE *fp = NULL;
605 size_t maxname, tries;
606 char *cp_base, *cp;
607 int osoflags, fd, e;
608 NYD_ENTER;
610 assert((oflags & OF_WRONLY) || (oflags & OF_RDWR));
611 assert(!(oflags & OF_RDONLY));
613 e = 0;
614 maxname = NAME_MAX;
615 #ifdef HAVE_PATHCONF
616 { long pc;
618 if ((pc = pathconf(tempdir, _PC_NAME_MAX)) != -1)
619 maxname = (size_t)pc;
621 #endif
623 cp_base =
624 cp = smalloc(strlen(tempdir) + 1 + maxname +1);
625 cp = sstpcpy(cp, tempdir);
626 *cp++ = '/';
628 osoflags = O_CREAT | O_EXCL | _O_CLOEXEC;
629 osoflags |= (oflags & OF_WRONLY) ? O_WRONLY : O_RDWR;
630 if (oflags & OF_APPEND)
631 osoflags |= O_APPEND;
633 for (tries = 0;; ++tries) {
634 size_t i;
635 char *x;
637 x = sstpcpy(cp, UAGENT);
638 *x++ = '-';
639 if (*prefix != '\0') {
640 x = sstpcpy(x, prefix);
641 *x++ = '-';
644 /* Calculate length of a random string addon */
645 i = PTR2SIZE(x - cp);
646 if (i >= maxname >> 1) {
647 x = cp;
648 i = maxname -1;
649 } else
650 i = maxname >> 1;
651 /* But don't be too fatalistic */
652 if (i > 8 && tries < FTMP_OPEN_TRIES / 2)
653 i = 8;
654 memcpy(x, getrandstring(i), i +1);
656 hold_all_sigs();
657 if ((fd = open(cp_base, osoflags, mode)) != -1) {
658 _CLOEXEC_SET(fd);
659 break;
661 if (tries >= FTMP_OPEN_TRIES) {
662 e = errno;
663 goto jfree;
665 rele_all_sigs();
668 if (oflags & OF_REGISTER)
669 fp = Fdopen(fd, (oflags & OF_RDWR ? "w+" : "w"), FAL0);
670 else
671 fp = fdopen(fd, (oflags & OF_RDWR ? "w+" : "w"));
673 if (fp == NULL || (oflags & OF_UNLINK)) {
674 e = errno;
675 unlink(cp_base);
676 goto jfree;
679 if (fn != NULL)
680 *fn = cp_base;
681 else
682 free(cp_base);
683 jleave:
684 if (fp == NULL || !(oflags & OF_HOLDSIGS))
685 rele_all_sigs();
686 if (fp == NULL)
687 errno = e;
688 NYD_LEAVE;
689 return fp;
690 jfree:
691 if ((cp = cp_base) != NULL)
692 free(cp);
693 goto jleave;
696 FL void
697 Ftmp_release(char **fn)
699 char *cp;
700 NYD_ENTER;
702 cp = *fn;
703 *fn = NULL;
704 if (cp != NULL) {
705 unlink(cp);
706 rele_all_sigs();
707 free(cp);
709 NYD_LEAVE;
712 FL void
713 Ftmp_free(char **fn)
715 char *cp;
716 NYD_ENTER;
718 cp = *fn;
719 *fn = NULL;
720 if (cp != NULL)
721 free(cp);
722 NYD_LEAVE;
725 FL bool_t
726 pipe_cloexec(int fd[2])
728 bool_t rv = FAL0;
729 NYD_ENTER;
731 #ifdef HAVE_PIPE2
732 if (pipe2(fd, O_CLOEXEC) == -1)
733 goto jleave;
734 #else
735 if (pipe(fd) == -1)
736 goto jleave;
737 (void)fcntl(fd[0], F_SETFD, FD_CLOEXEC);
738 (void)fcntl(fd[1], F_SETFD, FD_CLOEXEC);
739 #endif
740 rv = TRU1;
741 jleave:
742 NYD_LEAVE;
743 return rv;
746 FL FILE *
747 Popen(char const *cmd, char const *mode, char const *sh,
748 char const **env_addon, int newfd1)
750 int p[2], myside, hisside, fd0, fd1, pid;
751 char mod[2] = {'0', '\0'};
752 sigset_t nset;
753 FILE *rv = NULL;
754 NYD_ENTER;
756 /* First clean up child structures */
757 { sigset_t oset;
758 struct child **cpp, *cp;
760 sigfillset(&nset);
761 sigprocmask(SIG_BLOCK, &nset, &oset);
763 for (cpp = &_popen_child; *cpp != NULL;) {
764 if ((*cpp)->pid == -1) {
765 cp = *cpp;
766 *cpp = cp->link;
767 free(cp);
768 } else
769 cpp = &(*cpp)->link;
772 sigprocmask(SIG_SETMASK, &oset, NULL);
775 if (!pipe_cloexec(p))
776 goto jleave;
778 if (*mode == 'r') {
779 myside = p[READ];
780 fd0 = -1;
781 hisside = fd1 = p[WRITE];
782 mod[0] = *mode;
783 } else if (*mode == 'W') {
784 myside = p[WRITE];
785 hisside = fd0 = p[READ];
786 fd1 = newfd1;
787 mod[0] = 'w';
788 } else {
789 myside = p[WRITE];
790 hisside = fd0 = p[READ];
791 fd1 = -1;
792 mod[0] = 'w';
795 sigemptyset(&nset);
797 if (cmd == (char*)-1) {
798 if ((pid = fork_child()) == -1)
799 n_perr(_("fork"), 0);
800 else if (pid == 0) {
801 union {char const *ccp; int (*ptf)(void); int es;} u;
802 prepare_child(&nset, fd0, fd1);
803 close(p[READ]);
804 close(p[WRITE]);
805 u.ccp = sh;
806 u.es = (*u.ptf)();
807 _exit(u.es);
809 } else if (sh == NULL) {
810 pid = start_command(cmd, &nset, fd0, fd1, NULL, NULL, NULL, env_addon);
811 } else {
812 pid = start_command(sh, &nset, fd0, fd1, "-c", cmd, NULL, env_addon);
814 if (pid < 0) {
815 close(p[READ]);
816 close(p[WRITE]);
817 goto jleave;
819 close(hisside);
820 if ((rv = fdopen(myside, mod)) != NULL)
821 register_file(rv, 0, 1, pid, FP_RAW, NULL, 0L, NULL);
822 else
823 close(myside);
824 jleave:
825 NYD_LEAVE;
826 return rv;
829 FL bool_t
830 Pclose(FILE *ptr, bool_t dowait)
832 sigset_t nset, oset;
833 int pid;
834 bool_t rv = FAL0;
835 NYD_ENTER;
837 pid = file_pid(ptr);
838 if (pid < 0)
839 goto jleave;
840 unregister_file(ptr);
841 fclose(ptr);
842 if (dowait) {
843 sigemptyset(&nset);
844 sigaddset(&nset, SIGINT);
845 sigaddset(&nset, SIGHUP);
846 sigprocmask(SIG_BLOCK, &nset, &oset);
847 rv = wait_child(pid, NULL);
848 sigprocmask(SIG_SETMASK, &oset, NULL);
849 } else {
850 free_child(pid);
851 rv = TRU1;
853 jleave:
854 NYD_LEAVE;
855 return rv;
858 FL void
859 close_all_files(void)
861 NYD_ENTER;
862 while (fp_head != NULL)
863 if (fp_head->pipe)
864 Pclose(fp_head->fp, TRU1);
865 else
866 Fclose(fp_head->fp);
867 NYD_LEAVE;
870 FL int
871 fork_child(void)
873 struct child *cp;
874 int pid;
875 NYD_ENTER;
877 cp = _findchild(0, TRU1);
879 if ((cp->pid = pid = fork()) == -1) {
880 _delchild(cp);
881 n_perr(_("fork"), 0);
883 NYD_LEAVE;
884 return pid;
887 FL int
888 run_command(char const *cmd, sigset_t *mask, int infd, int outfd,
889 char const *a0, char const *a1, char const *a2)
891 int rv;
892 NYD_ENTER;
894 if ((rv = start_command(cmd, mask, infd, outfd, a0, a1, a2, NULL)) < 0)
895 rv = -1;
896 else
897 rv = wait_command(rv);
898 NYD_LEAVE;
899 return rv;
902 FL int
903 start_command(char const *cmd, sigset_t *mask, int infd, int outfd,
904 char const *a0, char const *a1, char const *a2,
905 char const **env_addon)
907 int rv;
908 NYD_ENTER;
910 if ((rv = fork_child()) == -1) {
911 n_perr(_("fork"), 0);
912 rv = -1;
913 } else if (rv == 0) {
914 char *argv[128];
915 int i;
917 if (env_addon != NULL) { /* TODO env_addon; should have struct child */
918 extern char **environ;
919 size_t ei, ei_orig, ai, ai_orig;
920 char **env;
922 /* TODO note we don't check the POSIX limit:
923 * the total space used to store the environment and the arguments to
924 * the process is limited to {ARG_MAX} bytes */
925 for (ei = 0; environ[ei] != NULL; ++ei)
927 ei_orig = ei;
928 for (ai = 0; env_addon[ai] != NULL; ++ai)
930 ai_orig = ai;
931 env = ac_alloc(sizeof(*env) * (ei + ai +1));
932 memcpy(env, environ, sizeof(*env) * ei);
934 /* Replace all those keys that yet exist */
935 while (ai-- > 0) {
936 char const *ee, *kvs;
937 size_t kl;
939 ee = env_addon[ai];
940 kvs = strchr(ee, '=');
941 assert(kvs != NULL);
942 kl = PTR2SIZE(kvs - ee);
943 assert(kl > 0);
944 for (ei = ei_orig; ei-- > 0;) {
945 char const *ekvs = strchr(env[ei], '=');
946 if (ekvs != NULL && kl == PTR2SIZE(ekvs - env[ei]) &&
947 !memcmp(ee, env[ei], kl)) {
948 env[ei] = UNCONST(ee);
949 env_addon[ai] = NULL;
950 break;
955 /* And append the rest */
956 for (ei = ei_orig, ai = ai_orig; ai-- > 0;)
957 if (env_addon[ai] != NULL)
958 env[ei++] = UNCONST(env_addon[ai]);
960 env[ei] = NULL;
961 environ = env;
964 i = getrawlist(cmd, strlen(cmd), argv, NELEM(argv), 0);
966 if ((argv[i++] = UNCONST(a0)) != NULL &&
967 (argv[i++] = UNCONST(a1)) != NULL &&
968 (argv[i++] = UNCONST(a2)) != NULL)
969 argv[i] = NULL;
970 prepare_child(mask, infd, outfd);
971 execvp(argv[0], argv);
972 perror(argv[0]);
973 _exit(EXIT_ERR);
975 NYD_LEAVE;
976 return rv;
979 FL void
980 prepare_child(sigset_t *nset, int infd, int outfd)
982 int i;
983 sigset_t fset;
984 NYD_ENTER;
986 /* All file descriptors other than 0, 1, and 2 are supposed to be cloexec */
987 if (infd >= 0)
988 dup2(infd, STDIN_FILENO);
989 if (outfd >= 0)
990 dup2(outfd, STDOUT_FILENO);
992 if (nset) {
993 for (i = 1; i < NSIG; ++i)
994 if (sigismember(nset, i))
995 safe_signal(i, SIG_IGN);
996 if (!sigismember(nset, SIGINT))
997 safe_signal(SIGINT, SIG_DFL);
1000 sigemptyset(&fset);
1001 sigprocmask(SIG_SETMASK, &fset, NULL);
1002 NYD_LEAVE;
1005 FL void
1006 free_child(int pid)
1008 sigset_t nset, oset;
1009 struct child *cp;
1010 NYD_ENTER;
1012 sigemptyset(&nset);
1013 sigaddset(&nset, SIGCHLD);
1014 sigprocmask(SIG_BLOCK, &nset, &oset);
1016 if ((cp = _findchild(pid, FAL0)) != NULL) {
1017 if (cp->done)
1018 _delchild(cp);
1019 else
1020 cp->free = 1;
1023 sigprocmask(SIG_SETMASK, &oset, NULL);
1024 NYD_LEAVE;
1027 FL bool_t
1028 wait_child(int pid, int *wait_status)
1030 sigset_t nset, oset;
1031 struct child *cp;
1032 int ws;
1033 bool_t rv;
1034 NYD_ENTER;
1036 sigemptyset(&nset);
1037 sigaddset(&nset, SIGCHLD);
1038 sigprocmask(SIG_BLOCK, &nset, &oset);
1040 cp = _findchild(pid, FAL0);
1041 if (cp != NULL) {
1042 while (!cp->done)
1043 sigsuspend(&oset);
1044 ws = cp->status;
1045 _delchild(cp);
1046 } else
1047 ws = 0;
1049 sigprocmask(SIG_SETMASK, &oset, NULL);
1051 if (wait_status != NULL)
1052 *wait_status = ws;
1053 rv = (WIFEXITED(ws) && WEXITSTATUS(ws) == 0);
1054 NYD_LEAVE;
1055 return rv;
1058 /* s-it-mode */