Bump s-nail v14.5
[s-mailx.git] / popen.c
blob55043328ceaf1200a65be7787163ce6e33549cf8
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 - 2013 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 struct fp {
52 FILE *fp;
53 struct fp *link;
54 char *realfile;
55 long offset;
56 int omode;
57 int pipe;
58 int pid;
59 enum {
60 FP_UNCOMPRESSED = 00,
61 FP_GZIPPED = 01,
62 FP_BZIP2ED = 02,
63 FP_IMAP = 03,
64 FP_MAILDIR = 04,
65 FP_MASK = 0177,
66 FP_READONLY = 0200
67 } compressed;
69 static struct fp *fp_head;
71 struct child {
72 int pid;
73 char done;
74 char free;
75 int status;
76 struct child *link;
78 static struct child *_popen_child;
80 static int scan_mode(const char *mode, int *omode);
81 static void register_file(FILE *fp, int omode, int ispipe, int pid,
82 int compressed, const char *realfile, long offset);
83 static enum okay compress(struct fp *fpp);
84 static int decompress(int compression, int infd, int outfd);
85 static enum okay unregister_file(FILE *fp);
86 static int file_pid(FILE *fp);
87 static int wait_command(int pid);
88 static struct child *findchild(int pid);
89 static void delchild(struct child *cp);
92 * Provide BSD-like signal() on all systems.
94 FL sighandler_type
95 safe_signal(int signum, sighandler_type handler)
97 struct sigaction nact, oact;
99 nact.sa_handler = handler;
100 sigemptyset(&nact.sa_mask);
101 nact.sa_flags = 0;
102 #ifdef SA_RESTART
103 nact.sa_flags |= SA_RESTART;
104 #endif
105 if (sigaction(signum, &nact, &oact) != 0)
106 return SIG_ERR;
107 return oact.sa_handler;
110 static int
111 scan_mode(const char *mode, int *omode)
114 if (!strcmp(mode, "r")) {
115 *omode = O_RDONLY;
116 } else if (!strcmp(mode, "w")) {
117 *omode = O_WRONLY | O_CREAT | O_TRUNC;
118 } else if (!strcmp(mode, "wx")) {
119 *omode = O_WRONLY | O_CREAT | O_EXCL;
120 } else if (!strcmp(mode, "a")) {
121 *omode = O_WRONLY | O_APPEND | O_CREAT;
122 } else if (!strcmp(mode, "a+")) {
123 *omode = O_RDWR | O_APPEND;
124 } else if (!strcmp(mode, "r+")) {
125 *omode = O_RDWR;
126 } else if (!strcmp(mode, "w+")) {
127 *omode = O_RDWR | O_CREAT | O_EXCL;
128 } else {
129 fprintf(stderr, tr(152,
130 "Internal error: bad stdio open mode %s\n"), mode);
131 errno = EINVAL;
132 *omode = 0; /* (silence CC) */
133 return -1;
135 return 0;
138 FL FILE *
139 safe_fopen(const char *file, const char *mode, int *omode)
141 int fd;
143 if (scan_mode(mode, omode) < 0)
144 return NULL;
145 if ((fd = open(file, *omode, 0666)) < 0)
146 return NULL;
147 return fdopen(fd, mode);
150 FL FILE *
151 Fopen(const char *file, const char *mode)
153 FILE *fp;
154 int omode;
156 if ((fp = safe_fopen(file, mode, &omode)) != NULL) {
157 (void)fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
158 register_file(fp, omode, 0, 0, FP_UNCOMPRESSED, NULL, 0L);
160 return fp;
163 FL FILE *
164 Fdopen(int fd, const char *mode)
166 FILE *fp;
167 int omode;
169 scan_mode(mode, &omode);
170 if ((fp = fdopen(fd, mode)) != NULL) {
171 (void)fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
172 register_file(fp, omode, 0, 0, FP_UNCOMPRESSED, NULL, 0L);
174 return fp;
177 FL int
178 Fclose(FILE *fp)
180 int i = 0;
181 if (unregister_file(fp) == OKAY)
182 i |= 1;
183 if (fclose(fp) == 0)
184 i |= 2;
185 return i == 3 ? 0 : EOF;
188 FL FILE *
189 Zopen(const char *file, const char *mode, int *compression) /* TODO MESS!
190 * TODO maybe we shouldn't be simple and run commands but instead
191 * TODO links against any of available zlib.h, bzlib.h, lzma.h!
192 * TODO even libzip? Much faster, not that much more work, and we're
193 * TODO *so* fixed anyway!!
194 * TODO *or*: make it all hookable via BOXEXTENSION/DECOMPRESS etc.
195 * TODO by the user; but not like this, Coverity went grazy about it */
197 int infd;
198 FILE *outf;
199 const char *rp;
200 int omode;
201 char *tempfn;
202 int bits;
203 int _compression;
204 long offset;
205 char *extension;
206 enum protocol p;
208 if (scan_mode(mode, &omode) < 0)
209 return NULL;
210 if (compression == NULL)
211 compression = &_compression;
212 bits = R_OK | (omode == O_RDONLY ? 0 : W_OK);
213 if (omode & O_APPEND && ((p = which_protocol(file)) == PROTO_IMAP ||
214 p == PROTO_MAILDIR)) {
215 *compression = p == PROTO_IMAP ? FP_IMAP : FP_MAILDIR;
216 omode = O_RDWR | O_APPEND | O_CREAT;
217 rp = file;
218 infd = -1;
219 goto open;
221 if ((extension = strrchr(file, '.')) != NULL) {
222 rp = file;
223 if (strcmp(extension, ".gz") == 0)
224 goto gzip;
225 if (strcmp(extension, ".bz2") == 0)
226 goto bz2;
229 if (access(file, F_OK) == 0) {
230 *compression = FP_UNCOMPRESSED;
231 return Fopen(file, mode);
232 } else if (access(rp=savecat(file, ".gz"), bits) == 0) {
233 gzip: *compression = FP_GZIPPED;
234 } else if (access(rp=savecat(file, ".bz2"), bits) == 0) {
235 bz2: *compression = FP_BZIP2ED;
236 } else {
237 *compression = FP_UNCOMPRESSED;
238 return Fopen(file, mode);
240 if (access(rp, W_OK) < 0)
241 *compression |= FP_READONLY;
243 if ((infd = open(rp, bits & W_OK ? O_RDWR : O_RDONLY)) < 0
244 && ((omode&O_CREAT) == 0 || errno != ENOENT))
245 return NULL;
246 open: if ((outf = Ftemp(&tempfn, "Rz", "w+", 0600, 0)) == NULL) {
247 perror(tr(167, "tmpfile"));
248 if (infd >= 0)
249 close(infd);
250 return NULL;
252 unlink(tempfn);
253 Ftfree(&tempfn);
255 if (infd >= 0 || (*compression&FP_MASK) == FP_IMAP ||
256 (*compression&FP_MASK) == FP_MAILDIR) {
257 if (decompress(*compression, infd, fileno(outf)) < 0) {
258 if (infd >= 0)
259 close(infd);
260 Fclose(outf);
261 return NULL;
263 } else {
264 if ((infd = creat(rp, 0666)) < 0) {
265 Fclose(outf);
266 return NULL;
269 if (infd >= 0)
270 close(infd);
271 fflush(outf);
273 if (omode & O_APPEND) {
274 int flags;
276 if ((flags = fcntl(fileno(outf), F_GETFL)) < 0 ||
277 fcntl(fileno(outf), F_SETFL, flags|O_APPEND)
278 < 0 || (offset = ftell(outf)) < 0) {
279 Fclose(outf);
280 return NULL;
282 } else {
283 rewind(outf);
284 offset = 0;
287 register_file(outf, omode, 0, 0, *compression, rp, offset);
288 return outf;
291 FL FILE *
292 Ftemp(char **fn, char const *prefix, char const *mode, int bits,
293 int doregfile)
295 FILE *fp = NULL;
296 char *cp;
297 int fd;
299 *fn =
300 cp = smalloc(strlen(tempdir) + 1 + sizeof("mail") + strlen(prefix) +
301 + 7 + 1);
302 cp = sstpcpy(cp, tempdir);
303 *cp++ = '/';
304 cp = sstpcpy(cp, "mail");
305 if (*prefix) {
306 *cp++ = '-';
307 cp = sstpcpy(cp, prefix);
309 sstpcpy(cp, ".XXXXXX");
311 #ifdef HAVE_MKSTEMP
312 if ((fd = mkstemp(*fn)) < 0)
313 goto jtemperr;
314 if (bits != (S_IRUSR|S_IWUSR) && fchmod(fd, bits) < 0)
315 goto jtemperr;
316 #else
317 if (mktemp(*fn) == NULL)
318 goto Ftemperr;
319 if ((fd = open(*fn, O_CREAT|O_EXCL|O_RDWR, bits)) < 0)
320 goto jtemperr;
321 #endif
323 if (doregfile)
324 fp = Fdopen(fd, mode);
325 else {
326 (void)fcntl(fd, F_SETFD, FD_CLOEXEC);
327 fp = fdopen(fd, mode);
329 jleave:
330 return fp;
331 jtemperr:
332 Ftfree(fn);
333 goto jleave;
336 FL void
337 Ftfree(char **fn)
339 char *cp = *fn;
341 *fn = NULL;
342 free(cp);
345 FL bool_t
346 pipe_cloexec(int fd[2])
348 bool_t rv = FAL0;
350 if (pipe(fd) < 0)
351 goto jleave;
352 fcntl(fd[0], F_SETFD, FD_CLOEXEC);
353 fcntl(fd[1], F_SETFD, FD_CLOEXEC);
354 rv = TRU1;
355 jleave:
356 return rv;
359 FL FILE *
360 Popen(const char *cmd, const char *mode, const char *sh, int newfd1)
362 int p[2];
363 int myside, hisside, fd0, fd1;
364 int pid;
365 char mod[2] = { '0', '\0' };
366 sigset_t nset;
367 FILE *fp;
369 if (! pipe_cloexec(p))
370 return NULL;
372 if (*mode == 'r') {
373 myside = p[READ];
374 fd0 = -1;
375 hisside = fd1 = p[WRITE];
376 mod[0] = *mode;
377 } else if (*mode == 'W') {
378 myside = p[WRITE];
379 hisside = fd0 = p[READ];
380 fd1 = newfd1;
381 mod[0] = 'w';
382 } else {
383 myside = p[WRITE];
384 hisside = fd0 = p[READ];
385 fd1 = -1;
386 mod[0] = 'w';
388 sigemptyset(&nset);
389 if (sh == NULL) {
390 pid = start_command(cmd, &nset, fd0, fd1, NULL, NULL, NULL);
391 } else {
392 pid = start_command(sh, &nset, fd0, fd1, "-c", cmd, NULL);
394 if (pid < 0) {
395 close(p[READ]);
396 close(p[WRITE]);
397 return NULL;
399 close(hisside);
400 if ((fp = fdopen(myside, mod)) != NULL)
401 register_file(fp, 0, 1, pid, FP_UNCOMPRESSED, NULL, 0L);
402 return fp;
405 FL int
406 Pclose(FILE *ptr, bool_t dowait)
408 int pid;
409 sigset_t nset, oset;
411 pid = file_pid(ptr);
412 if (pid < 0)
413 return 0;
414 unregister_file(ptr);
415 fclose(ptr);
416 if (dowait) {
417 sigemptyset(&nset);
418 sigaddset(&nset, SIGINT);
419 sigaddset(&nset, SIGHUP);
420 sigprocmask(SIG_BLOCK, &nset, &oset);
421 pid = wait_child(pid);
422 sigprocmask(SIG_SETMASK, &oset, (sigset_t*)NULL);
423 } else
424 free_child(pid);
425 return pid;
428 FL void
429 close_all_files(void)
431 while (fp_head)
432 if (fp_head->pipe)
433 Pclose(fp_head->fp, TRU1);
434 else
435 Fclose(fp_head->fp);
438 static void
439 register_file(FILE *fp, int omode, int ispipe, int pid, int compressed,
440 const char *realfile, long offset)
442 struct fp *fpp;
444 fpp = (struct fp*)smalloc(sizeof *fpp);
445 fpp->fp = fp;
446 fpp->omode = omode;
447 fpp->pipe = ispipe;
448 fpp->pid = pid;
449 fpp->link = fp_head;
450 fpp->compressed = compressed;
451 fpp->realfile = realfile ? sstrdup(realfile) : NULL;
452 fpp->offset = offset;
453 fp_head = fpp;
456 static enum okay
457 compress(struct fp *fpp)
459 int outfd;
460 char const *command[2];
461 enum okay ok;
463 if (fpp->omode == O_RDONLY)
464 return OKAY;
465 fflush(fpp->fp);
466 clearerr(fpp->fp);
467 if (fseek(fpp->fp, fpp->offset, SEEK_SET) < 0)
468 return STOP;
469 #ifdef HAVE_IMAP
470 if ((fpp->compressed&FP_MASK) == FP_IMAP) {
471 return imap_append(fpp->realfile, fpp->fp);
473 #endif
474 if ((fpp->compressed&FP_MASK) == FP_MAILDIR) {
475 return maildir_append(fpp->realfile, fpp->fp);
477 if ((outfd = open(fpp->realfile,
478 (fpp->omode|O_CREAT)&~O_EXCL,
479 0666)) < 0) {
480 fprintf(stderr, "Fatal: cannot create ");
481 perror(fpp->realfile);
482 return STOP;
484 if ((fpp->omode & O_APPEND) == 0)
485 ftruncate(outfd, 0);
486 switch (fpp->compressed & FP_MASK) {
487 case FP_GZIPPED:
488 command[0] = "gzip"; command[1] = "-c"; break;
489 case FP_BZIP2ED:
490 command[0] = "bzip2"; command[1] = "-c"; break;
491 default:
492 command[0] = "cat"; command[1] = NULL; break;
494 if (run_command(command[0], 0, fileno(fpp->fp), outfd,
495 command[1], NULL, NULL) < 0)
496 ok = STOP;
497 else
498 ok = OKAY;
499 close(outfd);
500 return ok;
503 static int
504 decompress(int compression, int infd, int outfd)
506 char const *command[2];
509 * Note that it is not possible to handle 'pack' or 'compress'
510 * formats because appending data does not work with them.
512 switch (compression & FP_MASK) {
513 case FP_GZIPPED: command[0] = "gzip"; command[1] = "-cd"; break;
514 case FP_BZIP2ED: command[0] = "bzip2"; command[1] = "-cd"; break;
515 case FP_IMAP: return 0;
516 case FP_MAILDIR: return 0;
517 default: command[0] = "cat"; command[1] = NULL;
519 return run_command(command[0], 0, infd, outfd, command[1], NULL, NULL);
522 static enum okay
523 unregister_file(FILE *fp)
525 struct fp **pp, *p;
526 enum okay ok = OKAY;
528 for (pp = &fp_head; (p = *pp) != (struct fp *)NULL; pp = &p->link)
529 if (p->fp == fp) {
530 if ((p->compressed&FP_MASK) != FP_UNCOMPRESSED)
531 ok = compress(p);
532 *pp = p->link;
533 free(p);
534 return ok;
536 panic(tr(153, "Invalid file pointer"));
537 /*NOTREACHED*/
538 return STOP;
541 static int
542 file_pid(FILE *fp)
544 struct fp *p;
546 for (p = fp_head; p; p = p->link)
547 if (p->fp == fp)
548 return (p->pid);
549 return -1;
553 * Run a command without a shell, with optional arguments and splicing
554 * of stdin and stdout. The command name can be a sequence of words.
555 * Signals must be handled by the caller.
556 * "Mask" contains the signals to ignore in the new process.
557 * SIGINT is enabled unless it's in the mask.
559 /*VARARGS4*/
560 FL int
561 run_command(char const *cmd, sigset_t *mask, int infd, int outfd,
562 char const *a0, char const *a1, char const *a2)
564 int pid;
566 if ((pid = start_command(cmd, mask, infd, outfd, a0, a1, a2)) < 0)
567 return -1;
568 return wait_command(pid);
571 /*VARARGS4*/
572 FL int
573 start_command(const char *cmd, sigset_t *mask, int infd, int outfd,
574 const char *a0, const char *a1, const char *a2)
576 int pid;
578 if ((pid = fork()) < 0) {
579 perror("fork");
580 return -1;
582 if (pid == 0) {
583 char *argv[100];
584 int i = getrawlist(cmd, strlen(cmd),
585 argv, sizeof argv / sizeof *argv, 0);
587 if ((argv[i++] = UNCONST(a0)) != NULL &&
588 (argv[i++] = UNCONST(a1)) != NULL &&
589 (argv[i++] = UNCONST(a2)) != NULL)
590 argv[i] = NULL;
591 prepare_child(mask, infd, outfd);
592 execvp(argv[0], argv);
593 perror(argv[0]);
594 _exit(1);
596 return pid;
599 FL void
600 prepare_child(sigset_t *nset, int infd, int outfd)
602 int i;
603 sigset_t fset;
606 * All file descriptors other than 0, 1, and 2 are supposed to be
607 * close-on-exec.
609 if (infd >= 0)
610 dup2(infd, 0);
611 if (outfd >= 0)
612 dup2(outfd, 1);
613 if (nset) {
614 for (i = 1; i < NSIG; i++)
615 if (sigismember(nset, i))
616 safe_signal(i, SIG_IGN);
617 if (!sigismember(nset, SIGINT))
618 safe_signal(SIGINT, SIG_DFL);
620 sigfillset(&fset);
621 sigprocmask(SIG_UNBLOCK, &fset, (sigset_t *)NULL);
624 static int
625 wait_command(int pid)
628 if (wait_child(pid) < 0 && (value("bsdcompat") || value("bsdmsgs"))) {
629 printf(tr(154, "Fatal error in process.\n"));
630 return -1;
632 return 0;
635 static struct child *
636 findchild(int pid)
638 struct child **cpp;
640 for (cpp = &_popen_child; *cpp != NULL && (*cpp)->pid != pid;
641 cpp = &(*cpp)->link)
643 if (*cpp == NULL) {
644 *cpp = smalloc(sizeof (struct child));
645 (*cpp)->pid = pid;
646 (*cpp)->done = (*cpp)->free = 0;
647 (*cpp)->link = NULL;
649 return *cpp;
652 static void
653 delchild(struct child *cp)
655 struct child **cpp;
657 for (cpp = &_popen_child; *cpp != cp; cpp = &(*cpp)->link)
659 *cpp = cp->link;
660 free(cp);
663 /*ARGSUSED*/
664 FL void
665 sigchild(int signo)
667 int pid;
668 int status;
669 struct child *cp;
670 UNUSED(signo);
672 again:
673 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
674 cp = findchild(pid);
675 if (cp->free)
676 delchild(cp);
677 else {
678 cp->done = 1;
679 cp->status = status;
682 if (pid == -1 && errno == EINTR)
683 goto again;
686 int wait_status;
689 * Mark a child as don't care.
691 FL void
692 free_child(int pid)
694 sigset_t nset, oset;
695 struct child *cp = findchild(pid);
696 sigemptyset(&nset);
697 sigaddset(&nset, SIGCHLD);
698 sigprocmask(SIG_BLOCK, &nset, &oset);
700 if (cp->done)
701 delchild(cp);
702 else
703 cp->free = 1;
704 sigprocmask(SIG_SETMASK, &oset, (sigset_t *)NULL);
708 * Wait for a specific child to die.
710 #if 0
712 * This version is correct code, but causes harm on some loosing
713 * systems. So we use the second one instead.
715 FL int
716 wait_child(int pid)
718 sigset_t nset, oset;
719 struct child *cp = findchild(pid);
720 sigemptyset(&nset);
721 sigaddset(&nset, SIGCHLD);
722 sigprocmask(SIG_BLOCK, &nset, &oset);
724 while (!cp->done)
725 sigsuspend(&oset);
726 wait_status = cp->status;
727 delchild(cp);
728 sigprocmask(SIG_SETMASK, &oset, (sigset_t *)NULL);
730 if (WIFEXITED(wait_status) && (WEXITSTATUS(wait_status) == 0))
731 return 0;
732 return -1;
734 #endif
735 FL int
736 wait_child(int pid)
738 pid_t term;
739 struct child *cp;
740 struct sigaction nact, oact;
742 nact.sa_handler = SIG_DFL;
743 sigemptyset(&nact.sa_mask);
744 nact.sa_flags = SA_NOCLDSTOP;
745 sigaction(SIGCHLD, &nact, &oact);
747 cp = findchild(pid);
748 if (!cp->done) {
749 do {
750 term = wait(&wait_status);
751 if (term == -1 && errno == EINTR)
752 continue;
753 if (term == 0 || term == -1)
754 break;
755 cp = findchild(term);
756 if (cp->free || term == pid) {
757 delchild(cp);
758 } else {
759 cp->done = 1;
760 cp->status = wait_status;
762 } while (term != pid);
763 } else {
764 wait_status = cp->status;
765 delchild(cp);
768 sigaction(SIGCHLD, &oact, NULL);
770 * Make sure no zombies are left.
772 sigchild(SIGCHLD);
774 if (WIFEXITED(wait_status) && (WEXITSTATUS(wait_status) == 0))
775 return 0;
776 return -1;