FIX: really_rewind() for pre POSIX Issue 7
[s-mailx.git] / popen.c
blob7ea46b376d202e0bba182821aa97d1079ee14bd0
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;
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 goto jleave;
171 #ifdef HAVE_IMAP
172 if ((fpp->flags & FP_MASK) == FP_IMAP) {
173 rv = imap_append(fpp->realfile, fpp->fp);
174 goto jleave;
176 #endif
177 if ((fpp->flags & FP_MASK) == FP_MAILDIR) {
178 rv = maildir_append(fpp->realfile, fpp->fp, fpp->offset);
179 goto jleave;
182 outfd = open(fpp->realfile, (fpp->omode | O_CREAT) & ~O_EXCL, 0666);
183 if (outfd == -1) {
184 n_err(_("Fatal: cannot create \"%s\": %s\n"),
185 fpp->realfile, strerror(errno));
186 goto jleave;
188 if (!(fpp->omode & O_APPEND))
189 ftruncate(outfd, 0);
191 cmd[2] = NULL;
192 switch (fpp->flags & FP_MASK) {
193 case FP_GZIP:
194 cmd[0] = "gzip"; cmd[1] = "-c"; break;
195 case FP_BZIP2:
196 cmd[0] = "bzip2"; cmd[1] = "-c"; break;
197 case FP_XZ:
198 cmd[0] = "xz"; cmd[1] = "-c"; break;
199 default:
200 cmd[0] = "cat"; cmd[1] = NULL; break;
201 case FP_HOOK:
202 if ((cmd[0] = ok_vlook(SHELL)) == NULL)
203 cmd[0] = XSHELL;
204 cmd[1] = "-c";
205 cmd[2] = fpp->save_cmd;
207 if (run_command(cmd[0], 0, fileno(fpp->fp), outfd, cmd[1], cmd[2], NULL)
208 >= 0)
209 rv = OKAY;
210 close(outfd);
211 jleave:
212 NYD_LEAVE;
213 return rv;
216 static int
217 _file_load(int flags, int infd, int outfd, char const *load_cmd)
219 char const *cmd[3];
220 int rv;
221 NYD_ENTER;
223 cmd[2] = NULL;
224 switch (flags & FP_MASK) {
225 case FP_GZIP: cmd[0] = "gzip"; cmd[1] = "-cd"; break;
226 case FP_BZIP2: cmd[0] = "bzip2"; cmd[1] = "-cd"; break;
227 case FP_XZ: cmd[0] = "xz"; cmd[1] = "-cd"; break;
228 default: cmd[0] = "cat"; cmd[1] = NULL; break;
229 case FP_HOOK:
230 if ((cmd[0] = ok_vlook(SHELL)) == NULL)
231 cmd[0] = XSHELL;
232 cmd[1] = "-c";
233 cmd[2] = load_cmd;
234 break;
235 case FP_MAILDIR:
236 case FP_IMAP:
237 rv = 0;
238 goto jleave;
241 rv = run_command(cmd[0], 0, infd, outfd, cmd[1], cmd[2], NULL);
242 jleave:
243 NYD_LEAVE;
244 return rv;
247 static enum okay
248 unregister_file(FILE *fp)
250 struct fp **pp, *p;
251 enum okay rv = OKAY;
252 NYD_ENTER;
254 for (pp = &fp_head; (p = *pp) != NULL; pp = &p->link)
255 if (p->fp == fp) {
256 if ((p->flags & FP_MASK) != FP_RAW) /* TODO ;} */
257 rv = _file_save(p);
258 *pp = p->link;
259 if (p->save_cmd != NULL)
260 free(p->save_cmd);
261 if (p->realfile != NULL)
262 free(p->realfile);
263 free(p);
264 goto jleave;
266 DBGOR(n_panic, n_alert)(_("Invalid file pointer"));
267 rv = STOP;
268 jleave:
269 NYD_LEAVE;
270 return rv;
273 static int
274 file_pid(FILE *fp)
276 int rv;
277 struct fp *p;
278 NYD2_ENTER;
280 rv = -1;
281 for (p = fp_head; p; p = p->link)
282 if (p->fp == fp) {
283 rv = p->pid;
284 break;
286 NYD2_LEAVE;
287 return rv;
290 static void
291 _sigchld(int signo)
293 pid_t pid;
294 int status;
295 struct child *cp;
296 NYD_X; /* Signal handler */
297 UNUSED(signo);
299 for (;;) {
300 pid = waitpid(-1, &status, WNOHANG);
301 if (pid <= 0) {
302 if (pid == -1 && errno == EINTR)
303 continue;
304 break;
307 if ((cp = _findchild(pid, FAL0)) != NULL) {
308 if (cp->free)
309 cp->pid = -1; /* XXX Was _delchild(cp);# */
310 else {
311 cp->done = 1;
312 cp->status = status;
318 static int
319 wait_command(int pid)
321 int rv = 0;
322 NYD_ENTER;
324 if (!wait_child(pid, NULL)) {
325 if (ok_blook(bsdcompat) || ok_blook(bsdmsgs))
326 n_err(_("Fatal error in process\n"));
327 rv = -1;
329 NYD_LEAVE;
330 return rv;
333 static struct child *
334 _findchild(int pid, bool_t create)
336 struct child **cpp;
337 NYD_ENTER;
339 for (cpp = &_popen_child; *cpp != NULL && (*cpp)->pid != pid;
340 cpp = &(*cpp)->link)
343 if (*cpp == NULL && create) {
344 *cpp = smalloc(sizeof **cpp);
345 (*cpp)->pid = pid;
346 (*cpp)->done = (*cpp)->free = 0;
347 (*cpp)->link = NULL;
349 NYD_LEAVE;
350 return *cpp;
353 static void
354 _delchild(struct child *cp)
356 struct child **cpp;
357 NYD_ENTER;
359 cpp = &_popen_child;
360 for (;;) {
361 if (*cpp == cp) {
362 *cpp = cp->link;
363 free(cp);
364 break;
366 if (*(cpp = &(*cpp)->link) == NULL) {
367 DBG( n_err("! popen.c:_delchild(): implementation error\n"); )
368 break;
371 NYD_LEAVE;
374 FL void
375 command_manager_start(void)
377 struct sigaction nact, oact;
378 NYD_ENTER;
380 nact.sa_handler = &_sigchld;
381 sigemptyset(&nact.sa_mask);
382 nact.sa_flags = 0
383 #ifdef SA_RESTART
384 | SA_RESTART
385 #endif
386 #ifdef SA_NOCLDSTOP
387 | SA_NOCLDSTOP
388 #endif
390 if (sigaction(SIGCHLD, &nact, &oact) != 0)
391 n_panic(_("Cannot install signal handler for child process management"));
392 NYD_LEAVE;
395 FL FILE *
396 safe_fopen(char const *file, char const *oflags, int *xflags)
398 int osflags, fd;
399 FILE *fp = NULL;
400 NYD2_ENTER; /* (only for Fopen() and once in lex.c) */
402 if (scan_mode(oflags, &osflags) < 0)
403 goto jleave;
404 osflags |= _O_CLOEXEC;
405 if (xflags != NULL)
406 *xflags = osflags;
408 if ((fd = open(file, osflags, 0666)) == -1)
409 goto jleave;
410 _CLOEXEC_SET(fd);
412 fp = fdopen(fd, oflags);
413 jleave:
414 NYD2_LEAVE;
415 return fp;
418 FL FILE *
419 Fopen(char const *file, char const *oflags)
421 FILE *fp;
422 int osflags;
423 NYD_ENTER;
425 if ((fp = safe_fopen(file, oflags, &osflags)) != NULL)
426 register_file(fp, osflags, 0, 0, FP_RAW, NULL, 0L, NULL);
427 NYD_LEAVE;
428 return fp;
431 FL FILE *
432 Fdopen(int fd, char const *oflags, bool_t nocloexec)
434 FILE *fp;
435 int osflags;
436 NYD_ENTER;
438 scan_mode(oflags, &osflags);
439 if (!nocloexec)
440 osflags |= _O_CLOEXEC; /* Ensured to be set by caller as documented! */
442 if ((fp = fdopen(fd, oflags)) != NULL)
443 register_file(fp, osflags, 0, 0, FP_RAW, NULL, 0L, NULL);
444 NYD_LEAVE;
445 return fp;
448 FL int
449 Fclose(FILE *fp)
451 int i = 0;
452 NYD_ENTER;
454 if (unregister_file(fp) == OKAY)
455 i |= 1;
456 if (fclose(fp) == 0)
457 i |= 2;
458 NYD_LEAVE;
459 return (i == 3 ? 0 : EOF);
462 FL FILE *
463 Zopen(char const *file, char const *oflags) /* FIXME MESS! */
465 FILE *rv = NULL;
466 char const *cload = NULL, *csave = NULL;
467 int flags, osflags, mode, infd;
468 enum oflags rof;
469 long offset;
470 enum protocol p;
471 NYD_ENTER;
473 if (scan_mode(oflags, &osflags) < 0)
474 goto jleave;
476 flags = 0;
477 rof = OF_RDWR | OF_UNLINK;
478 if (osflags & O_APPEND)
479 rof |= OF_APPEND;
480 mode = (osflags == O_RDONLY) ? R_OK : R_OK | W_OK;
482 if ((osflags & O_APPEND) && ((p = which_protocol(file)) == PROTO_IMAP ||
483 p == PROTO_MAILDIR)) {
484 flags |= (p == PROTO_IMAP) ? FP_IMAP : FP_MAILDIR;
485 osflags = O_RDWR | O_APPEND | O_CREAT;
486 infd = -1;
487 } else {
488 char const *ext;
490 if ((ext = strrchr(file, '.')) != NULL) {
491 if (!strcmp(ext, ".gz"))
492 flags |= FP_GZIP;
493 else if (!strcmp(ext, ".xz"))
494 flags |= FP_XZ;
495 else if (!strcmp(ext, ".bz2"))
496 flags |= FP_BZIP2;
497 else {
498 #undef _X1
499 #define _X1 "file-hook-load-"
500 #undef _X2
501 #define _X2 "file-hook-save-"
502 size_t l = strlen(++ext);
503 char *vbuf = ac_alloc(l + MAX(sizeof(_X1), sizeof(_X2)));
505 memcpy(vbuf, _X1, sizeof(_X1) -1);
506 memcpy(vbuf + sizeof(_X1) -1, ext, l);
507 vbuf[sizeof(_X1) -1 + l] = '\0';
508 cload = vok_vlook(vbuf);
509 memcpy(vbuf, _X2, sizeof(_X2) -1);
510 memcpy(vbuf + sizeof(_X2) -1, ext, l);
511 vbuf[sizeof(_X2) -1 + l] = '\0';
512 csave = vok_vlook(vbuf);
513 #undef _X2
514 #undef _X1
515 ac_free(vbuf);
517 if ((csave != NULL) && (cload != NULL))
518 flags |= FP_HOOK;
519 else if ((csave != NULL) | (cload != NULL)) {
520 n_alert(_("Only one of *mailbox-(load|save)-%s* is set! "
521 "Treating as plain text!"), ext);
522 goto jraw;
523 } else
524 goto jraw;
526 } else {
527 jraw:
528 /*flags |= FP_RAW;*/
529 rv = Fopen(file, oflags);
530 goto jleave;
533 if ((infd = open(file, (mode & W_OK) ? O_RDWR : O_RDONLY)) == -1 &&
534 (!(osflags & O_CREAT) || errno != ENOENT))
535 goto jleave;
538 /* Note rv is not yet register_file()d, fclose() it in error path! */
539 if ((rv = Ftmp(NULL, "zopen", rof, 0600)) == NULL) {
540 n_perr(_("tmpfile"), 0);
541 goto jerr;
544 if (flags & (FP_IMAP | FP_MAILDIR))
546 else if (infd >= 0) {
547 if (_file_load(flags, infd, fileno(rv), cload) < 0) {
548 jerr:
549 if (rv != NULL)
550 fclose(rv);
551 rv = NULL;
552 if (infd >= 0)
553 close(infd);
554 goto jleave;
556 } else {
557 if ((infd = creat(file, 0666)) == -1) {
558 fclose(rv);
559 rv = NULL;
560 goto jleave;
564 if (infd >= 0)
565 close(infd);
566 fflush(rv);
568 if (!(osflags & O_APPEND))
569 rewind(rv);
570 if ((offset = ftell(rv)) == -1) {
571 Fclose(rv);
572 rv = NULL;
573 goto jleave;
576 register_file(rv, osflags, 0, 0, flags, file, offset, csave);
577 jleave:
578 NYD_LEAVE;
579 return rv;
582 FL FILE *
583 Ftmp(char **fn, char const *prefix, enum oflags oflags, int mode)
585 FILE *fp = NULL;
586 size_t maxname, tries;
587 char *cp_base, *cp;
588 int osoflags, fd, e;
589 NYD_ENTER;
591 assert((oflags & OF_WRONLY) || (oflags & OF_RDWR));
592 assert(!(oflags & OF_RDONLY));
594 e = 0;
595 maxname = NAME_MAX;
596 #ifdef HAVE_PATHCONF
597 { long pc;
599 if ((pc = pathconf(tempdir, _PC_NAME_MAX)) != -1)
600 maxname = (size_t)pc;
602 #endif
604 cp_base =
605 cp = smalloc(strlen(tempdir) + 1 + maxname +1);
606 cp = sstpcpy(cp, tempdir);
607 *cp++ = '/';
609 osoflags = O_CREAT | O_EXCL | _O_CLOEXEC;
610 osoflags |= (oflags & OF_WRONLY) ? O_WRONLY : O_RDWR;
611 if (oflags & OF_APPEND)
612 osoflags |= O_APPEND;
614 for (tries = 0;; ++tries) {
615 size_t i;
616 char *x;
618 x = sstpcpy(cp, UAGENT);
619 *x++ = '-';
620 if (*prefix != '\0') {
621 x = sstpcpy(x, prefix);
622 *x++ = '-';
625 /* Calculate length of a random string addon */
626 i = PTR2SIZE(x - cp);
627 if (i >= maxname >> 1) {
628 x = cp;
629 i = maxname -1;
630 } else
631 i = maxname >> 1;
632 /* But don't be too fatalistic */
633 if (i > 8 && tries < FTMP_OPEN_TRIES / 2)
634 i = 8;
635 memcpy(x, getrandstring(i), i +1);
637 hold_all_sigs();
638 if ((fd = open(cp_base, osoflags, mode)) != -1) {
639 _CLOEXEC_SET(fd);
640 break;
642 if (tries >= FTMP_OPEN_TRIES) {
643 e = errno;
644 goto jfree;
646 rele_all_sigs();
649 if (oflags & OF_REGISTER)
650 fp = Fdopen(fd, (oflags & OF_RDWR ? "w+" : "w"), FAL0);
651 else
652 fp = fdopen(fd, (oflags & OF_RDWR ? "w+" : "w"));
654 if (fp == NULL || (oflags & OF_UNLINK)) {
655 e = errno;
656 unlink(cp_base);
657 goto jfree;
660 if (fn != NULL)
661 *fn = cp_base;
662 else
663 free(cp_base);
664 jleave:
665 if (fp == NULL || !(oflags & OF_HOLDSIGS))
666 rele_all_sigs();
667 if (fp == NULL)
668 errno = e;
669 NYD_LEAVE;
670 return fp;
671 jfree:
672 if ((cp = cp_base) != NULL)
673 free(cp);
674 goto jleave;
677 FL void
678 Ftmp_release(char **fn)
680 char *cp;
681 NYD_ENTER;
683 cp = *fn;
684 *fn = NULL;
685 if (cp != NULL) {
686 unlink(cp);
687 rele_all_sigs();
688 free(cp);
690 NYD_LEAVE;
693 FL void
694 Ftmp_free(char **fn)
696 char *cp;
697 NYD_ENTER;
699 cp = *fn;
700 *fn = NULL;
701 if (cp != NULL)
702 free(cp);
703 NYD_LEAVE;
706 FL bool_t
707 pipe_cloexec(int fd[2])
709 bool_t rv = FAL0;
710 NYD_ENTER;
712 #ifdef HAVE_PIPE2
713 if (pipe2(fd, O_CLOEXEC) == -1)
714 goto jleave;
715 #else
716 if (pipe(fd) == -1)
717 goto jleave;
718 (void)fcntl(fd[0], F_SETFD, FD_CLOEXEC);
719 (void)fcntl(fd[1], F_SETFD, FD_CLOEXEC);
720 #endif
721 rv = TRU1;
722 jleave:
723 NYD_LEAVE;
724 return rv;
727 FL FILE *
728 Popen(char const *cmd, char const *mode, char const *sh,
729 char const **env_addon, int newfd1)
731 int p[2], myside, hisside, fd0, fd1, pid;
732 char mod[2] = {'0', '\0'};
733 sigset_t nset;
734 FILE *rv = NULL;
735 NYD_ENTER;
737 /* First clean up child structures */
738 { sigset_t oset;
739 struct child **cpp, *cp;
741 sigfillset(&nset);
742 sigprocmask(SIG_BLOCK, &nset, &oset);
744 for (cpp = &_popen_child; *cpp != NULL;) {
745 if ((*cpp)->pid == -1) {
746 cp = *cpp;
747 *cpp = cp->link;
748 free(cp);
749 } else
750 cpp = &(*cpp)->link;
753 sigprocmask(SIG_SETMASK, &oset, NULL);
756 if (!pipe_cloexec(p))
757 goto jleave;
759 if (*mode == 'r') {
760 myside = p[READ];
761 fd0 = -1;
762 hisside = fd1 = p[WRITE];
763 mod[0] = *mode;
764 } else if (*mode == 'W') {
765 myside = p[WRITE];
766 hisside = fd0 = p[READ];
767 fd1 = newfd1;
768 mod[0] = 'w';
769 } else {
770 myside = p[WRITE];
771 hisside = fd0 = p[READ];
772 fd1 = -1;
773 mod[0] = 'w';
776 sigemptyset(&nset);
778 if (cmd == (char*)-1) {
779 if ((pid = fork_child()) == -1)
780 n_perr(_("fork"), 0);
781 else if (pid == 0) {
782 union {char const *ccp; int (*ptf)(void); int es;} u;
783 prepare_child(&nset, fd0, fd1);
784 close(p[READ]);
785 close(p[WRITE]);
786 u.ccp = sh;
787 u.es = (*u.ptf)();
788 _exit(u.es);
790 } else if (sh == NULL) {
791 pid = start_command(cmd, &nset, fd0, fd1, NULL, NULL, NULL, env_addon);
792 } else {
793 pid = start_command(sh, &nset, fd0, fd1, "-c", cmd, NULL, env_addon);
795 if (pid < 0) {
796 close(p[READ]);
797 close(p[WRITE]);
798 goto jleave;
800 close(hisside);
801 if ((rv = fdopen(myside, mod)) != NULL)
802 register_file(rv, 0, 1, pid, FP_RAW, NULL, 0L, NULL);
803 else
804 close(myside);
805 jleave:
806 NYD_LEAVE;
807 return rv;
810 FL bool_t
811 Pclose(FILE *ptr, bool_t dowait)
813 sigset_t nset, oset;
814 int pid;
815 bool_t rv = FAL0;
816 NYD_ENTER;
818 pid = file_pid(ptr);
819 if (pid < 0)
820 goto jleave;
821 unregister_file(ptr);
822 fclose(ptr);
823 if (dowait) {
824 sigemptyset(&nset);
825 sigaddset(&nset, SIGINT);
826 sigaddset(&nset, SIGHUP);
827 sigprocmask(SIG_BLOCK, &nset, &oset);
828 rv = wait_child(pid, NULL);
829 sigprocmask(SIG_SETMASK, &oset, NULL);
830 } else {
831 free_child(pid);
832 rv = TRU1;
834 jleave:
835 NYD_LEAVE;
836 return rv;
839 FL void
840 close_all_files(void)
842 NYD_ENTER;
843 while (fp_head != NULL)
844 if (fp_head->pipe)
845 Pclose(fp_head->fp, TRU1);
846 else
847 Fclose(fp_head->fp);
848 NYD_LEAVE;
851 FL int
852 fork_child(void)
854 struct child *cp;
855 int pid;
856 NYD_ENTER;
858 cp = _findchild(0, TRU1);
860 if ((cp->pid = pid = fork()) == -1) {
861 _delchild(cp);
862 n_perr(_("fork"), 0);
864 NYD_LEAVE;
865 return pid;
868 FL int
869 run_command(char const *cmd, sigset_t *mask, int infd, int outfd,
870 char const *a0, char const *a1, char const *a2)
872 int rv;
873 NYD_ENTER;
875 if ((rv = start_command(cmd, mask, infd, outfd, a0, a1, a2, NULL)) < 0)
876 rv = -1;
877 else
878 rv = wait_command(rv);
879 NYD_LEAVE;
880 return rv;
883 FL int
884 start_command(char const *cmd, sigset_t *mask, int infd, int outfd,
885 char const *a0, char const *a1, char const *a2,
886 char const **env_addon)
888 int rv;
889 NYD_ENTER;
891 if ((rv = fork_child()) == -1) {
892 n_perr(_("fork"), 0);
893 rv = -1;
894 } else if (rv == 0) {
895 char *argv[128];
896 int i;
898 if (env_addon != NULL) { /* TODO env_addon; should have struct child */
899 extern char **environ;
900 size_t ei, ei_orig, ai, ai_orig;
901 char **env;
903 /* TODO note we don't check the POSIX limit:
904 * the total space used to store the environment and the arguments to
905 * the process is limited to {ARG_MAX} bytes */
906 for (ei = 0; environ[ei] != NULL; ++ei)
908 ei_orig = ei;
909 for (ai = 0; env_addon[ai] != NULL; ++ai)
911 ai_orig = ai;
912 env = ac_alloc(sizeof(*env) * (ei + ai +1));
913 memcpy(env, environ, sizeof(*env) * ei);
915 /* Replace all those keys that yet exist */
916 while (ai-- > 0) {
917 char const *ee, *kvs;
918 size_t kl;
920 ee = env_addon[ai];
921 kvs = strchr(ee, '=');
922 assert(kvs != NULL);
923 kl = PTR2SIZE(kvs - ee);
924 assert(kl > 0);
925 for (ei = ei_orig; ei-- > 0;) {
926 char const *ekvs = strchr(env[ei], '=');
927 if (ekvs != NULL && kl == PTR2SIZE(ekvs - env[ei]) &&
928 !memcmp(ee, env[ei], kl)) {
929 env[ei] = UNCONST(ee);
930 env_addon[ai] = NULL;
931 break;
936 /* And append the rest */
937 for (ei = ei_orig, ai = ai_orig; ai-- > 0;)
938 if (env_addon[ai] != NULL)
939 env[ei++] = UNCONST(env_addon[ai]);
941 env[ei] = NULL;
942 environ = env;
945 i = getrawlist(cmd, strlen(cmd), argv, NELEM(argv), 0);
947 if ((argv[i++] = UNCONST(a0)) != NULL &&
948 (argv[i++] = UNCONST(a1)) != NULL &&
949 (argv[i++] = UNCONST(a2)) != NULL)
950 argv[i] = NULL;
951 prepare_child(mask, infd, outfd);
952 execvp(argv[0], argv);
953 perror(argv[0]);
954 _exit(EXIT_ERR);
956 NYD_LEAVE;
957 return rv;
960 FL void
961 prepare_child(sigset_t *nset, int infd, int outfd)
963 int i;
964 sigset_t fset;
965 NYD_ENTER;
967 /* All file descriptors other than 0, 1, and 2 are supposed to be cloexec */
968 if (infd >= 0)
969 dup2(infd, STDIN_FILENO);
970 if (outfd >= 0)
971 dup2(outfd, STDOUT_FILENO);
973 if (nset) {
974 for (i = 1; i < NSIG; ++i)
975 if (sigismember(nset, i))
976 safe_signal(i, SIG_IGN);
977 if (!sigismember(nset, SIGINT))
978 safe_signal(SIGINT, SIG_DFL);
981 sigemptyset(&fset);
982 sigprocmask(SIG_SETMASK, &fset, NULL);
983 NYD_LEAVE;
986 FL void
987 free_child(int pid)
989 sigset_t nset, oset;
990 struct child *cp;
991 NYD_ENTER;
993 sigemptyset(&nset);
994 sigaddset(&nset, SIGCHLD);
995 sigprocmask(SIG_BLOCK, &nset, &oset);
997 if ((cp = _findchild(pid, FAL0)) != NULL) {
998 if (cp->done)
999 _delchild(cp);
1000 else
1001 cp->free = 1;
1004 sigprocmask(SIG_SETMASK, &oset, NULL);
1005 NYD_LEAVE;
1008 FL bool_t
1009 wait_child(int pid, int *wait_status)
1011 sigset_t nset, oset;
1012 struct child *cp;
1013 int ws;
1014 bool_t rv;
1015 NYD_ENTER;
1017 sigemptyset(&nset);
1018 sigaddset(&nset, SIGCHLD);
1019 sigprocmask(SIG_BLOCK, &nset, &oset);
1021 cp = _findchild(pid, FAL0);
1022 if (cp != NULL) {
1023 while (!cp->done)
1024 sigsuspend(&oset);
1025 ws = cp->status;
1026 _delchild(cp);
1027 } else
1028 ws = 0;
1030 sigprocmask(SIG_SETMASK, &oset, NULL);
1032 if (wait_status != NULL)
1033 *wait_status = ws;
1034 rv = (WIFEXITED(ws) && WEXITSTATUS(ws) == 0);
1035 NYD_LEAVE;
1036 return rv;
1039 /* s-it-mode */