Enable WANT_QUOTE_FOLD, *_ASSERT -> *_DEBUG, more cleanup
[s-mailx.git] / popen.c
blob5203dcd7ad26917276880c2f4f87c8bafd82440d
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 #include "nail.h"
42 #include <sys/wait.h>
44 #include <fcntl.h>
46 #define READ 0
47 #define WRITE 1
49 struct fp {
50 FILE *fp;
51 struct fp *link;
52 char *realfile;
53 long offset;
54 int omode;
55 int pipe;
56 int pid;
57 enum {
58 FP_UNCOMPRESSED = 00,
59 FP_GZIPPED = 01,
60 FP_BZIP2ED = 02,
61 FP_IMAP = 03,
62 FP_MAILDIR = 04,
63 FP_MASK = 0177,
64 FP_READONLY = 0200
65 } compressed;
67 static struct fp *fp_head;
69 struct child {
70 int pid;
71 char done;
72 char free;
73 int status;
74 struct child *link;
76 static struct child *child;
78 static int scan_mode(const char *mode, int *omode);
79 static void register_file(FILE *fp, int omode, int ispipe, int pid,
80 int compressed, const char *realfile, long offset);
81 static enum okay compress(struct fp *fpp);
82 static int decompress(int compression, int input, int output);
83 static enum okay unregister_file(FILE *fp);
84 static int file_pid(FILE *fp);
85 static int wait_command(int pid);
86 static struct child *findchild(int pid);
87 static void delchild(struct child *cp);
90 * Provide BSD-like signal() on all systems.
92 sighandler_type
93 safe_signal(int signum, sighandler_type handler)
95 struct sigaction nact, oact;
97 nact.sa_handler = handler;
98 sigemptyset(&nact.sa_mask);
99 nact.sa_flags = 0;
100 #ifdef SA_RESTART
101 nact.sa_flags |= SA_RESTART;
102 #endif
103 if (sigaction(signum, &nact, &oact) != 0)
104 return SIG_ERR;
105 return oact.sa_handler;
108 static int
109 scan_mode(const char *mode, int *omode)
112 if (!strcmp(mode, "r")) {
113 *omode = O_RDONLY;
114 } else if (!strcmp(mode, "w")) {
115 *omode = O_WRONLY | O_CREAT | O_TRUNC;
116 } else if (!strcmp(mode, "wx")) {
117 *omode = O_WRONLY | O_CREAT | O_EXCL;
118 } else if (!strcmp(mode, "a")) {
119 *omode = O_WRONLY | O_APPEND | O_CREAT;
120 } else if (!strcmp(mode, "a+")) {
121 *omode = O_RDWR | O_APPEND;
122 } else if (!strcmp(mode, "r+")) {
123 *omode = O_RDWR;
124 } else if (!strcmp(mode, "w+")) {
125 *omode = O_RDWR | O_CREAT | O_EXCL;
126 } else {
127 fprintf(stderr, tr(152,
128 "Internal error: bad stdio open mode %s\n"), mode);
129 errno = EINVAL;
130 return -1;
132 return 0;
135 FILE *
136 safe_fopen(const char *file, const char *mode, int *omode)
138 int fd;
140 if (scan_mode(mode, omode) < 0)
141 return NULL;
142 if ((fd = open(file, *omode, 0666)) < 0)
143 return NULL;
144 return fdopen(fd, mode);
147 FILE *
148 Fopen(const char *file, const char *mode)
150 FILE *fp;
151 int omode;
153 if ((fp = safe_fopen(file, mode, &omode)) != NULL) {
154 (void)fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
155 register_file(fp, omode, 0, 0, FP_UNCOMPRESSED, NULL, 0L);
157 return fp;
160 FILE *
161 Fdopen(int fd, const char *mode)
163 FILE *fp;
164 int omode;
166 scan_mode(mode, &omode);
167 if ((fp = fdopen(fd, mode)) != NULL) {
168 (void)fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
169 register_file(fp, omode, 0, 0, FP_UNCOMPRESSED, NULL, 0L);
171 return fp;
175 Fclose(FILE *fp)
177 int i = 0;
178 if (unregister_file(fp) == OKAY)
179 i |= 1;
180 if (fclose(fp) == 0)
181 i |= 2;
182 return i == 3 ? 0 : EOF;
185 FILE *
186 Zopen(const char *file, const char *mode, int *compression) /* TODO MESS!
187 * TODO maybe we shouldn't be simple and run commands but instead
188 * TODO links against any of available zlib.h, bzlib.h, lzma.h!
189 * TODO even libzip? Much faster, not that much more work, and we're
190 * TODO *so* fixed anyway!!
191 * TODO *or*: make it all hookable via BOXEXTENSION/DECOMPRESS etc.
192 * TODO by the user; but not like this, Coverity went grazy about it */
194 int input;
195 FILE *output;
196 const char *rp;
197 int omode;
198 char *tempfn;
199 int bits;
200 int _compression;
201 long offset;
202 char *extension;
203 enum protocol p;
205 if (scan_mode(mode, &omode) < 0)
206 return NULL;
207 if (compression == NULL)
208 compression = &_compression;
209 bits = R_OK | (omode == O_RDONLY ? 0 : W_OK);
210 if (omode & O_APPEND && ((p = which_protocol(file)) == PROTO_IMAP ||
211 p == PROTO_MAILDIR)) {
212 *compression = p == PROTO_IMAP ? FP_IMAP : FP_MAILDIR;
213 omode = O_RDWR | O_APPEND | O_CREAT;
214 rp = file;
215 input = -1;
216 goto open;
218 if ((extension = strrchr(file, '.')) != NULL) {
219 rp = file;
220 if (strcmp(extension, ".gz") == 0)
221 goto gzip;
222 if (strcmp(extension, ".bz2") == 0)
223 goto bz2;
226 if (access(file, F_OK) == 0) {
227 *compression = FP_UNCOMPRESSED;
228 return Fopen(file, mode);
229 } else if (access(rp=savecat(file, ".gz"), bits) == 0) {
230 gzip: *compression = FP_GZIPPED;
231 } else if (access(rp=savecat(file, ".bz2"), bits) == 0) {
232 bz2: *compression = FP_BZIP2ED;
233 } else {
234 *compression = FP_UNCOMPRESSED;
235 return Fopen(file, mode);
237 if (access(rp, W_OK) < 0)
238 *compression |= FP_READONLY;
240 if ((input = open(rp, bits & W_OK ? O_RDWR : O_RDONLY)) < 0
241 && ((omode&O_CREAT) == 0 || errno != ENOENT))
242 return NULL;
243 open: if ((output = Ftemp(&tempfn, "Rz", "w+", 0600, 0)) == NULL) {
244 perror(tr(167, "tmpfile"));
245 if (input >= 0)
246 close(input);
247 return NULL;
249 unlink(tempfn);
250 Ftfree(&tempfn);
252 if (input >= 0 || (*compression&FP_MASK) == FP_IMAP ||
253 (*compression&FP_MASK) == FP_MAILDIR) {
254 if (decompress(*compression, input, fileno(output)) < 0) {
255 if (input >= 0)
256 close(input);
257 Fclose(output);
258 return NULL;
260 } else {
261 if ((input = creat(rp, 0666)) < 0) {
262 Fclose(output);
263 return NULL;
266 if (input >= 0)
267 close(input);
268 fflush(output);
270 if (omode & O_APPEND) {
271 int flags;
273 if ((flags = fcntl(fileno(output), F_GETFL)) < 0 ||
274 fcntl(fileno(output), F_SETFL, flags|O_APPEND)
275 < 0 || (offset = ftell(output)) < 0) {
276 Fclose(output);
277 return NULL;
279 } else {
280 rewind(output);
281 offset = 0;
284 register_file(output, omode, 0, 0, *compression, rp, offset);
285 return output;
288 FILE *
289 Ftemp(char **fn, char const *prefix, char const *mode, int bits,
290 int doregfile)
292 FILE *fp = NULL;
293 char *cp;
294 int fd;
296 *fn =
297 cp = smalloc(strlen(tempdir) + 1 + sizeof("mail") + strlen(prefix) +
298 + 7 + 1);
299 cp = sstpcpy(cp, tempdir);
300 *cp++ = '/';
301 cp = sstpcpy(cp, "mail");
302 if (*prefix) {
303 *cp++ = '-';
304 cp = sstpcpy(cp, prefix);
306 sstpcpy(cp, ".XXXXXX");
308 #ifdef HAVE_MKSTEMP
309 if ((fd = mkstemp(*fn)) < 0)
310 goto jtemperr;
311 if (bits != (S_IRUSR|S_IWUSR) && fchmod(fd, bits) < 0)
312 goto jtemperr;
313 #else
314 if (mktemp(*fn) == NULL)
315 goto Ftemperr;
316 if ((fd = open(*fn, O_CREAT|O_EXCL|O_RDWR, bits)) < 0)
317 goto jtemperr;
318 #endif
320 if (doregfile)
321 fp = Fdopen(fd, mode);
322 else {
323 (void)fcntl(fd, F_SETFD, FD_CLOEXEC);
324 fp = fdopen(fd, mode);
326 jleave:
327 return fp;
328 jtemperr:
329 Ftfree(fn);
330 goto jleave;
333 void
334 Ftfree(char **fn)
336 char *cp = *fn;
338 *fn = NULL;
339 free(cp);
342 bool_t
343 pipe_cloexec(int fd[2])
345 bool_t rv = FAL0;
347 if (pipe(fd) < 0)
348 goto jleave;
349 fcntl(fd[0], F_SETFD, FD_CLOEXEC);
350 fcntl(fd[1], F_SETFD, FD_CLOEXEC);
351 rv = TRU1;
352 jleave:
353 return rv;
356 FILE *
357 Popen(const char *cmd, const char *mode, const char *sh, int newfd1)
359 int p[2];
360 int myside, hisside, fd0, fd1;
361 int pid;
362 char mod[2] = { '0', '\0' };
363 sigset_t nset;
364 FILE *fp;
366 if (! pipe_cloexec(p))
367 return NULL;
369 if (*mode == 'r') {
370 myside = p[READ];
371 fd0 = -1;
372 hisside = fd1 = p[WRITE];
373 mod[0] = *mode;
374 } else if (*mode == 'W') {
375 myside = p[WRITE];
376 hisside = fd0 = p[READ];
377 fd1 = newfd1;
378 mod[0] = 'w';
379 } else {
380 myside = p[WRITE];
381 hisside = fd0 = p[READ];
382 fd1 = -1;
383 mod[0] = 'w';
385 sigemptyset(&nset);
386 if (sh == NULL) {
387 pid = start_command(cmd, &nset, fd0, fd1, NULL, NULL, NULL);
388 } else {
389 pid = start_command(sh, &nset, fd0, fd1, "-c", cmd, NULL);
391 if (pid < 0) {
392 close(p[READ]);
393 close(p[WRITE]);
394 return NULL;
396 close(hisside);
397 if ((fp = fdopen(myside, mod)) != NULL)
398 register_file(fp, 0, 1, pid, FP_UNCOMPRESSED, NULL, 0L);
399 return fp;
403 Pclose(FILE *ptr, bool_t dowait)
405 int pid;
406 sigset_t nset, oset;
408 pid = file_pid(ptr);
409 if (pid < 0)
410 return 0;
411 unregister_file(ptr);
412 fclose(ptr);
413 if (dowait) {
414 sigemptyset(&nset);
415 sigaddset(&nset, SIGINT);
416 sigaddset(&nset, SIGHUP);
417 sigprocmask(SIG_BLOCK, &nset, &oset);
418 pid = wait_child(pid);
419 sigprocmask(SIG_SETMASK, &oset, (sigset_t*)NULL);
420 } else
421 free_child(pid);
422 return pid;
425 void
426 close_all_files(void)
428 while (fp_head)
429 if (fp_head->pipe)
430 Pclose(fp_head->fp, TRU1);
431 else
432 Fclose(fp_head->fp);
435 static void
436 register_file(FILE *fp, int omode, int ispipe, int pid, int compressed,
437 const char *realfile, long offset)
439 struct fp *fpp;
441 fpp = (struct fp*)smalloc(sizeof *fpp);
442 fpp->fp = fp;
443 fpp->omode = omode;
444 fpp->pipe = ispipe;
445 fpp->pid = pid;
446 fpp->link = fp_head;
447 fpp->compressed = compressed;
448 fpp->realfile = realfile ? sstrdup(realfile) : NULL;
449 fpp->offset = offset;
450 fp_head = fpp;
453 static enum okay
454 compress(struct fp *fpp)
456 int output;
457 char const *command[2];
458 enum okay ok;
460 if (fpp->omode == O_RDONLY)
461 return OKAY;
462 fflush(fpp->fp);
463 clearerr(fpp->fp);
464 if (fseek(fpp->fp, fpp->offset, SEEK_SET) < 0)
465 return STOP;
466 #ifdef HAVE_IMAP
467 if ((fpp->compressed&FP_MASK) == FP_IMAP) {
468 return imap_append(fpp->realfile, fpp->fp);
470 #endif
471 if ((fpp->compressed&FP_MASK) == FP_MAILDIR) {
472 return maildir_append(fpp->realfile, fpp->fp);
474 if ((output = open(fpp->realfile,
475 (fpp->omode|O_CREAT)&~O_EXCL,
476 0666)) < 0) {
477 fprintf(stderr, "Fatal: cannot create ");
478 perror(fpp->realfile);
479 return STOP;
481 if ((fpp->omode & O_APPEND) == 0)
482 ftruncate(output, 0);
483 switch (fpp->compressed & FP_MASK) {
484 case FP_GZIPPED:
485 command[0] = "gzip"; command[1] = "-c"; break;
486 case FP_BZIP2ED:
487 command[0] = "bzip2"; command[1] = "-c"; break;
488 default:
489 command[0] = "cat"; command[1] = NULL; break;
491 if (run_command(command[0], 0, fileno(fpp->fp), output,
492 command[1], NULL, NULL) < 0)
493 ok = STOP;
494 else
495 ok = OKAY;
496 close(output);
497 return ok;
500 static int
501 decompress(int compression, int input, int output)
503 char const *command[2];
506 * Note that it is not possible to handle 'pack' or 'compress'
507 * formats because appending data does not work with them.
509 switch (compression & FP_MASK) {
510 case FP_GZIPPED: command[0] = "gzip"; command[1] = "-cd"; break;
511 case FP_BZIP2ED: command[0] = "bzip2"; command[1] = "-cd"; break;
512 case FP_IMAP: return 0;
513 case FP_MAILDIR: return 0;
514 default: command[0] = "cat"; command[1] = NULL;
516 return run_command(command[0], 0, input, output,
517 command[1], NULL, NULL);
520 static enum okay
521 unregister_file(FILE *fp)
523 struct fp **pp, *p;
524 enum okay ok = OKAY;
526 for (pp = &fp_head; (p = *pp) != (struct fp *)NULL; pp = &p->link)
527 if (p->fp == fp) {
528 if ((p->compressed&FP_MASK) != FP_UNCOMPRESSED)
529 ok = compress(p);
530 *pp = p->link;
531 free(p);
532 return ok;
534 panic(tr(153, "Invalid file pointer"));
535 /*NOTREACHED*/
536 return STOP;
539 static int
540 file_pid(FILE *fp)
542 struct fp *p;
544 for (p = fp_head; p; p = p->link)
545 if (p->fp == fp)
546 return (p->pid);
547 return -1;
551 * Run a command without a shell, with optional arguments and splicing
552 * of stdin and stdout. The command name can be a sequence of words.
553 * Signals must be handled by the caller.
554 * "Mask" contains the signals to ignore in the new process.
555 * SIGINT is enabled unless it's in the mask.
557 /*VARARGS4*/
559 run_command(char const *cmd, sigset_t *mask, int infd, int outfd,
560 char const *a0, char const *a1, char const *a2)
562 int pid;
564 if ((pid = start_command(cmd, mask, infd, outfd, a0, a1, a2)) < 0)
565 return -1;
566 return wait_command(pid);
569 /*VARARGS4*/
571 start_command(const char *cmd, sigset_t *mask, int infd, int outfd,
572 const char *a0, const char *a1, const char *a2)
574 int pid;
576 if ((pid = fork()) < 0) {
577 perror("fork");
578 return -1;
580 if (pid == 0) {
581 char *argv[100];
582 int i = getrawlist(cmd, strlen(cmd),
583 argv, sizeof argv / sizeof *argv, 0);
585 if ((argv[i++] = UNCONST(a0)) != NULL &&
586 (argv[i++] = UNCONST(a1)) != NULL &&
587 (argv[i++] = UNCONST(a2)) != NULL)
588 argv[i] = NULL;
589 prepare_child(mask, infd, outfd);
590 execvp(argv[0], argv);
591 perror(argv[0]);
592 _exit(1);
594 return pid;
597 void
598 prepare_child(sigset_t *nset, int infd, int outfd)
600 int i;
601 sigset_t fset;
604 * All file descriptors other than 0, 1, and 2 are supposed to be
605 * close-on-exec.
607 if (infd >= 0)
608 dup2(infd, 0);
609 if (outfd >= 0)
610 dup2(outfd, 1);
611 if (nset) {
612 for (i = 1; i < NSIG; i++)
613 if (sigismember(nset, i))
614 safe_signal(i, SIG_IGN);
615 if (!sigismember(nset, SIGINT))
616 safe_signal(SIGINT, SIG_DFL);
618 sigfillset(&fset);
619 sigprocmask(SIG_UNBLOCK, &fset, (sigset_t *)NULL);
622 static int
623 wait_command(int pid)
626 if (wait_child(pid) < 0 && (value("bsdcompat") || value("bsdmsgs"))) {
627 printf(tr(154, "Fatal error in process.\n"));
628 return -1;
630 return 0;
633 static struct child *
634 findchild(int pid)
636 struct child **cpp;
638 for (cpp = &child; *cpp != (struct child *)NULL && (*cpp)->pid != pid;
639 cpp = &(*cpp)->link)
641 if (*cpp == (struct child *)NULL) {
642 *cpp = (struct child *) smalloc(sizeof (struct child));
643 (*cpp)->pid = pid;
644 (*cpp)->done = (*cpp)->free = 0;
645 (*cpp)->link = (struct child *)NULL;
647 return *cpp;
650 static void
651 delchild(struct child *cp)
653 struct child **cpp;
655 for (cpp = &child; *cpp != cp; cpp = &(*cpp)->link)
657 *cpp = cp->link;
658 free(cp);
661 /*ARGSUSED*/
662 void
663 sigchild(int signo)
665 int pid;
666 int status;
667 struct child *cp;
668 (void)signo;
670 again:
671 while ((pid = waitpid(-1, (int*)&status, WNOHANG)) > 0) {
672 cp = findchild(pid);
673 if (cp->free)
674 delchild(cp);
675 else {
676 cp->done = 1;
677 cp->status = status;
680 if (pid == -1 && errno == EINTR)
681 goto again;
684 int wait_status;
687 * Mark a child as don't care.
689 void
690 free_child(int pid)
692 sigset_t nset, oset;
693 struct child *cp = findchild(pid);
694 sigemptyset(&nset);
695 sigaddset(&nset, SIGCHLD);
696 sigprocmask(SIG_BLOCK, &nset, &oset);
698 if (cp->done)
699 delchild(cp);
700 else
701 cp->free = 1;
702 sigprocmask(SIG_SETMASK, &oset, (sigset_t *)NULL);
706 * Wait for a specific child to die.
708 #if 0
710 * This version is correct code, but causes harm on some loosing
711 * systems. So we use the second one instead.
713 int
714 wait_child(int pid)
716 sigset_t nset, oset;
717 struct child *cp = findchild(pid);
718 sigemptyset(&nset);
719 sigaddset(&nset, SIGCHLD);
720 sigprocmask(SIG_BLOCK, &nset, &oset);
722 while (!cp->done)
723 sigsuspend(&oset);
724 wait_status = cp->status;
725 delchild(cp);
726 sigprocmask(SIG_SETMASK, &oset, (sigset_t *)NULL);
728 if (WIFEXITED(wait_status) && (WEXITSTATUS(wait_status) == 0))
729 return 0;
730 return -1;
732 #endif
733 int
734 wait_child(int pid)
736 pid_t term;
737 struct child *cp;
738 struct sigaction nact, oact;
740 nact.sa_handler = SIG_DFL;
741 sigemptyset(&nact.sa_mask);
742 nact.sa_flags = SA_NOCLDSTOP;
743 sigaction(SIGCHLD, &nact, &oact);
745 cp = findchild(pid);
746 if (!cp->done) {
747 do {
748 term = wait(&wait_status);
749 if (term == -1 && errno == EINTR)
750 continue;
751 if (term == 0 || term == -1)
752 break;
753 cp = findchild(term);
754 if (cp->free || term == pid) {
755 delchild(cp);
756 } else {
757 cp->done = 1;
758 cp->status = wait_status;
760 } while (term != pid);
761 } else {
762 wait_status = cp->status;
763 delchild(cp);
766 sigaction(SIGCHLD, &oact, NULL);
768 * Make sure no zombies are left.
770 sigchild(SIGCHLD);
772 if (WIFEXITED(wait_status) && (WEXITSTATUS(wait_status) == 0))
773 return 0;
774 return -1;