README: document *next* branch etc.
[s-mailx.git] / popen.c
blob0accca33779e1d3cf98c83f051769dea39bf50ff
1 /*
2 * S-nail - 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 #include "rcv.h"
41 #include "extern.h"
42 #include <sys/stat.h>
43 #include <fcntl.h>
44 #include <unistd.h>
45 #include <sys/wait.h>
46 #include <errno.h>
48 #ifndef NSIG
49 #define NSIG 32
50 #endif
52 #define READ 0
53 #define WRITE 1
55 struct fp {
56 FILE *fp;
57 struct fp *link;
58 char *realfile;
59 long offset;
60 int omode;
61 int pipe;
62 int pid;
63 enum {
64 FP_UNCOMPRESSED = 00,
65 FP_GZIPPED = 01,
66 FP_BZIP2ED = 02,
67 FP_IMAP = 03,
68 FP_MAILDIR = 04,
69 FP_MASK = 0177,
70 FP_READONLY = 0200
71 } compressed;
73 static struct fp *fp_head;
75 struct child {
76 int pid;
77 char done;
78 char free;
79 int status;
80 struct child *link;
82 static struct child *child;
84 static int scan_mode(const char *mode, int *omode);
85 static void register_file(FILE *fp, int omode, int pipe, int pid,
86 int compressed, const char *realfile, long offset);
87 static enum okay compress(struct fp *fpp);
88 static int decompress(int compression, int input, int output);
89 static enum okay unregister_file(FILE *fp);
90 static int file_pid(FILE *fp);
91 static int wait_command(int pid);
92 static struct child *findchild(int pid);
93 static void delchild(struct child *cp);
96 * Provide BSD-like signal() on all systems.
98 sighandler_type
99 safe_signal(int signum, sighandler_type handler)
101 struct sigaction nact, oact;
103 nact.sa_handler = handler;
104 sigemptyset(&nact.sa_mask);
105 nact.sa_flags = 0;
106 #ifdef SA_RESTART
107 nact.sa_flags |= SA_RESTART;
108 #endif
109 if (sigaction(signum, &nact, &oact) != 0)
110 return SIG_ERR;
111 return oact.sa_handler;
114 static int
115 scan_mode(const char *mode, int *omode)
118 if (!strcmp(mode, "r")) {
119 *omode = O_RDONLY;
120 } else if (!strcmp(mode, "w")) {
121 *omode = O_WRONLY | O_CREAT | O_TRUNC;
122 } else if (!strcmp(mode, "wx")) {
123 *omode = O_WRONLY | O_CREAT | O_EXCL;
124 } else if (!strcmp(mode, "a")) {
125 *omode = O_WRONLY | O_APPEND | O_CREAT;
126 } else if (!strcmp(mode, "a+")) {
127 *omode = O_RDWR | O_APPEND;
128 } else if (!strcmp(mode, "r+")) {
129 *omode = O_RDWR;
130 } else if (!strcmp(mode, "w+")) {
131 *omode = O_RDWR | O_CREAT | O_EXCL;
132 } else {
133 fprintf(stderr, catgets(catd, CATSET, 152,
134 "Internal error: bad stdio open mode %s\n"), mode);
135 errno = EINVAL;
136 return -1;
138 return 0;
141 FILE *
142 safe_fopen(const char *file, const char *mode, int *omode)
144 int fd;
146 if (scan_mode(mode, omode) < 0)
147 return NULL;
148 if ((fd = open(file, *omode, 0666)) < 0)
149 return NULL;
150 return fdopen(fd, mode);
153 FILE *
154 Fopen(const char *file, const char *mode)
156 FILE *fp;
157 int omode;
159 if ((fp = safe_fopen(file, mode, &omode)) != NULL) {
160 register_file(fp, omode, 0, 0, FP_UNCOMPRESSED, NULL, 0L);
161 fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
163 return fp;
166 FILE *
167 Fdopen(int fd, const char *mode)
169 FILE *fp;
170 int omode;
172 scan_mode(mode, &omode);
173 if ((fp = fdopen(fd, mode)) != NULL) {
174 register_file(fp, omode, 0, 0, FP_UNCOMPRESSED, NULL, 0L);
175 fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
177 return fp;
181 Fclose(FILE *fp)
183 int i = 0;
184 if (unregister_file(fp) == OKAY)
185 i |= 1;
186 if (fclose(fp) == 0)
187 i |= 2;
188 return i == 3 ? 0 : EOF;
191 FILE *
192 Zopen(const char *file, const char *mode, int *compression)
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;
225 if (access(file, F_OK) == 0) {
226 *compression = FP_UNCOMPRESSED;
227 return Fopen(file, mode);
228 } else if (access(rp=savecat(file, ".gz"), bits) == 0) {
229 gzip: *compression = FP_GZIPPED;
230 } else if (access(rp=savecat(file, ".bz2"), bits) == 0) {
231 bz2: *compression = FP_BZIP2ED;
232 } else {
233 *compression = FP_UNCOMPRESSED;
234 return Fopen(file, mode);
236 if (access(rp, W_OK) < 0)
237 *compression |= FP_READONLY;
238 if ((input = open(rp, bits & W_OK ? O_RDWR : O_RDONLY)) < 0
239 && ((omode&O_CREAT) == 0 || errno != ENOENT))
240 return NULL;
241 open: if ((output = Ftemp(&tempfn, "Rz", "w+", 0600, 0)) == NULL) {
242 perror(catgets(catd, CATSET, 167, "tmpfile"));
243 close(input);
244 return NULL;
246 unlink(tempfn);
247 if (input >= 0 || (*compression&FP_MASK) == FP_IMAP ||
248 (*compression&FP_MASK) == FP_MAILDIR) {
249 if (decompress(*compression, input, fileno(output)) < 0) {
250 close(input);
251 Fclose(output);
252 return NULL;
254 } else {
255 if ((input = creat(rp, 0666)) < 0) {
256 Fclose(output);
257 return NULL;
260 close(input);
261 fflush(output);
262 if (omode & O_APPEND) {
263 int flags;
265 if ((flags = fcntl(fileno(output), F_GETFL)) != -1)
266 fcntl(fileno(output), F_SETFL, flags | O_APPEND);
267 offset = ftell(output);
268 } else {
269 rewind(output);
270 offset = 0;
272 register_file(output, omode, 0, 0, *compression, rp, offset);
273 return output;
276 FILE *
277 Popen(const char *cmd, const char *mode, const char *shell, int newfd1)
279 int p[2];
280 int myside, hisside, fd0, fd1;
281 int pid;
282 char mod[2] = { '0', '\0' };
283 sigset_t nset;
284 FILE *fp;
286 if (pipe(p) < 0)
287 return NULL;
288 fcntl(p[READ], F_SETFD, FD_CLOEXEC);
289 fcntl(p[WRITE], F_SETFD, FD_CLOEXEC);
290 if (*mode == 'r') {
291 myside = p[READ];
292 fd0 = -1;
293 hisside = fd1 = p[WRITE];
294 mod[0] = *mode;
295 } else if (*mode == 'W') {
296 myside = p[WRITE];
297 hisside = fd0 = p[READ];
298 fd1 = newfd1;
299 mod[0] = 'w';
300 } else {
301 myside = p[WRITE];
302 hisside = fd0 = p[READ];
303 fd1 = -1;
304 mod[0] = 'w';
306 sigemptyset(&nset);
307 if (shell == NULL) {
308 pid = start_command(cmd, &nset, fd0, fd1, NULL, NULL, NULL);
309 } else {
310 pid = start_command(shell, &nset, fd0, fd1, "-c", cmd, NULL);
312 if (pid < 0) {
313 close(p[READ]);
314 close(p[WRITE]);
315 return NULL;
317 close(hisside);
318 if ((fp = fdopen(myside, mod)) != NULL)
319 register_file(fp, 0, 1, pid, FP_UNCOMPRESSED, NULL, 0L);
320 return fp;
324 Pclose(FILE *ptr)
326 int i;
327 sigset_t nset, oset;
329 i = file_pid(ptr);
330 if (i < 0)
331 return 0;
332 unregister_file(ptr);
333 fclose(ptr);
334 sigemptyset(&nset);
335 sigaddset(&nset, SIGINT);
336 sigaddset(&nset, SIGHUP);
337 sigprocmask(SIG_BLOCK, &nset, &oset);
338 i = wait_child(i);
339 sigprocmask(SIG_SETMASK, &oset, (sigset_t *)NULL);
340 return i;
343 void
344 close_all_files(void)
347 while (fp_head)
348 if (fp_head->pipe)
349 Pclose(fp_head->fp);
350 else
351 Fclose(fp_head->fp);
354 static void
355 register_file(FILE *fp, int omode, int pipe, int pid, int compressed,
356 const char *realfile, long offset)
358 struct fp *fpp;
360 fpp = (struct fp*)smalloc(sizeof *fpp);
361 fpp->fp = fp;
362 fpp->omode = omode;
363 fpp->pipe = pipe;
364 fpp->pid = pid;
365 fpp->link = fp_head;
366 fpp->compressed = compressed;
367 fpp->realfile = realfile ? sstrdup(realfile) : NULL;
368 fpp->offset = offset;
369 fp_head = fpp;
372 static enum okay
373 compress(struct fp *fpp)
375 int output;
376 char *command[2];
377 enum okay ok;
379 if (fpp->omode == O_RDONLY)
380 return OKAY;
381 fflush(fpp->fp);
382 clearerr(fpp->fp);
383 fseek(fpp->fp, fpp->offset, SEEK_SET);
384 if ((fpp->compressed&FP_MASK) == FP_IMAP) {
385 return imap_append(fpp->realfile, fpp->fp);
387 if ((fpp->compressed&FP_MASK) == FP_MAILDIR) {
388 return maildir_append(fpp->realfile, fpp->fp);
390 if ((output = open(fpp->realfile,
391 (fpp->omode|O_CREAT)&~O_EXCL,
392 0666)) < 0) {
393 fprintf(stderr, "Fatal: cannot create ");
394 perror(fpp->realfile);
395 return STOP;
397 if ((fpp->omode & O_APPEND) == 0)
398 ftruncate(output, 0);
399 switch (fpp->compressed & FP_MASK) {
400 case FP_GZIPPED:
401 command[0] = "gzip"; command[1] = "-c"; break;
402 case FP_BZIP2ED:
403 command[0] = "bzip2"; command[1] = "-c"; break;
404 default:
405 command[0] = "cat"; command[1] = NULL; break;
407 if (run_command(command[0], 0, fileno(fpp->fp), output,
408 command[1], NULL, NULL) < 0)
409 ok = STOP;
410 else
411 ok = OKAY;
412 close(output);
413 return ok;
416 static int
417 decompress(int compression, int input, int output)
419 char *command[2];
422 * Note that it is not possible to handle 'pack' or 'compress'
423 * formats because appending data does not work with them.
425 switch (compression & FP_MASK) {
426 case FP_GZIPPED: command[0] = "gzip"; command[1] = "-cd"; break;
427 case FP_BZIP2ED: command[0] = "bzip2"; command[1] = "-cd"; break;
428 case FP_IMAP: return 0;
429 case FP_MAILDIR: return 0;
430 default: command[0] = "cat"; command[1] = NULL;
432 return run_command(command[0], 0, input, output,
433 command[1], NULL, NULL);
436 static enum okay
437 unregister_file(FILE *fp)
439 struct fp **pp, *p;
440 enum okay ok = OKAY;
442 for (pp = &fp_head; (p = *pp) != (struct fp *)NULL; pp = &p->link)
443 if (p->fp == fp) {
444 if ((p->compressed&FP_MASK) != FP_UNCOMPRESSED)
445 ok = compress(p);
446 *pp = p->link;
447 free(p);
448 return ok;
450 panic(catgets(catd, CATSET, 153, "Invalid file pointer"));
451 /*NOTREACHED*/
452 return STOP;
455 static int
456 file_pid(FILE *fp)
458 struct fp *p;
460 for (p = fp_head; p; p = p->link)
461 if (p->fp == fp)
462 return (p->pid);
463 return -1;
467 * Run a command without a shell, with optional arguments and splicing
468 * of stdin and stdout. The command name can be a sequence of words.
469 * Signals must be handled by the caller.
470 * "Mask" contains the signals to ignore in the new process.
471 * SIGINT is enabled unless it's in the mask.
473 /*VARARGS4*/
475 run_command(char *cmd, sigset_t *mask, int infd, int outfd,
476 char *a0, char *a1, char *a2)
478 int pid;
480 if ((pid = start_command(cmd, mask, infd, outfd, a0, a1, a2)) < 0)
481 return -1;
482 return wait_command(pid);
485 /*VARARGS4*/
487 start_command(const char *cmd, sigset_t *mask, int infd, int outfd,
488 const char *a0, const char *a1, const char *a2)
490 int pid;
492 if ((pid = fork()) < 0) {
493 perror("fork");
494 return -1;
496 if (pid == 0) {
497 char *argv[100];
498 int i = getrawlist(cmd, strlen(cmd),
499 argv, sizeof argv / sizeof *argv, 0);
501 if ((argv[i++] = (char *)a0) != NULL &&
502 (argv[i++] = (char *)a1) != NULL &&
503 (argv[i++] = (char *)a2) != NULL)
504 argv[i] = NULL;
505 prepare_child(mask, infd, outfd);
506 execvp(argv[0], argv);
507 perror(argv[0]);
508 _exit(1);
510 return pid;
513 void
514 prepare_child(sigset_t *nset, int infd, int outfd)
516 int i;
517 sigset_t fset;
520 * All file descriptors other than 0, 1, and 2 are supposed to be
521 * close-on-exec.
523 if (infd >= 0)
524 dup2(infd, 0);
525 if (outfd >= 0)
526 dup2(outfd, 1);
527 if (nset) {
528 for (i = 1; i < NSIG; i++)
529 if (sigismember(nset, i))
530 safe_signal(i, SIG_IGN);
531 if (!sigismember(nset, SIGINT))
532 safe_signal(SIGINT, SIG_DFL);
534 sigfillset(&fset);
535 sigprocmask(SIG_UNBLOCK, &fset, (sigset_t *)NULL);
538 static int
539 wait_command(int pid)
542 if (wait_child(pid) < 0 && (value("bsdcompat") || value("bsdmsgs"))) {
543 printf(catgets(catd, CATSET, 154, "Fatal error in process.\n"));
544 return -1;
546 return 0;
549 static struct child *
550 findchild(int pid)
552 struct child **cpp;
554 for (cpp = &child; *cpp != (struct child *)NULL && (*cpp)->pid != pid;
555 cpp = &(*cpp)->link)
557 if (*cpp == (struct child *)NULL) {
558 *cpp = (struct child *) smalloc(sizeof (struct child));
559 (*cpp)->pid = pid;
560 (*cpp)->done = (*cpp)->free = 0;
561 (*cpp)->link = (struct child *)NULL;
563 return *cpp;
566 static void
567 delchild(struct child *cp)
569 struct child **cpp;
571 for (cpp = &child; *cpp != cp; cpp = &(*cpp)->link)
573 *cpp = cp->link;
574 free(cp);
577 /*ARGSUSED*/
578 void
579 sigchild(int signo)
581 int pid;
582 int status;
583 struct child *cp;
584 (void)signo;
586 again:
587 while ((pid = waitpid(-1, (int*)&status, WNOHANG)) > 0) {
588 cp = findchild(pid);
589 if (cp->free)
590 delchild(cp);
591 else {
592 cp->done = 1;
593 cp->status = status;
596 if (pid == -1 && errno == EINTR)
597 goto again;
600 int wait_status;
603 * Mark a child as don't care.
605 void
606 free_child(int pid)
608 sigset_t nset, oset;
609 struct child *cp = findchild(pid);
610 sigemptyset(&nset);
611 sigaddset(&nset, SIGCHLD);
612 sigprocmask(SIG_BLOCK, &nset, &oset);
614 if (cp->done)
615 delchild(cp);
616 else
617 cp->free = 1;
618 sigprocmask(SIG_SETMASK, &oset, (sigset_t *)NULL);
622 * Wait for a specific child to die.
624 #if 0
626 * This version is correct code, but causes harm on some loosing
627 * systems. So we use the second one instead.
629 int
630 wait_child(int pid)
632 sigset_t nset, oset;
633 struct child *cp = findchild(pid);
634 sigemptyset(&nset);
635 sigaddset(&nset, SIGCHLD);
636 sigprocmask(SIG_BLOCK, &nset, &oset);
638 while (!cp->done)
639 sigsuspend(&oset);
640 wait_status = cp->status;
641 delchild(cp);
642 sigprocmask(SIG_SETMASK, &oset, (sigset_t *)NULL);
644 if (WIFEXITED(wait_status) && (WEXITSTATUS(wait_status) == 0))
645 return 0;
646 return -1;
648 #endif
649 int
650 wait_child(int pid)
652 pid_t term;
653 struct child *cp;
654 struct sigaction nact, oact;
656 nact.sa_handler = SIG_DFL;
657 sigemptyset(&nact.sa_mask);
658 nact.sa_flags = SA_NOCLDSTOP;
659 sigaction(SIGCHLD, &nact, &oact);
661 cp = findchild(pid);
662 if (!cp->done) {
663 do {
664 term = wait(&wait_status);
665 if (term == -1 && errno == EINTR)
666 continue;
667 if (term == 0 || term == -1)
668 break;
669 cp = findchild(term);
670 if (cp->free || term == pid) {
671 delchild(cp);
672 } else {
673 cp->done = 1;
674 cp->status = wait_status;
676 } while (term != pid);
677 } else {
678 wait_status = cp->status;
679 delchild(cp);
682 sigaction(SIGCHLD, &oact, NULL);
684 * Make sure no zombies are left.
686 sigchild(SIGCHLD);
688 if (WIFEXITED(wait_status) && (WEXITSTATUS(wait_status) == 0))
689 return 0;
690 return -1;