Fix `headers' command documentation+ (Jan Chaloupka)..
[s-mailx.git] / popen.c
blob8473597cc46088674e0141d7e3938045433da796
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 - 2014 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. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
40 #ifndef HAVE_AMALGAMATION
41 # include "nail.h"
42 #endif
44 #include <sys/wait.h>
46 #include <fcntl.h>
48 #define READ 0
49 #define WRITE 1
51 #ifndef O_CLOEXEC
52 # define _OUR_CLOEXEC
53 # define O_CLOEXEC 0
54 # define _SET_CLOEXEC(FD) do fcntl((FD), F_SETFD, FD_CLOEXEC); while (0)
55 #else
56 # define _SET_CLOEXEC(FD) do {} while (0)
57 #endif
59 struct fp {
60 FILE *fp;
61 struct fp *link;
62 char *realfile;
63 long offset;
64 int omode;
65 int pipe;
66 int pid;
67 enum {
68 FP_RAW = 0,
69 FP_GZIP = 1<<0,
70 FP_XZ = 1<<1,
71 FP_BZIP2 = 1<<2,
72 FP_IMAP = 1<<3,
73 FP_MAILDIR = 1<<4,
74 FP_MASK = (1<<5) - 1,
75 FP_READONLY = 1<<5
76 } compressed;
79 struct child {
80 int pid;
81 char done;
82 char free;
83 int status;
84 struct child *link;
87 static struct fp *fp_head;
88 static struct child *_popen_child;
90 static int scan_mode(char const *mode, int *omode);
91 static void register_file(FILE *fp, int omode, int ispipe, int pid,
92 int compressed, char const *realfile, long offset);
93 static enum okay _compress(struct fp *fpp);
94 static int _decompress(int compression, int infd, int outfd);
95 static enum okay unregister_file(FILE *fp);
96 static int file_pid(FILE *fp);
98 /* Handle SIGCHLD */
99 static void _sigchld(int signo);
101 static int wait_command(int pid);
102 static struct child *findchild(int pid);
103 static void delchild(struct child *cp);
105 static int
106 scan_mode(char const *mode, int *omode)
108 static struct {
109 char const mode[4];
110 int omode;
111 } const maps[] = {
112 {"r", O_RDONLY},
113 {"w", O_WRONLY | O_CREAT | O_TRUNC},
114 {"wx", O_WRONLY | O_CREAT | O_EXCL},
115 {"a", O_WRONLY | O_APPEND | O_CREAT},
116 {"a+", O_RDWR | O_APPEND},
117 {"r+", O_RDWR},
118 {"w+", O_RDWR | O_CREAT | O_EXCL}
121 int i;
122 NYD2_ENTER;
124 for (i = 0; UICMP(z, i, <, NELEM(maps)); ++i)
125 if (!strcmp(maps[i].mode, mode)) {
126 *omode = maps[i].omode;
127 i = 0;
128 goto jleave;
131 alert(_("Internal error: bad stdio open mode %s\n"), mode);
132 errno = EINVAL;
133 *omode = 0; /* (silence CC) */
134 i = -1;
135 jleave:
136 NYD2_LEAVE;
137 return i;
140 static void
141 register_file(FILE *fp, int omode, int ispipe, int pid, int compressed,
142 char const *realfile, long offset)
144 struct fp *fpp;
145 NYD_ENTER;
147 fpp = smalloc(sizeof *fpp);
148 fpp->fp = fp;
149 fpp->omode = omode;
150 fpp->pipe = ispipe;
151 fpp->pid = pid;
152 fpp->link = fp_head;
153 fpp->compressed = compressed;
154 fpp->realfile = (realfile != NULL) ? sstrdup(realfile) : NULL;
155 fpp->offset = offset;
156 fp_head = fpp;
157 NYD_LEAVE;
160 static enum okay
161 _compress(struct fp *fpp)
163 char const *cmd[2];
164 int outfd;
165 enum okay rv;
166 NYD_ENTER;
168 if (fpp->omode == O_RDONLY) {
169 rv = OKAY;
170 goto jleave;
172 rv = STOP;
174 fflush(fpp->fp);
175 clearerr(fpp->fp);
176 if (fseek(fpp->fp, fpp->offset, SEEK_SET) == -1)
177 goto jleave;
179 #ifdef HAVE_IMAP
180 if ((fpp->compressed & FP_MASK) == FP_IMAP) {
181 rv = imap_append(fpp->realfile, fpp->fp);
182 goto jleave;
184 #endif
185 if ((fpp->compressed & FP_MASK) == FP_MAILDIR) {
186 rv = maildir_append(fpp->realfile, fpp->fp);
187 goto jleave;
190 outfd = open(fpp->realfile, (fpp->omode | O_CREAT) & ~O_EXCL, 0666);
191 if (outfd == -1) {
192 fprintf(stderr, "Fatal: cannot create ");
193 perror(fpp->realfile);
194 goto jleave;
196 if (!(fpp->omode & O_APPEND))
197 ftruncate(outfd, 0);
198 switch (fpp->compressed & FP_MASK) {
199 case FP_GZIP:
200 cmd[0] = "gzip"; cmd[1] = "-c"; break;
201 case FP_BZIP2:
202 cmd[0] = "bzip2"; cmd[1] = "-c"; break;
203 case FP_XZ:
204 cmd[0] = "xz"; cmd[1] = "-c"; break;
205 default:
206 cmd[0] = "cat"; cmd[1] = NULL; break;
208 if (run_command(cmd[0], 0, fileno(fpp->fp), outfd, cmd[1], NULL, NULL) >= 0)
209 rv = OKAY;
210 close(outfd);
211 jleave:
212 NYD_LEAVE;
213 return rv;
216 static int
217 _decompress(int compression, int infd, int outfd)
219 char const *cmd[2];
220 int rv;
221 NYD_ENTER;
223 switch (compression & FP_MASK) {
224 case FP_GZIP: cmd[0] = "gzip"; cmd[1] = "-cd"; break;
225 case FP_BZIP2: cmd[0] = "bzip2"; cmd[1] = "-cd"; break;
226 case FP_XZ: cmd[0] = "xz"; cmd[1] = "-cd"; break;
227 default: cmd[0] = "cat"; cmd[1] = NULL; break;
228 case FP_IMAP:
229 case FP_MAILDIR:
230 rv = 0;
231 goto jleave;
233 rv = run_command(cmd[0], 0, infd, outfd, cmd[1], NULL, NULL);
234 jleave:
235 NYD_LEAVE;
236 return rv;
239 static enum okay
240 unregister_file(FILE *fp)
242 struct fp **pp, *p;
243 enum okay rv = OKAY;
244 NYD_ENTER;
246 for (pp = &fp_head; (p = *pp) != NULL; pp = &p->link)
247 if (p->fp == fp) {
248 if ((p->compressed & FP_MASK) != FP_RAW) /* TODO ;} */
249 rv = _compress(p);
250 *pp = p->link;
251 if (p->realfile != NULL)
252 free(p->realfile);
253 free(p);
254 goto jleave;
256 #ifdef HAVE_DEBUG
257 panic
258 #else
259 alert
260 #endif
261 (_("Invalid file pointer"));
262 rv = STOP;
263 jleave:
264 NYD_LEAVE;
265 return rv;
268 static int
269 file_pid(FILE *fp)
271 int rv;
272 struct fp *p;
273 NYD2_ENTER;
275 rv = -1;
276 for (p = fp_head; p; p = p->link)
277 if (p->fp == fp) {
278 rv = p->pid;
279 break;
281 NYD2_LEAVE;
282 return rv;
285 static void
286 _sigchld(int signo)
288 int pid, status;
289 struct child *cp;
290 NYD_X; /* Signal handler */
291 UNUSED(signo);
293 for (;;) {
294 pid = (int)waitpid((pid_t)-1, &status, WNOHANG);
295 if (pid <= 0) {
296 if (pid == -1 && errno == EINTR)
297 continue;
298 break;
300 cp = findchild(pid);
301 if (cp->free)
302 delchild(cp);
303 else {
304 cp->done = 1;
305 cp->status = status;
310 static int
311 wait_command(int pid)
313 int rv = 0;
314 NYD_ENTER;
316 if (!wait_child(pid, NULL)) {
317 if (ok_blook(bsdcompat) || ok_blook(bsdmsgs))
318 fprintf(stderr, _("Fatal error in process.\n"));
319 rv = -1;
321 NYD_LEAVE;
322 return rv;
325 static struct child *
326 findchild(int pid)
328 struct child **cpp;
329 NYD_ENTER;
331 for (cpp = &_popen_child; *cpp != NULL && (*cpp)->pid != pid;
332 cpp = &(*cpp)->link)
334 if (*cpp == NULL) {
335 *cpp = smalloc(sizeof **cpp);
336 (*cpp)->pid = pid;
337 (*cpp)->done = (*cpp)->free = 0;
338 (*cpp)->link = NULL;
340 NYD_LEAVE;
341 return *cpp;
344 static void
345 delchild(struct child *cp)
347 struct child **cpp;
348 NYD_ENTER;
350 for (cpp = &_popen_child; *cpp != cp; cpp = &(*cpp)->link)
352 *cpp = cp->link;
353 free(cp);
354 NYD_LEAVE;
357 FL void
358 command_manager_start(void)
360 struct sigaction nact, oact;
361 NYD_ENTER;
363 nact.sa_handler = &_sigchld;
364 sigemptyset(&nact.sa_mask);
365 nact.sa_flags = 0
366 #ifdef SA_RESTART
367 | SA_RESTART
368 #endif
369 #ifdef SA_NOCLDSTOP
370 | SA_NOCLDSTOP
371 #endif
373 if (sigaction(SIGCHLD, &nact, &oact) != 0)
374 panic("Cannot install signal handler for child process management");
375 NYD_LEAVE;
378 FL FILE *
379 safe_fopen(char const *file, char const *oflags, int *xflags)
381 int osflags, fd;
382 FILE *fp = NULL;
383 NYD2_ENTER; /* (only for Fopen() and once in lex.c) */
385 if (scan_mode(oflags, &osflags) < 0)
386 goto jleave;
387 osflags |= O_CLOEXEC;
388 if (xflags != NULL)
389 *xflags = osflags;
391 if ((fd = open(file, osflags, 0666)) == -1)
392 goto jleave;
393 _SET_CLOEXEC(fd);
395 fp = fdopen(fd, oflags);
396 jleave:
397 NYD2_LEAVE;
398 return fp;
401 FL FILE *
402 Fopen(char const *file, char const *oflags)
404 FILE *fp;
405 int osflags;
406 NYD_ENTER;
408 if ((fp = safe_fopen(file, oflags, &osflags)) != NULL)
409 register_file(fp, osflags, 0, 0, FP_RAW, NULL, 0L);
410 NYD_LEAVE;
411 return fp;
414 FL FILE *
415 Fdopen(int fd, char const *oflags)
417 FILE *fp;
418 int osflags;
419 NYD_ENTER;
421 scan_mode(oflags, &osflags);
422 osflags |= O_CLOEXEC;
424 if ((fp = fdopen(fd, oflags)) != NULL)
425 register_file(fp, osflags, 0, 0, FP_RAW, NULL, 0L);
426 NYD_LEAVE;
427 return fp;
430 FL int
431 Fclose(FILE *fp)
433 int i = 0;
434 NYD_ENTER;
436 if (unregister_file(fp) == OKAY)
437 i |= 1;
438 if (fclose(fp) == 0)
439 i |= 2;
440 NYD_LEAVE;
441 return (i == 3 ? 0 : EOF);
444 FL FILE *
445 Zopen(char const *file, char const *oflags, int *compression) /* FIXME MESS! */
447 FILE *rv = NULL;
448 int _compression, osflags, mode, infd;
449 enum oflags rof;
450 long offset;
451 enum protocol p;
452 NYD_ENTER;
454 if (compression == NULL)
455 compression = &_compression;
457 if (scan_mode(oflags, &osflags) < 0)
458 goto jleave;
459 rof = OF_RDWR | OF_UNLINK;
460 if (osflags & O_APPEND)
461 rof |= OF_APPEND;
462 if (osflags == O_RDONLY) {
463 mode = R_OK;
464 *compression = FP_READONLY;
465 } else {
466 mode = R_OK | W_OK;
467 *compression = 0;
470 /* TODO ???? */
471 if ((osflags & O_APPEND) && ((p = which_protocol(file)) == PROTO_IMAP ||
472 p == PROTO_MAILDIR)) {
473 *compression |= (p == PROTO_IMAP) ? FP_IMAP : FP_MAILDIR;
474 osflags = O_RDWR | O_APPEND | O_CREAT;
475 infd = -1;
476 } else {
477 char const *ext;
479 if ((ext = strrchr(file, '.')) != NULL) {
480 if (!strcmp(ext, ".gz"))
481 *compression |= FP_GZIP;
482 else if (!strcmp(ext, ".xz"))
483 *compression |= FP_XZ;
484 else if (!strcmp(ext, ".bz2"))
485 *compression |= FP_BZIP2;
486 else
487 goto jraw;
488 } else {
489 jraw:
490 *compression |= FP_RAW;
491 rv = Fopen(file, oflags);
492 goto jleave;
494 if ((infd = open(file, (mode & W_OK) ? O_RDWR : O_RDONLY)) == -1 &&
495 (!(osflags & O_CREAT) || errno != ENOENT))
496 goto jleave;
499 if ((rv = Ftmp(NULL, "zopen", rof, 0600)) == NULL) {
500 perror(_("tmpfile"));
501 goto jerr;
503 if (infd >= 0 || (*compression & FP_MASK) == FP_IMAP ||
504 (*compression & FP_MASK) == FP_MAILDIR) {
505 if (_decompress(*compression, infd, fileno(rv)) < 0) {
506 jerr:
507 if (rv != NULL)
508 Fclose(rv);
509 rv = NULL;
510 if (infd >= 0)
511 close(infd);
512 goto jleave;
514 } else {
515 if ((infd = creat(file, 0666)) == -1) {
516 Fclose(rv);
517 rv = NULL;
518 goto jleave;
521 if (infd >= 0)
522 close(infd);
523 fflush(rv);
525 if (!(osflags & O_APPEND))
526 rewind(rv);
527 if ((offset = ftell(rv)) == -1) {
528 Fclose(rv);
529 rv = NULL;
530 goto jleave;
532 register_file(rv, osflags, 0, 0, *compression, file, offset);
533 jleave:
534 NYD_LEAVE;
535 return rv;
538 FL FILE *
539 Ftmp(char **fn, char const *prefix, enum oflags oflags, int mode)
541 FILE *fp = NULL;
542 char *cp_base, *cp;
543 int fd;
544 NYD_ENTER;
546 cp_base =
547 cp = smalloc(strlen(tempdir) + 1 + sizeof("mail") + strlen(prefix) + 7 +1);
548 cp = sstpcpy(cp, tempdir);
549 *cp++ = '/';
550 cp = sstpcpy(cp, "mail");
551 if (*prefix) {
552 *cp++ = '-';
553 cp = sstpcpy(cp, prefix);
555 /* TODO Ftmp(): unroll our own creation loop with atoi(random()) */
556 sstpcpy(cp, ".XXXXXX");
558 hold_all_sigs();
559 #ifdef HAVE_MKSTEMP
560 if ((fd = mkstemp(cp_base)) == -1)
561 goto jfree;
562 if (mode != (S_IRUSR | S_IWUSR) && fchmod(fd, mode) == -1)
563 goto jclose;
564 if (oflags & OF_APPEND) {
565 int f;
567 if ((f = fcntl(fd, F_GETFL)) == -1 ||
568 fcntl(fd, F_SETFL, f | O_APPEND) == -1) {
569 jclose:
570 close(fd);
571 goto junlink;
574 if (!(oflags & OF_REGISTER))
575 fcntl(fd, F_SETFD, FD_CLOEXEC);
576 #else
577 if (mktemp(cp_base) == NULL)
578 goto jfree;
579 if ((fd = open(cp_base, O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC |
580 (oflags & OF_APPEND ? O_APPEND : 0), mode)) == -1)
581 goto junlink;
582 if (!(oflags & OF_REGISTER))
583 _SET_CLOEXEC(fd);
584 #endif
586 fp = (*((oflags & OF_REGISTER) ? &Fdopen : &fdopen))(fd,
587 (oflags & OF_RDWR ? "w+" : "w"));
588 if (fp == NULL || (oflags & OF_UNLINK)) {
589 junlink:
590 unlink(cp_base);
591 goto jfree;
594 if (fn != NULL)
595 *fn = cp_base;
596 jleave:
597 if (fp == NULL || !(oflags & OF_HOLDSIGS))
598 rele_all_sigs();
599 NYD_LEAVE;
600 return fp;
601 jfree:
602 if ((cp = cp_base) != NULL)
603 free(cp);
604 goto jleave;
607 FL void
608 Ftmp_release(char **fn)
610 char *cp;
611 NYD_ENTER;
613 cp = *fn;
614 *fn = NULL;
615 if (cp != NULL) {
616 unlink(cp);
617 rele_all_sigs();
618 free(cp);
620 NYD_LEAVE;
623 FL void
624 Ftmp_free(char **fn)
626 char *cp;
627 NYD_ENTER;
629 cp = *fn;
630 *fn = NULL;
631 if (cp != NULL)
632 free(cp);
633 NYD_LEAVE;
636 FL bool_t
637 pipe_cloexec(int fd[2])
639 bool_t rv = FAL0;
640 NYD_ENTER;
642 #ifdef HAVE_PIPE2
643 if (pipe2(fd, O_CLOEXEC) == -1)
644 goto jleave;
645 #else
646 if (pipe(fd) == -1)
647 goto jleave;
648 fcntl(fd[0], F_SETFD, FD_CLOEXEC);
649 fcntl(fd[1], F_SETFD, FD_CLOEXEC);
650 #endif
651 rv = TRU1;
652 jleave:
653 NYD_LEAVE;
654 return rv;
657 FL FILE *
658 Popen(char const *cmd, char const *mode, char const *sh,
659 char const **env_addon, int newfd1)
661 int p[2], myside, hisside, fd0, fd1, pid;
662 char mod[2] = {'0', '\0'};
663 sigset_t nset;
664 FILE *rv = NULL;
665 NYD_ENTER;
667 if (!pipe_cloexec(p))
668 goto jleave;
670 if (*mode == 'r') {
671 myside = p[READ];
672 fd0 = -1;
673 hisside = fd1 = p[WRITE];
674 mod[0] = *mode;
675 } else if (*mode == 'W') {
676 myside = p[WRITE];
677 hisside = fd0 = p[READ];
678 fd1 = newfd1;
679 mod[0] = 'w';
680 } else {
681 myside = p[WRITE];
682 hisside = fd0 = p[READ];
683 fd1 = -1;
684 mod[0] = 'w';
686 sigemptyset(&nset);
687 if (sh == NULL) {
688 pid = start_command(cmd, &nset, fd0, fd1, NULL, NULL, NULL, env_addon);
689 } else {
690 pid = start_command(sh, &nset, fd0, fd1, "-c", cmd, NULL, env_addon);
692 if (pid < 0) {
693 close(p[READ]);
694 close(p[WRITE]);
695 goto jleave;
697 close(hisside);
698 if ((rv = fdopen(myside, mod)) != NULL)
699 register_file(rv, 0, 1, pid, FP_RAW, NULL, 0L);
700 jleave:
701 NYD_LEAVE;
702 return rv;
705 FL bool_t
706 Pclose(FILE *ptr, bool_t dowait)
708 sigset_t nset, oset;
709 int pid;
710 bool_t rv = FAL0;
711 NYD_ENTER;
713 pid = file_pid(ptr);
714 if (pid < 0)
715 goto jleave;
716 unregister_file(ptr);
717 fclose(ptr);
718 if (dowait) {
719 sigemptyset(&nset);
720 sigaddset(&nset, SIGINT);
721 sigaddset(&nset, SIGHUP);
722 sigprocmask(SIG_BLOCK, &nset, &oset);
723 rv = wait_child(pid, NULL);
724 sigprocmask(SIG_SETMASK, &oset, NULL);
725 } else {
726 free_child(pid);
727 rv = TRU1;
729 jleave:
730 NYD_LEAVE;
731 return rv;
734 FL void
735 close_all_files(void)
737 NYD_ENTER;
738 while (fp_head != NULL)
739 if (fp_head->pipe)
740 Pclose(fp_head->fp, TRU1);
741 else
742 Fclose(fp_head->fp);
743 NYD_LEAVE;
746 FL int
747 run_command(char const *cmd, sigset_t *mask, int infd, int outfd,
748 char const *a0, char const *a1, char const *a2)
750 int rv;
751 NYD_ENTER;
753 if ((rv = start_command(cmd, mask, infd, outfd, a0, a1, a2, NULL)) < 0)
754 rv = -1;
755 else
756 rv = wait_command(rv);
757 NYD_LEAVE;
758 return rv;
761 FL int
762 start_command(char const *cmd, sigset_t *mask, int infd, int outfd,
763 char const *a0, char const *a1, char const *a2,
764 char const **env_addon)
766 int rv;
767 NYD_ENTER;
769 if ((rv = fork()) == -1) {
770 perror("fork");
771 rv = -1;
772 } else if (rv == 0) {
773 char *argv[128];
774 int i;
776 if (env_addon != NULL) { /* TODO env_addon; should have struct child */
777 extern char **environ;
778 size_t ei, ei_orig, ai, ai_orig;
779 char **env;
781 /* TODO note we don't check the POSIX limit:
782 * the total space used to store the environment and the arguments to
783 * the process is limited to {ARG_MAX} bytes */
784 for (ei = 0; environ[ei] != NULL; ++ei)
786 ei_orig = ei;
787 for (ai = 0; env_addon[ai] != NULL; ++ai)
789 ai_orig = ai;
790 env = ac_alloc(sizeof(*env) * (ei + ai +1));
791 memcpy(env, environ, sizeof(*env) * ei);
793 /* Replace all those keys that yet exist */
794 while (ai-- > 0) {
795 char const *ee, *kvs;
796 size_t kl;
798 ee = env_addon[ai];
799 kvs = strchr(ee, '=');
800 assert(kvs != NULL);
801 kl = PTR2SIZE(kvs - ee);
802 assert(kl > 0);
803 for (ei = ei_orig; ei-- > 0;) {
804 char const *ekvs = strchr(env[ei], '=');
805 if (ekvs != NULL && kl == PTR2SIZE(ekvs - env[ei]) &&
806 !memcmp(ee, env[ei], kl)) {
807 env[ei] = UNCONST(ee);
808 env_addon[ai] = NULL;
809 break;
814 /* And append the rest */
815 for (ei = ei_orig, ai = ai_orig; ai-- > 0;)
816 if (env_addon[ai] != NULL)
817 env[ei++] = UNCONST(env_addon[ai]);
819 env[ei] = NULL;
820 environ = env;
823 i = getrawlist(cmd, strlen(cmd), argv, NELEM(argv), 0);
825 if ((argv[i++] = UNCONST(a0)) != NULL &&
826 (argv[i++] = UNCONST(a1)) != NULL &&
827 (argv[i++] = UNCONST(a2)) != NULL)
828 argv[i] = NULL;
829 prepare_child(mask, infd, outfd);
830 execvp(argv[0], argv);
831 perror(argv[0]);
832 _exit(1);
834 NYD_LEAVE;
835 return rv;
838 FL void
839 prepare_child(sigset_t *nset, int infd, int outfd)
841 int i;
842 sigset_t fset;
843 NYD_ENTER;
845 /* All file descriptors other than 0, 1, and 2 are supposed to be cloexec */
846 if (infd >= 0)
847 dup2(infd, 0);
848 if (outfd >= 0)
849 dup2(outfd, 1);
851 if (nset) {
852 for (i = 1; i < NSIG; ++i)
853 if (sigismember(nset, i))
854 safe_signal(i, SIG_IGN);
855 if (!sigismember(nset, SIGINT))
856 safe_signal(SIGINT, SIG_DFL);
859 sigemptyset(&fset);
860 sigprocmask(SIG_SETMASK, &fset, NULL);
861 NYD_LEAVE;
864 FL void
865 free_child(int pid)
867 sigset_t nset, oset;
868 struct child *cp;
869 NYD_ENTER;
871 sigemptyset(&nset);
872 sigaddset(&nset, SIGCHLD);
873 sigprocmask(SIG_BLOCK, &nset, &oset);
875 cp = findchild(pid);
876 if (cp->done)
877 delchild(cp);
878 else
879 cp->free = 1;
881 sigprocmask(SIG_SETMASK, &oset, NULL);
882 NYD_LEAVE;
885 FL bool_t
886 wait_child(int pid, int *wait_status)
888 sigset_t nset, oset;
889 struct child *cp;
890 int ws;
891 bool_t rv;
892 NYD_ENTER;
894 sigemptyset(&nset);
895 sigaddset(&nset, SIGCHLD);
896 sigprocmask(SIG_BLOCK, &nset, &oset);
898 cp = findchild(pid);
899 while (!cp->done)
900 sigsuspend(&oset);
901 ws = cp->status;
902 delchild(cp);
904 sigprocmask(SIG_SETMASK, &oset, NULL);
906 if (wait_status != NULL)
907 *wait_status = ws;
908 rv = (WIFEXITED(ws) && WEXITSTATUS(ws) == 0);
909 NYD_LEAVE;
910 return rv;
913 #ifdef _OUR_CLOEXEC
914 # undef O_CLOEXEC
915 # undef _OUR_CLOEXEC
916 #endif
917 #undef _SET_CLOEXEC
919 /* s-it-mode */