collect(): flags not value(), keep interactivity..
[s-mailx.git] / popen.c
blobed869155746ad4a8243abd9b3e86c15f82182235
1 /*
2 * Heirloom mailx - a mail user agent derived from Berkeley Mail.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 Steffen "Daode" Nurpmeso.
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 lint
41 #ifdef DOSCCS
42 static char sccsid[] = "@(#)popen.c 2.20 (gritter) 3/4/06";
43 #endif
44 #endif /* not lint */
46 #include "rcv.h"
47 #include "extern.h"
48 #include <sys/stat.h>
49 #include <fcntl.h>
50 #include <unistd.h>
51 #include <sys/wait.h>
52 #include <errno.h>
54 #ifndef NSIG
55 #define NSIG 32
56 #endif
58 #define READ 0
59 #define WRITE 1
61 struct fp {
62 FILE *fp;
63 struct fp *link;
64 char *realfile;
65 long offset;
66 int omode;
67 int pipe;
68 int pid;
69 enum {
70 FP_UNCOMPRESSED = 00,
71 FP_GZIPPED = 01,
72 FP_BZIP2ED = 02,
73 FP_IMAP = 03,
74 FP_MAILDIR = 04,
75 FP_MASK = 0177,
76 FP_READONLY = 0200
77 } compressed;
79 static struct fp *fp_head;
81 struct child {
82 int pid;
83 char done;
84 char free;
85 int status;
86 struct child *link;
88 static struct child *child;
90 static int scan_mode(const char *mode, int *omode);
91 static void register_file(FILE *fp, int omode, int pipe, int pid,
92 int compressed, const char *realfile, long offset);
93 static enum okay compress(struct fp *fpp);
94 static int decompress(int compression, int input, int output);
95 static enum okay unregister_file(FILE *fp);
96 static int file_pid(FILE *fp);
97 static int wait_command(int pid);
98 static struct child *findchild(int pid);
99 static void delchild(struct child *cp);
102 * Provide BSD-like signal() on all systems.
104 sighandler_type
105 safe_signal(int signum, sighandler_type handler)
107 struct sigaction nact, oact;
109 nact.sa_handler = handler;
110 sigemptyset(&nact.sa_mask);
111 nact.sa_flags = 0;
112 #ifdef SA_RESTART
113 nact.sa_flags |= SA_RESTART;
114 #endif
115 if (sigaction(signum, &nact, &oact) != 0)
116 return SIG_ERR;
117 return oact.sa_handler;
120 static int
121 scan_mode(const char *mode, int *omode)
124 if (!strcmp(mode, "r")) {
125 *omode = O_RDONLY;
126 } else if (!strcmp(mode, "w")) {
127 *omode = O_WRONLY | O_CREAT | O_TRUNC;
128 } else if (!strcmp(mode, "wx")) {
129 *omode = O_WRONLY | O_CREAT | O_EXCL;
130 } else if (!strcmp(mode, "a")) {
131 *omode = O_WRONLY | O_APPEND | O_CREAT;
132 } else if (!strcmp(mode, "a+")) {
133 *omode = O_RDWR | O_APPEND;
134 } else if (!strcmp(mode, "r+")) {
135 *omode = O_RDWR;
136 } else if (!strcmp(mode, "w+")) {
137 *omode = O_RDWR | O_CREAT | O_EXCL;
138 } else {
139 fprintf(stderr, catgets(catd, CATSET, 152,
140 "Internal error: bad stdio open mode %s\n"), mode);
141 errno = EINVAL;
142 return -1;
144 return 0;
147 FILE *
148 safe_fopen(const char *file, const char *mode, int *omode)
150 int fd;
152 if (scan_mode(mode, omode) < 0)
153 return NULL;
154 if ((fd = open(file, *omode, 0666)) < 0)
155 return NULL;
156 return fdopen(fd, mode);
159 FILE *
160 Fopen(const char *file, const char *mode)
162 FILE *fp;
163 int omode;
165 if ((fp = safe_fopen(file, mode, &omode)) != NULL) {
166 register_file(fp, omode, 0, 0, FP_UNCOMPRESSED, NULL, 0L);
167 fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
169 return fp;
172 FILE *
173 Fdopen(int fd, const char *mode)
175 FILE *fp;
176 int omode;
178 scan_mode(mode, &omode);
179 if ((fp = fdopen(fd, mode)) != NULL) {
180 register_file(fp, omode, 0, 0, FP_UNCOMPRESSED, NULL, 0L);
181 fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
183 return fp;
187 Fclose(FILE *fp)
189 int i = 0;
190 if (unregister_file(fp) == OKAY)
191 i |= 1;
192 if (fclose(fp) == 0)
193 i |= 2;
194 return i == 3 ? 0 : EOF;
197 FILE *
198 Zopen(const char *file, const char *mode, int *compression)
200 int input;
201 FILE *output;
202 const char *rp;
203 int omode;
204 char *tempfn;
205 int bits;
206 int _compression;
207 long offset;
208 char *extension;
209 enum protocol p;
211 if (scan_mode(mode, &omode) < 0)
212 return NULL;
213 if (compression == NULL)
214 compression = &_compression;
215 bits = R_OK | (omode == O_RDONLY ? 0 : W_OK);
216 if (omode & O_APPEND && ((p = which_protocol(file)) == PROTO_IMAP ||
217 p == PROTO_MAILDIR)) {
218 *compression = p == PROTO_IMAP ? FP_IMAP : FP_MAILDIR;
219 omode = O_RDWR | O_APPEND | O_CREAT;
220 rp = file;
221 input = -1;
222 goto open;
224 if ((extension = strrchr(file, '.')) != NULL) {
225 rp = file;
226 if (strcmp(extension, ".gz") == 0)
227 goto gzip;
228 if (strcmp(extension, ".bz2") == 0)
229 goto bz2;
231 if (access(file, F_OK) == 0) {
232 *compression = FP_UNCOMPRESSED;
233 return Fopen(file, mode);
234 } else if (access(rp=savecat(file, ".gz"), bits) == 0) {
235 gzip: *compression = FP_GZIPPED;
236 } else if (access(rp=savecat(file, ".bz2"), bits) == 0) {
237 bz2: *compression = FP_BZIP2ED;
238 } else {
239 *compression = FP_UNCOMPRESSED;
240 return Fopen(file, mode);
242 if (access(rp, W_OK) < 0)
243 *compression |= FP_READONLY;
244 if ((input = open(rp, bits & W_OK ? O_RDWR : O_RDONLY)) < 0
245 && ((omode&O_CREAT) == 0 || errno != ENOENT))
246 return NULL;
247 open: if ((output = Ftemp(&tempfn, "Rz", "w+", 0600, 0)) == NULL) {
248 perror(catgets(catd, CATSET, 167, "tmpfile"));
249 close(input);
250 return NULL;
252 unlink(tempfn);
253 if (input >= 0 || (*compression&FP_MASK) == FP_IMAP ||
254 (*compression&FP_MASK) == FP_MAILDIR) {
255 if (decompress(*compression, input, fileno(output)) < 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 close(input);
267 fflush(output);
268 if (omode & O_APPEND) {
269 int flags;
271 if ((flags = fcntl(fileno(output), F_GETFL)) != -1)
272 fcntl(fileno(output), F_SETFL, flags | O_APPEND);
273 offset = ftell(output);
274 } else {
275 rewind(output);
276 offset = 0;
278 register_file(output, omode, 0, 0, *compression, rp, offset);
279 return output;
282 FILE *
283 Popen(const char *cmd, const char *mode, const char *shell, int newfd1)
285 int p[2];
286 int myside, hisside, fd0, fd1;
287 int pid;
288 char mod[2] = { '0', '\0' };
289 sigset_t nset;
290 FILE *fp;
292 if (pipe(p) < 0)
293 return NULL;
294 fcntl(p[READ], F_SETFD, FD_CLOEXEC);
295 fcntl(p[WRITE], F_SETFD, FD_CLOEXEC);
296 if (*mode == 'r') {
297 myside = p[READ];
298 fd0 = -1;
299 hisside = fd1 = p[WRITE];
300 mod[0] = *mode;
301 } else if (*mode == 'W') {
302 myside = p[WRITE];
303 hisside = fd0 = p[READ];
304 fd1 = newfd1;
305 mod[0] = 'w';
306 } else {
307 myside = p[WRITE];
308 hisside = fd0 = p[READ];
309 fd1 = -1;
310 mod[0] = 'w';
312 sigemptyset(&nset);
313 if (shell == NULL) {
314 pid = start_command(cmd, &nset, fd0, fd1, NULL, NULL, NULL);
315 } else {
316 pid = start_command(shell, &nset, fd0, fd1, "-c", cmd, NULL);
318 if (pid < 0) {
319 close(p[READ]);
320 close(p[WRITE]);
321 return NULL;
323 close(hisside);
324 if ((fp = fdopen(myside, mod)) != NULL)
325 register_file(fp, 0, 1, pid, FP_UNCOMPRESSED, NULL, 0L);
326 return fp;
330 Pclose(FILE *ptr)
332 int i;
333 sigset_t nset, oset;
335 i = file_pid(ptr);
336 if (i < 0)
337 return 0;
338 unregister_file(ptr);
339 fclose(ptr);
340 sigemptyset(&nset);
341 sigaddset(&nset, SIGINT);
342 sigaddset(&nset, SIGHUP);
343 sigprocmask(SIG_BLOCK, &nset, &oset);
344 i = wait_child(i);
345 sigprocmask(SIG_SETMASK, &oset, (sigset_t *)NULL);
346 return i;
349 void
350 close_all_files(void)
353 while (fp_head)
354 if (fp_head->pipe)
355 Pclose(fp_head->fp);
356 else
357 Fclose(fp_head->fp);
360 static void
361 register_file(FILE *fp, int omode, int pipe, int pid, int compressed,
362 const char *realfile, long offset)
364 struct fp *fpp;
366 fpp = (struct fp*)smalloc(sizeof *fpp);
367 fpp->fp = fp;
368 fpp->omode = omode;
369 fpp->pipe = pipe;
370 fpp->pid = pid;
371 fpp->link = fp_head;
372 fpp->compressed = compressed;
373 fpp->realfile = realfile ? sstrdup(realfile) : NULL;
374 fpp->offset = offset;
375 fp_head = fpp;
378 static enum okay
379 compress(struct fp *fpp)
381 int output;
382 char *command[2];
383 enum okay ok;
385 if (fpp->omode == O_RDONLY)
386 return OKAY;
387 fflush(fpp->fp);
388 clearerr(fpp->fp);
389 fseek(fpp->fp, fpp->offset, SEEK_SET);
390 if ((fpp->compressed&FP_MASK) == FP_IMAP) {
391 return imap_append(fpp->realfile, fpp->fp);
393 if ((fpp->compressed&FP_MASK) == FP_MAILDIR) {
394 return maildir_append(fpp->realfile, fpp->fp);
396 if ((output = open(fpp->realfile,
397 (fpp->omode|O_CREAT)&~O_EXCL,
398 0666)) < 0) {
399 fprintf(stderr, "Fatal: cannot create ");
400 perror(fpp->realfile);
401 return STOP;
403 if ((fpp->omode & O_APPEND) == 0)
404 ftruncate(output, 0);
405 switch (fpp->compressed & FP_MASK) {
406 case FP_GZIPPED:
407 command[0] = "gzip"; command[1] = "-c"; break;
408 case FP_BZIP2ED:
409 command[0] = "bzip2"; command[1] = "-c"; break;
410 default:
411 command[0] = "cat"; command[1] = NULL; break;
413 if (run_command(command[0], 0, fileno(fpp->fp), output,
414 command[1], NULL, NULL) < 0)
415 ok = STOP;
416 else
417 ok = OKAY;
418 close(output);
419 return ok;
422 static int
423 decompress(int compression, int input, int output)
425 char *command[2];
428 * Note that it is not possible to handle 'pack' or 'compress'
429 * formats because appending data does not work with them.
431 switch (compression & FP_MASK) {
432 case FP_GZIPPED: command[0] = "gzip"; command[1] = "-cd"; break;
433 case FP_BZIP2ED: command[0] = "bzip2"; command[1] = "-cd"; break;
434 case FP_IMAP: return 0;
435 case FP_MAILDIR: return 0;
436 default: command[0] = "cat"; command[1] = NULL;
438 return run_command(command[0], 0, input, output,
439 command[1], NULL, NULL);
442 static enum okay
443 unregister_file(FILE *fp)
445 struct fp **pp, *p;
446 enum okay ok = OKAY;
448 for (pp = &fp_head; (p = *pp) != (struct fp *)NULL; pp = &p->link)
449 if (p->fp == fp) {
450 if ((p->compressed&FP_MASK) != FP_UNCOMPRESSED)
451 ok = compress(p);
452 *pp = p->link;
453 free(p);
454 return ok;
456 panic(catgets(catd, CATSET, 153, "Invalid file pointer"));
457 /*NOTREACHED*/
458 return STOP;
461 static int
462 file_pid(FILE *fp)
464 struct fp *p;
466 for (p = fp_head; p; p = p->link)
467 if (p->fp == fp)
468 return (p->pid);
469 return -1;
473 * Run a command without a shell, with optional arguments and splicing
474 * of stdin and stdout. The command name can be a sequence of words.
475 * Signals must be handled by the caller.
476 * "Mask" contains the signals to ignore in the new process.
477 * SIGINT is enabled unless it's in the mask.
479 /*VARARGS4*/
481 run_command(char *cmd, sigset_t *mask, int infd, int outfd,
482 char *a0, char *a1, char *a2)
484 int pid;
486 if ((pid = start_command(cmd, mask, infd, outfd, a0, a1, a2)) < 0)
487 return -1;
488 return wait_command(pid);
491 /*VARARGS4*/
493 start_command(const char *cmd, sigset_t *mask, int infd, int outfd,
494 const char *a0, const char *a1, const char *a2)
496 int pid;
498 if ((pid = fork()) < 0) {
499 perror("fork");
500 return -1;
502 if (pid == 0) {
503 char *argv[100];
504 int i = getrawlist(cmd, strlen(cmd),
505 argv, sizeof argv / sizeof *argv, 0);
507 if ((argv[i++] = (char *)a0) != NULL &&
508 (argv[i++] = (char *)a1) != NULL &&
509 (argv[i++] = (char *)a2) != NULL)
510 argv[i] = NULL;
511 prepare_child(mask, infd, outfd);
512 execvp(argv[0], argv);
513 perror(argv[0]);
514 _exit(1);
516 return pid;
519 void
520 prepare_child(sigset_t *nset, int infd, int outfd)
522 int i;
523 sigset_t fset;
526 * All file descriptors other than 0, 1, and 2 are supposed to be
527 * close-on-exec.
529 if (infd >= 0)
530 dup2(infd, 0);
531 if (outfd >= 0)
532 dup2(outfd, 1);
533 if (nset) {
534 for (i = 1; i < NSIG; i++)
535 if (sigismember(nset, i))
536 safe_signal(i, SIG_IGN);
537 if (!sigismember(nset, SIGINT))
538 safe_signal(SIGINT, SIG_DFL);
540 sigfillset(&fset);
541 sigprocmask(SIG_UNBLOCK, &fset, (sigset_t *)NULL);
544 static int
545 wait_command(int pid)
548 if (wait_child(pid) < 0 && (value("bsdcompat") || value("bsdmsgs"))) {
549 printf(catgets(catd, CATSET, 154, "Fatal error in process.\n"));
550 return -1;
552 return 0;
555 static struct child *
556 findchild(int pid)
558 struct child **cpp;
560 for (cpp = &child; *cpp != (struct child *)NULL && (*cpp)->pid != pid;
561 cpp = &(*cpp)->link)
563 if (*cpp == (struct child *)NULL) {
564 *cpp = (struct child *) smalloc(sizeof (struct child));
565 (*cpp)->pid = pid;
566 (*cpp)->done = (*cpp)->free = 0;
567 (*cpp)->link = (struct child *)NULL;
569 return *cpp;
572 static void
573 delchild(struct child *cp)
575 struct child **cpp;
577 for (cpp = &child; *cpp != cp; cpp = &(*cpp)->link)
579 *cpp = cp->link;
580 free(cp);
583 /*ARGSUSED*/
584 void
585 sigchild(int signo)
587 int pid;
588 int status;
589 struct child *cp;
590 (void)signo;
592 again:
593 while ((pid = waitpid(-1, (int*)&status, WNOHANG)) > 0) {
594 cp = findchild(pid);
595 if (cp->free)
596 delchild(cp);
597 else {
598 cp->done = 1;
599 cp->status = status;
602 if (pid == -1 && errno == EINTR)
603 goto again;
606 int wait_status;
609 * Mark a child as don't care.
611 void
612 free_child(int pid)
614 sigset_t nset, oset;
615 struct child *cp = findchild(pid);
616 sigemptyset(&nset);
617 sigaddset(&nset, SIGCHLD);
618 sigprocmask(SIG_BLOCK, &nset, &oset);
620 if (cp->done)
621 delchild(cp);
622 else
623 cp->free = 1;
624 sigprocmask(SIG_SETMASK, &oset, (sigset_t *)NULL);
628 * Wait for a specific child to die.
630 #if 0
632 * This version is correct code, but causes harm on some loosing
633 * systems. So we use the second one instead.
635 int
636 wait_child(int pid)
638 sigset_t nset, oset;
639 struct child *cp = findchild(pid);
640 sigemptyset(&nset);
641 sigaddset(&nset, SIGCHLD);
642 sigprocmask(SIG_BLOCK, &nset, &oset);
644 while (!cp->done)
645 sigsuspend(&oset);
646 wait_status = cp->status;
647 delchild(cp);
648 sigprocmask(SIG_SETMASK, &oset, (sigset_t *)NULL);
650 if (WIFEXITED(wait_status) && (WEXITSTATUS(wait_status) == 0))
651 return 0;
652 return -1;
654 #endif
655 int
656 wait_child(int pid)
658 pid_t term;
659 struct child *cp;
660 struct sigaction nact, oact;
662 nact.sa_handler = SIG_DFL;
663 sigemptyset(&nact.sa_mask);
664 nact.sa_flags = SA_NOCLDSTOP;
665 sigaction(SIGCHLD, &nact, &oact);
667 cp = findchild(pid);
668 if (!cp->done) {
669 do {
670 term = wait(&wait_status);
671 if (term == -1 && errno == EINTR)
672 continue;
673 if (term == 0 || term == -1)
674 break;
675 cp = findchild(term);
676 if (cp->free || term == pid) {
677 delchild(cp);
678 } else {
679 cp->done = 1;
680 cp->status = wait_status;
682 } while (term != pid);
683 } else {
684 wait_status = cp->status;
685 delchild(cp);
688 sigaction(SIGCHLD, &oact, NULL);
690 * Make sure no zombies are left.
692 sigchild(SIGCHLD);
694 if (WIFEXITED(wait_status) && (WEXITSTATUS(wait_status) == 0))
695 return 0;
696 return -1;