Allow "env_addon" argument for run_command()
[s-mailx.git] / popen.c
blob9b52e03063e821affc790d4bf6b3b2dfef8fddee
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 /* TODO FP_UNLINK: should be in a separated process so that unlinking
66 * TODO the temporary "garbage" is "safe"(r than it is like that) */
67 FP_UNLINK = 1<<6
68 } flags;
71 struct child {
72 int pid;
73 char done;
74 char free;
75 int status;
76 struct child *link;
79 static struct fp *fp_head;
80 static struct child *_popen_child;
82 static int scan_mode(char const *mode, int *omode);
83 static void register_file(FILE *fp, int omode, int ispipe, int pid,
84 int flags, char const *realfile, long offset,
85 char const *save_cmd);
86 static enum okay _file_save(struct fp *fpp);
87 static int _file_load(int flags, int infd, int outfd,
88 char const *load_cmd);
89 static enum okay unregister_file(FILE *fp);
90 static int file_pid(FILE *fp);
92 /* Handle SIGCHLD */
93 static void _sigchld(int signo);
95 static int wait_command(int pid);
96 static struct child *_findchild(int pid, bool_t create);
97 static void _delchild(struct child *cp);
99 static int
100 scan_mode(char const *mode, int *omode)
102 static struct {
103 char const mode[4];
104 int omode;
105 } const maps[] = {
106 {"r", O_RDONLY},
107 {"w", O_WRONLY | O_CREAT | O_TRUNC},
108 {"wx", O_WRONLY | O_CREAT | O_EXCL},
109 {"a", O_WRONLY | O_APPEND | O_CREAT},
110 {"a+", O_RDWR | O_APPEND},
111 {"r+", O_RDWR},
112 {"w+", O_RDWR | O_CREAT | O_EXCL}
115 int i;
116 NYD2_ENTER;
118 for (i = 0; UICMP(z, i, <, NELEM(maps)); ++i)
119 if (!strcmp(maps[i].mode, mode)) {
120 *omode = maps[i].omode;
121 i = 0;
122 goto jleave;
125 n_alert(_("Internal error: bad stdio open mode %s"), mode);
126 errno = EINVAL;
127 *omode = 0; /* (silence CC) */
128 i = -1;
129 jleave:
130 NYD2_LEAVE;
131 return i;
134 static void
135 register_file(FILE *fp, int omode, int ispipe, int pid, int flags,
136 char const *realfile, long offset, char const *save_cmd)
138 struct fp *fpp;
139 NYD_ENTER;
141 assert(!(flags & FP_UNLINK) || realfile != NULL);
143 fpp = smalloc(sizeof *fpp);
144 fpp->fp = fp;
145 fpp->omode = omode;
146 fpp->pipe = ispipe;
147 fpp->pid = pid;
148 fpp->link = fp_head;
149 fpp->flags = flags;
150 fpp->realfile = (realfile != NULL) ? sstrdup(realfile) : NULL;
151 fpp->save_cmd = (save_cmd != NULL) ? sstrdup(save_cmd) : NULL;
152 fpp->offset = offset;
153 fp_head = fpp;
154 NYD_LEAVE;
157 static enum okay
158 _file_save(struct fp *fpp)
160 char const *cmd[3];
161 int outfd, infd;
162 enum okay rv;
163 NYD_ENTER;
165 if (fpp->omode == O_RDONLY) {
166 rv = OKAY;
167 goto jleave;
169 rv = STOP;
171 fflush(fpp->fp);
172 clearerr(fpp->fp);
174 #ifdef HAVE_IMAP
175 if ((fpp->flags & FP_MASK) == FP_IMAP) {
176 rv = imap_append(fpp->realfile, fpp->fp);
177 goto jleave;
179 #endif
180 if ((fpp->flags & FP_MASK) == FP_MAILDIR) {
181 if (fseek(fpp->fp, fpp->offset, SEEK_SET) == -1) {
182 outfd = errno;
183 n_err(_("Fatal: cannot restore file position and save %s: %s\n"),
184 fpp->realfile, strerror(outfd));
185 goto jleave;
187 rv = maildir_append(fpp->realfile, fpp->fp, fpp->offset);
188 goto jleave;
191 /* Ensure the I/O library doesn't optimize the fseek(3) away! */
192 if(lseek(infd = fileno(fpp->fp), fpp->offset, SEEK_SET) == -1){
193 outfd = errno;
194 n_err(_("Fatal: cannot restore file position and save %s: %s\n"),
195 fpp->realfile, strerror(outfd));
196 goto jleave;
199 outfd = open(fpp->realfile,
200 ((fpp->omode | O_CREAT | (fpp->omode & O_APPEND ? 0 : O_TRUNC))
201 & ~O_EXCL), 0666);
202 if (outfd == -1) {
203 outfd = errno;
204 n_err(_("Fatal: cannot create %s: %s\n"),
205 fpp->realfile, strerror(outfd));
206 goto jleave;
209 cmd[2] = NULL;
210 switch (fpp->flags & FP_MASK) {
211 case FP_GZIP:
212 cmd[0] = "gzip"; cmd[1] = "-c"; break;
213 case FP_BZIP2:
214 cmd[0] = "bzip2"; cmd[1] = "-c"; break;
215 case FP_XZ:
216 cmd[0] = "xz"; cmd[1] = "-c"; break;
217 default:
218 cmd[0] = "cat"; cmd[1] = NULL; break;
219 case FP_HOOK:
220 if ((cmd[0] = ok_vlook(SHELL)) == NULL)
221 cmd[0] = XSHELL;
222 cmd[1] = "-c";
223 cmd[2] = fpp->save_cmd;
225 if (run_command(cmd[0], 0, infd, outfd, cmd[1], cmd[2], NULL, NULL) >= 0)
226 rv = OKAY;
228 close(outfd);
229 jleave:
230 NYD_LEAVE;
231 return rv;
234 static int
235 _file_load(int flags, int infd, int outfd, char const *load_cmd)
237 char const *cmd[3];
238 int rv;
239 NYD_ENTER;
241 cmd[2] = NULL;
242 switch (flags & FP_MASK) {
243 case FP_GZIP: cmd[0] = "gzip"; cmd[1] = "-cd"; break;
244 case FP_BZIP2: cmd[0] = "bzip2"; cmd[1] = "-cd"; break;
245 case FP_XZ: cmd[0] = "xz"; cmd[1] = "-cd"; break;
246 default: cmd[0] = "cat"; cmd[1] = NULL; break;
247 case FP_HOOK:
248 if ((cmd[0] = ok_vlook(SHELL)) == NULL)
249 cmd[0] = XSHELL;
250 cmd[1] = "-c";
251 cmd[2] = load_cmd;
252 break;
253 case FP_MAILDIR:
254 case FP_IMAP:
255 rv = 0;
256 goto jleave;
259 rv = run_command(cmd[0], 0, infd, outfd, cmd[1], cmd[2], NULL, NULL);
260 jleave:
261 NYD_LEAVE;
262 return rv;
265 static enum okay
266 unregister_file(FILE *fp)
268 struct fp **pp, *p;
269 enum okay rv = OKAY;
270 NYD_ENTER;
272 for (pp = &fp_head; (p = *pp) != NULL; pp = &p->link)
273 if (p->fp == fp) {
274 if ((p->flags & FP_MASK) != FP_RAW) /* TODO ;} */
275 rv = _file_save(p);
276 if (p->flags & FP_UNLINK && unlink(p->realfile))
277 rv = STOP;
278 *pp = p->link;
279 if (p->save_cmd != NULL)
280 free(p->save_cmd);
281 if (p->realfile != NULL)
282 free(p->realfile);
283 free(p);
284 goto jleave;
286 DBGOR(n_panic, n_alert)(_("Invalid file pointer"));
287 rv = STOP;
288 jleave:
289 NYD_LEAVE;
290 return rv;
293 static int
294 file_pid(FILE *fp)
296 int rv;
297 struct fp *p;
298 NYD2_ENTER;
300 rv = -1;
301 for (p = fp_head; p; p = p->link)
302 if (p->fp == fp) {
303 rv = p->pid;
304 break;
306 NYD2_LEAVE;
307 return rv;
310 static void
311 _sigchld(int signo)
313 pid_t pid;
314 int status;
315 struct child *cp;
316 NYD_X; /* Signal handler */
317 UNUSED(signo);
319 for (;;) {
320 pid = waitpid(-1, &status, WNOHANG);
321 if (pid <= 0) {
322 if (pid == -1 && errno == EINTR)
323 continue;
324 break;
327 if ((cp = _findchild(pid, FAL0)) != NULL) {
328 if (cp->free)
329 cp->pid = -1; /* XXX Was _delchild(cp);# */
330 else {
331 cp->done = 1;
332 cp->status = status;
338 static int
339 wait_command(int pid)
341 int rv = 0;
342 NYD_ENTER;
344 if (!wait_child(pid, NULL)) {
345 if (ok_blook(bsdcompat) || ok_blook(bsdmsgs))
346 n_err(_("Fatal error in process\n"));
347 rv = -1;
349 NYD_LEAVE;
350 return rv;
353 static struct child *
354 _findchild(int pid, bool_t create)
356 struct child **cpp;
357 NYD_ENTER;
359 for (cpp = &_popen_child; *cpp != NULL && (*cpp)->pid != pid;
360 cpp = &(*cpp)->link)
363 if (*cpp == NULL && create) {
364 *cpp = smalloc(sizeof **cpp);
365 (*cpp)->pid = pid;
366 (*cpp)->done = (*cpp)->free = 0;
367 (*cpp)->link = NULL;
369 NYD_LEAVE;
370 return *cpp;
373 static void
374 _delchild(struct child *cp)
376 struct child **cpp;
377 NYD_ENTER;
379 cpp = &_popen_child;
380 for (;;) {
381 if (*cpp == cp) {
382 *cpp = cp->link;
383 free(cp);
384 break;
386 if (*(cpp = &(*cpp)->link) == NULL) {
387 DBG( n_err("! popen.c:_delchild(): implementation error\n"); )
388 break;
391 NYD_LEAVE;
394 FL void
395 command_manager_start(void)
397 struct sigaction nact, oact;
398 NYD_ENTER;
400 nact.sa_handler = &_sigchld;
401 sigemptyset(&nact.sa_mask);
402 nact.sa_flags = 0
403 #ifdef SA_RESTART
404 | SA_RESTART
405 #endif
406 #ifdef SA_NOCLDSTOP
407 | SA_NOCLDSTOP
408 #endif
410 if (sigaction(SIGCHLD, &nact, &oact) != 0)
411 n_panic(_("Cannot install signal handler for child process management"));
412 NYD_LEAVE;
415 FL FILE *
416 safe_fopen(char const *file, char const *oflags, int *xflags)
418 int osflags, fd;
419 FILE *fp = NULL;
420 NYD2_ENTER; /* (only for Fopen() and once in lex.c) */
422 if (scan_mode(oflags, &osflags) < 0)
423 goto jleave;
424 osflags |= _O_CLOEXEC;
425 if (xflags != NULL)
426 *xflags = osflags;
428 if ((fd = open(file, osflags, 0666)) == -1)
429 goto jleave;
430 _CLOEXEC_SET(fd);
432 fp = fdopen(fd, oflags);
433 jleave:
434 NYD2_LEAVE;
435 return fp;
438 FL FILE *
439 Fopen(char const *file, char const *oflags)
441 FILE *fp;
442 int osflags;
443 NYD_ENTER;
445 if ((fp = safe_fopen(file, oflags, &osflags)) != NULL)
446 register_file(fp, osflags, 0, 0, FP_RAW, NULL, 0L, NULL);
447 NYD_LEAVE;
448 return fp;
451 FL FILE *
452 Fdopen(int fd, char const *oflags, bool_t nocloexec)
454 FILE *fp;
455 int osflags;
456 NYD_ENTER;
458 scan_mode(oflags, &osflags);
459 if (!nocloexec)
460 osflags |= _O_CLOEXEC; /* Ensured to be set by caller as documented! */
462 if ((fp = fdopen(fd, oflags)) != NULL)
463 register_file(fp, osflags, 0, 0, FP_RAW, NULL, 0L, NULL);
464 NYD_LEAVE;
465 return fp;
468 FL int
469 Fclose(FILE *fp)
471 int i = 0;
472 NYD_ENTER;
474 if (unregister_file(fp) == OKAY)
475 i |= 1;
476 if (fclose(fp) == 0)
477 i |= 2;
478 NYD_LEAVE;
479 return (i == 3 ? 0 : EOF);
482 FL FILE *
483 Zopen(char const *file, char const *oflags) /* FIXME MESS! */
485 FILE *rv = NULL;
486 char const *cload = NULL, *csave = NULL;
487 int flags, osflags, mode, infd;
488 enum oflags rof;
489 long offset;
490 enum protocol p;
491 NYD_ENTER;
493 if (scan_mode(oflags, &osflags) < 0)
494 goto jleave;
496 flags = 0;
497 rof = OF_RDWR | OF_UNLINK;
498 if (osflags & O_APPEND)
499 rof |= OF_APPEND;
500 mode = (osflags == O_RDONLY) ? R_OK : R_OK | W_OK;
502 if ((osflags & O_APPEND) && ((p = which_protocol(file)) == PROTO_IMAP ||
503 p == PROTO_MAILDIR)) {
504 flags |= (p == PROTO_IMAP) ? FP_IMAP : FP_MAILDIR;
505 osflags = O_RDWR | O_APPEND | O_CREAT;
506 infd = -1;
507 } else {
508 char const *ext;
510 if ((ext = strrchr(file, '.')) != NULL) {
511 if (!asccasecmp(ext, ".gz"))
512 flags |= FP_GZIP;
513 else if (!asccasecmp(ext, ".xz")) {
514 flags |= FP_XZ;
515 osflags &= ~O_APPEND;
516 rof &= ~OF_APPEND;
517 } else if (!asccasecmp(ext, ".bz2")) {
518 flags |= FP_BZIP2;
519 osflags &= ~O_APPEND;
520 rof &= ~OF_APPEND;
521 } else {
522 #undef _X1
523 #define _X1 "file-hook-load-"
524 #undef _X2
525 #define _X2 "file-hook-save-"
526 size_t l = strlen(++ext);
527 char *vbuf = ac_alloc(l + MAX(sizeof(_X1), sizeof(_X2)));
529 memcpy(vbuf, _X1, sizeof(_X1) -1);
530 memcpy(vbuf + sizeof(_X1) -1, ext, l);
531 vbuf[sizeof(_X1) -1 + l] = '\0';
532 cload = vok_vlook(vbuf);
533 memcpy(vbuf, _X2, sizeof(_X2) -1);
534 memcpy(vbuf + sizeof(_X2) -1, ext, l);
535 vbuf[sizeof(_X2) -1 + l] = '\0';
536 csave = vok_vlook(vbuf);
537 #undef _X2
538 #undef _X1
539 ac_free(vbuf);
541 if ((csave != NULL) && (cload != NULL)) {
542 flags |= FP_HOOK;
543 osflags &= ~O_APPEND;
544 rof &= ~OF_APPEND;
545 } else if ((csave != NULL) | (cload != NULL)) {
546 n_alert(_("Only one of *mailbox-(load|save)-%s* is set! "
547 "Treating as plain text!"), ext);
548 goto jraw;
549 } else
550 goto jraw;
552 } else {
553 jraw:
554 /*flags |= FP_RAW;*/
555 rv = Fopen(file, oflags);
556 goto jleave;
559 if ((infd = open(file, (mode & W_OK) ? O_RDWR : O_RDONLY)) == -1 &&
560 (!(osflags & O_CREAT) || errno != ENOENT))
561 goto jleave;
564 /* Note rv is not yet register_file()d, fclose() it in error path! */
565 if ((rv = Ftmp(NULL, "zopen", rof, 0600)) == NULL) {
566 n_perr(_("tmpfile"), 0);
567 goto jerr;
570 if (flags & (FP_IMAP | FP_MAILDIR))
572 else if (infd >= 0) {
573 if (_file_load(flags, infd, fileno(rv), cload) < 0) {
574 jerr:
575 if (rv != NULL)
576 fclose(rv);
577 rv = NULL;
578 if (infd >= 0)
579 close(infd);
580 goto jleave;
582 } else {
583 if ((infd = creat(file, 0666)) == -1) {
584 fclose(rv);
585 rv = NULL;
586 goto jleave;
590 if (infd >= 0)
591 close(infd);
592 fflush(rv);
594 if (!(osflags & O_APPEND))
595 rewind(rv);
596 if ((offset = ftell(rv)) == -1) {
597 Fclose(rv);
598 rv = NULL;
599 goto jleave;
602 register_file(rv, osflags, 0, 0, flags, file, offset, csave);
603 jleave:
604 NYD_LEAVE;
605 return rv;
608 FL FILE *
609 Ftmp(char **fn, char const *prefix, enum oflags oflags, int mode)
611 FILE *fp = NULL;
612 size_t maxname, tries;
613 char *cp_base, *cp;
614 int osoflags, fd, e;
615 NYD_ENTER;
617 assert((oflags & OF_WRONLY) || (oflags & OF_RDWR));
618 assert(!(oflags & OF_RDONLY));
619 assert(!(oflags & OF_REGISTER_UNLINK) || (oflags & OF_REGISTER));
621 e = 0;
622 maxname = NAME_MAX;
623 #ifdef HAVE_PATHCONF
624 { long pc;
626 if ((pc = pathconf(tempdir, _PC_NAME_MAX)) != -1)
627 maxname = (size_t)pc;
629 #endif
631 cp_base =
632 cp = smalloc(strlen(tempdir) + 1 + maxname +1);
633 cp = sstpcpy(cp, tempdir);
634 *cp++ = '/';
636 osoflags = O_CREAT | O_EXCL | _O_CLOEXEC;
637 osoflags |= (oflags & OF_WRONLY) ? O_WRONLY : O_RDWR;
638 if (oflags & OF_APPEND)
639 osoflags |= O_APPEND;
641 for (tries = 0;; ++tries) {
642 size_t i;
643 char *x;
645 x = sstpcpy(cp, UAGENT);
646 *x++ = '-';
647 if (*prefix != '\0') {
648 x = sstpcpy(x, prefix);
649 *x++ = '-';
652 /* Calculate length of a random string addon */
653 i = PTR2SIZE(x - cp);
654 if (i >= maxname >> 1) {
655 x = cp;
656 i = maxname -1;
657 } else
658 i = maxname >> 1;
659 /* But don't be too fatalistic */
660 if (i > 8 && tries < FTMP_OPEN_TRIES / 2)
661 i = 8;
662 memcpy(x, getrandstring(i), i +1);
664 hold_all_sigs();
665 if ((fd = open(cp_base, osoflags, mode)) != -1) {
666 _CLOEXEC_SET(fd);
667 break;
669 if (tries >= FTMP_OPEN_TRIES) {
670 e = errno;
671 goto jfree;
673 rele_all_sigs();
676 if (oflags & OF_REGISTER) {
677 char const *osflags = (oflags & OF_RDWR ? "w+" : "w");
678 int osflagbits;
680 scan_mode(osflags, &osflagbits); /* TODO osoflags&xy ?!!? */
681 if ((fp = fdopen(fd, osflags)) != NULL)
682 register_file(fp, osflagbits | _O_CLOEXEC, 0, 0,
683 (FP_RAW | (oflags & OF_REGISTER_UNLINK ? FP_UNLINK : 0)),
684 cp_base, 0L, NULL);
685 } else
686 fp = fdopen(fd, (oflags & OF_RDWR ? "w+" : "w"));
688 if (fp == NULL || (oflags & OF_UNLINK)) {
689 e = errno;
690 unlink(cp_base);
691 goto jfree;
694 if (fn != NULL)
695 *fn = cp_base;
696 else
697 free(cp_base);
698 jleave:
699 if (fp == NULL || !(oflags & OF_HOLDSIGS))
700 rele_all_sigs();
701 if (fp == NULL)
702 errno = e;
703 NYD_LEAVE;
704 return fp;
705 jfree:
706 if ((cp = cp_base) != NULL)
707 free(cp);
708 goto jleave;
711 FL void
712 Ftmp_release(char **fn)
714 char *cp;
715 NYD_ENTER;
717 cp = *fn;
718 *fn = NULL;
719 if (cp != NULL) {
720 unlink(cp);
721 rele_all_sigs();
722 free(cp);
724 NYD_LEAVE;
727 FL void
728 Ftmp_free(char **fn)
730 char *cp;
731 NYD_ENTER;
733 cp = *fn;
734 *fn = NULL;
735 if (cp != NULL)
736 free(cp);
737 NYD_LEAVE;
740 FL bool_t
741 pipe_cloexec(int fd[2])
743 bool_t rv = FAL0;
744 NYD_ENTER;
746 #ifdef HAVE_PIPE2
747 if (pipe2(fd, O_CLOEXEC) == -1)
748 goto jleave;
749 #else
750 if (pipe(fd) == -1)
751 goto jleave;
752 (void)fcntl(fd[0], F_SETFD, FD_CLOEXEC);
753 (void)fcntl(fd[1], F_SETFD, FD_CLOEXEC);
754 #endif
755 rv = TRU1;
756 jleave:
757 NYD_LEAVE;
758 return rv;
761 FL FILE *
762 Popen(char const *cmd, char const *mode, char const *sh,
763 char const **env_addon, int newfd1)
765 int p[2], myside, hisside, fd0, fd1, pid;
766 char mod[2] = {'0', '\0'};
767 sigset_t nset;
768 FILE *rv = NULL;
769 NYD_ENTER;
771 /* First clean up child structures */
772 { sigset_t oset;
773 struct child **cpp, *cp;
775 sigfillset(&nset);
776 sigprocmask(SIG_BLOCK, &nset, &oset);
778 for (cpp = &_popen_child; *cpp != NULL;) {
779 if ((*cpp)->pid == -1) {
780 cp = *cpp;
781 *cpp = cp->link;
782 free(cp);
783 } else
784 cpp = &(*cpp)->link;
787 sigprocmask(SIG_SETMASK, &oset, NULL);
790 if (!pipe_cloexec(p))
791 goto jleave;
793 if (*mode == 'r') {
794 myside = p[READ];
795 fd0 = -1;
796 hisside = fd1 = p[WRITE];
797 mod[0] = *mode;
798 } else if (*mode == 'W') {
799 myside = p[WRITE];
800 hisside = fd0 = p[READ];
801 fd1 = newfd1;
802 mod[0] = 'w';
803 } else {
804 myside = p[WRITE];
805 hisside = fd0 = p[READ];
806 fd1 = -1;
807 mod[0] = 'w';
810 sigemptyset(&nset);
812 if (cmd == (char*)-1) {
813 if ((pid = fork_child()) == -1)
814 n_perr(_("fork"), 0);
815 else if (pid == 0) {
816 union {char const *ccp; int (*ptf)(void); int es;} u;
817 prepare_child(&nset, fd0, fd1);
818 close(p[READ]);
819 close(p[WRITE]);
820 u.ccp = sh;
821 u.es = (*u.ptf)();
822 _exit(u.es);
824 } else if (sh == NULL) {
825 pid = start_command(cmd, &nset, fd0, fd1, NULL, NULL, NULL, env_addon);
826 } else {
827 pid = start_command(sh, &nset, fd0, fd1, "-c", cmd, NULL, env_addon);
829 if (pid < 0) {
830 close(p[READ]);
831 close(p[WRITE]);
832 goto jleave;
834 close(hisside);
835 if ((rv = fdopen(myside, mod)) != NULL)
836 register_file(rv, 0, 1, pid, FP_RAW, NULL, 0L, NULL);
837 else
838 close(myside);
839 jleave:
840 NYD_LEAVE;
841 return rv;
844 FL bool_t
845 Pclose(FILE *ptr, bool_t dowait)
847 sigset_t nset, oset;
848 int pid;
849 bool_t rv = FAL0;
850 NYD_ENTER;
852 pid = file_pid(ptr);
853 if (pid < 0)
854 goto jleave;
855 unregister_file(ptr);
856 fclose(ptr);
857 if (dowait) {
858 sigemptyset(&nset);
859 sigaddset(&nset, SIGINT);
860 sigaddset(&nset, SIGHUP);
861 sigprocmask(SIG_BLOCK, &nset, &oset);
862 rv = wait_child(pid, NULL);
863 sigprocmask(SIG_SETMASK, &oset, NULL);
864 } else {
865 free_child(pid);
866 rv = TRU1;
868 jleave:
869 NYD_LEAVE;
870 return rv;
873 FL void
874 close_all_files(void)
876 NYD_ENTER;
877 while (fp_head != NULL)
878 if (fp_head->pipe)
879 Pclose(fp_head->fp, TRU1);
880 else
881 Fclose(fp_head->fp);
882 NYD_LEAVE;
885 FL int
886 fork_child(void)
888 struct child *cp;
889 int pid;
890 NYD_ENTER;
892 cp = _findchild(0, TRU1);
894 if ((cp->pid = pid = fork()) == -1) {
895 _delchild(cp);
896 n_perr(_("fork"), 0);
898 NYD_LEAVE;
899 return pid;
902 FL int
903 run_command(char const *cmd, sigset_t *mask, int infd, int outfd,
904 char const *a0, char const *a1, char const *a2, char const **env_addon)
906 int rv;
907 NYD_ENTER;
909 if ((rv = start_command(cmd, mask, infd, outfd, a0, a1, a2, env_addon)) < 0)
910 rv = -1;
911 else
912 rv = wait_command(rv);
913 NYD_LEAVE;
914 return rv;
917 FL int
918 start_command(char const *cmd, sigset_t *mask, int infd, int outfd,
919 char const *a0, char const *a1, char const *a2,
920 char const **env_addon)
922 int rv;
923 NYD_ENTER;
925 if ((rv = fork_child()) == -1) {
926 n_perr(_("fork"), 0);
927 rv = -1;
928 } else if (rv == 0) {
929 char *argv[128];
930 int i;
932 if (env_addon != NULL) { /* TODO env_addon; should have struct child */
933 extern char **environ;
934 size_t ei, ei_orig, ai, ai_orig;
935 char **env;
937 /* TODO note we don't check the POSIX limit:
938 * the total space used to store the environment and the arguments to
939 * the process is limited to {ARG_MAX} bytes */
940 for (ei = 0; environ[ei] != NULL; ++ei)
942 ei_orig = ei;
943 for (ai = 0; env_addon[ai] != NULL; ++ai)
945 ai_orig = ai;
946 env = ac_alloc(sizeof(*env) * (ei + ai +1));
947 memcpy(env, environ, sizeof(*env) * ei);
949 /* Replace all those keys that yet exist */
950 while (ai-- > 0) {
951 char const *ee, *kvs;
952 size_t kl;
954 ee = env_addon[ai];
955 kvs = strchr(ee, '=');
956 assert(kvs != NULL);
957 kl = PTR2SIZE(kvs - ee);
958 assert(kl > 0);
959 for (ei = ei_orig; ei-- > 0;) {
960 char const *ekvs = strchr(env[ei], '=');
961 if (ekvs != NULL && kl == PTR2SIZE(ekvs - env[ei]) &&
962 !memcmp(ee, env[ei], kl)) {
963 env[ei] = UNCONST(ee);
964 env_addon[ai] = NULL;
965 break;
970 /* And append the rest */
971 for (ei = ei_orig, ai = ai_orig; ai-- > 0;)
972 if (env_addon[ai] != NULL)
973 env[ei++] = UNCONST(env_addon[ai]);
975 env[ei] = NULL;
976 environ = env;
979 i = getrawlist(cmd, strlen(cmd), argv, NELEM(argv), 0);
981 if ((argv[i++] = UNCONST(a0)) != NULL &&
982 (argv[i++] = UNCONST(a1)) != NULL &&
983 (argv[i++] = UNCONST(a2)) != NULL)
984 argv[i] = NULL;
985 prepare_child(mask, infd, outfd);
986 execvp(argv[0], argv);
987 perror(argv[0]);
988 _exit(EXIT_ERR);
990 NYD_LEAVE;
991 return rv;
994 FL void
995 prepare_child(sigset_t *nset, int infd, int outfd)
997 int i;
998 sigset_t fset;
999 NYD_ENTER;
1001 /* All file descriptors other than 0, 1, and 2 are supposed to be cloexec */
1002 if (infd >= 0)
1003 dup2(infd, STDIN_FILENO);
1004 if (outfd >= 0)
1005 dup2(outfd, STDOUT_FILENO);
1007 if (nset) {
1008 for (i = 1; i < NSIG; ++i)
1009 if (sigismember(nset, i))
1010 safe_signal(i, SIG_IGN);
1011 if (!sigismember(nset, SIGINT))
1012 safe_signal(SIGINT, SIG_DFL);
1015 sigemptyset(&fset);
1016 sigprocmask(SIG_SETMASK, &fset, NULL);
1017 NYD_LEAVE;
1020 FL void
1021 free_child(int pid)
1023 sigset_t nset, oset;
1024 struct child *cp;
1025 NYD_ENTER;
1027 sigemptyset(&nset);
1028 sigaddset(&nset, SIGCHLD);
1029 sigprocmask(SIG_BLOCK, &nset, &oset);
1031 if ((cp = _findchild(pid, FAL0)) != NULL) {
1032 if (cp->done)
1033 _delchild(cp);
1034 else
1035 cp->free = 1;
1038 sigprocmask(SIG_SETMASK, &oset, NULL);
1039 NYD_LEAVE;
1042 FL bool_t
1043 wait_child(int pid, int *wait_status)
1045 sigset_t nset, oset;
1046 struct child *cp;
1047 int ws;
1048 bool_t rv;
1049 NYD_ENTER;
1051 sigemptyset(&nset);
1052 sigaddset(&nset, SIGCHLD);
1053 sigprocmask(SIG_BLOCK, &nset, &oset);
1055 cp = _findchild(pid, FAL0);
1056 if (cp != NULL) {
1057 while (!cp->done)
1058 sigsuspend(&oset);
1059 ws = cp->status;
1060 _delchild(cp);
1061 } else
1062 ws = 0;
1064 sigprocmask(SIG_SETMASK, &oset, NULL);
1066 if (wait_status != NULL)
1067 *wait_status = ws;
1068 rv = (WIFEXITED(ws) && WEXITSTATUS(ws) == 0);
1069 NYD_LEAVE;
1070 return rv;
1073 /* s-it-mode */