collect.c: fix compiler warnings..
[s-mailx.git] / popen.c
blob348fd1e6bd8334fb1db70e553c1b14dc04969506
1 /*
2 * Heirloom mailx - a mail user agent derived from Berkeley Mail.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 */
6 /*
7 * Copyright (c) 1980, 1993
8 * The Regents of the University of California. All rights reserved.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the University of
21 * California, Berkeley and its contributors.
22 * 4. Neither the name of the University nor the names of its contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * SUCH DAMAGE.
39 #ifndef lint
40 #ifdef DOSCCS
41 static char sccsid[] = "@(#)popen.c 2.20 (gritter) 3/4/06";
42 #endif
43 #endif /* not lint */
45 #include "rcv.h"
46 #include "extern.h"
47 #include <sys/stat.h>
48 #include <fcntl.h>
49 #include <unistd.h>
50 #include <sys/wait.h>
51 #include <errno.h>
53 #ifndef NSIG
54 #define NSIG 32
55 #endif
57 #define READ 0
58 #define WRITE 1
60 struct fp {
61 FILE *fp;
62 struct fp *link;
63 char *realfile;
64 long offset;
65 int omode;
66 int pipe;
67 int pid;
68 enum {
69 FP_UNCOMPRESSED = 00,
70 FP_GZIPPED = 01,
71 FP_BZIP2ED = 02,
72 FP_IMAP = 03,
73 FP_MAILDIR = 04,
74 FP_MASK = 0177,
75 FP_READONLY = 0200
76 } compressed;
78 static struct fp *fp_head;
80 struct child {
81 int pid;
82 char done;
83 char free;
84 int status;
85 struct child *link;
87 static struct child *child;
89 static int scan_mode(const char *mode, int *omode);
90 static void register_file(FILE *fp, int omode, int pipe, int pid,
91 int compressed, const char *realfile, long offset);
92 static enum okay compress(struct fp *fpp);
93 static int decompress(int compression, int input, int output);
94 static enum okay unregister_file(FILE *fp);
95 static int file_pid(FILE *fp);
96 static int wait_command(int pid);
97 static struct child *findchild(int pid);
98 static void delchild(struct child *cp);
101 * Provide BSD-like signal() on all systems.
103 sighandler_type
104 safe_signal(int signum, sighandler_type handler)
106 struct sigaction nact, oact;
108 nact.sa_handler = handler;
109 sigemptyset(&nact.sa_mask);
110 nact.sa_flags = 0;
111 #ifdef SA_RESTART
112 nact.sa_flags |= SA_RESTART;
113 #endif
114 if (sigaction(signum, &nact, &oact) != 0)
115 return SIG_ERR;
116 return oact.sa_handler;
119 static int
120 scan_mode(const char *mode, int *omode)
123 if (!strcmp(mode, "r")) {
124 *omode = O_RDONLY;
125 } else if (!strcmp(mode, "w")) {
126 *omode = O_WRONLY | O_CREAT | O_TRUNC;
127 } else if (!strcmp(mode, "wx")) {
128 *omode = O_WRONLY | O_CREAT | O_EXCL;
129 } else if (!strcmp(mode, "a")) {
130 *omode = O_WRONLY | O_APPEND | O_CREAT;
131 } else if (!strcmp(mode, "a+")) {
132 *omode = O_RDWR | O_APPEND;
133 } else if (!strcmp(mode, "r+")) {
134 *omode = O_RDWR;
135 } else if (!strcmp(mode, "w+")) {
136 *omode = O_RDWR | O_CREAT | O_EXCL;
137 } else {
138 fprintf(stderr, catgets(catd, CATSET, 152,
139 "Internal error: bad stdio open mode %s\n"), mode);
140 errno = EINVAL;
141 return -1;
143 return 0;
146 FILE *
147 safe_fopen(const char *file, const char *mode, int *omode)
149 int fd;
151 if (scan_mode(mode, omode) < 0)
152 return NULL;
153 if ((fd = open(file, *omode, 0666)) < 0)
154 return NULL;
155 return fdopen(fd, mode);
158 FILE *
159 Fopen(const char *file, const char *mode)
161 FILE *fp;
162 int omode;
164 if ((fp = safe_fopen(file, mode, &omode)) != NULL) {
165 register_file(fp, omode, 0, 0, FP_UNCOMPRESSED, NULL, 0L);
166 fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
168 return fp;
171 FILE *
172 Fdopen(int fd, const char *mode)
174 FILE *fp;
175 int omode;
177 scan_mode(mode, &omode);
178 if ((fp = fdopen(fd, mode)) != NULL) {
179 register_file(fp, omode, 0, 0, FP_UNCOMPRESSED, NULL, 0L);
180 fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
182 return fp;
186 Fclose(FILE *fp)
188 int i = 0;
189 if (unregister_file(fp) == OKAY)
190 i |= 1;
191 if (fclose(fp) == 0)
192 i |= 2;
193 return i == 3 ? 0 : EOF;
196 FILE *
197 Zopen(const char *file, const char *mode, int *compression)
199 int input;
200 FILE *output;
201 const char *rp;
202 int omode;
203 char *tempfn;
204 int bits;
205 int _compression;
206 long offset;
207 char *extension;
208 enum protocol p;
210 if (scan_mode(mode, &omode) < 0)
211 return NULL;
212 if (compression == NULL)
213 compression = &_compression;
214 bits = R_OK | (omode == O_RDONLY ? 0 : W_OK);
215 if (omode & O_APPEND && ((p = which_protocol(file)) == PROTO_IMAP ||
216 p == PROTO_MAILDIR)) {
217 *compression = p == PROTO_IMAP ? FP_IMAP : FP_MAILDIR;
218 omode = O_RDWR | O_APPEND | O_CREAT;
219 rp = file;
220 input = -1;
221 goto open;
223 if ((extension = strrchr(file, '.')) != NULL) {
224 rp = file;
225 if (strcmp(extension, ".gz") == 0)
226 goto gzip;
227 if (strcmp(extension, ".bz2") == 0)
228 goto bz2;
230 if (access(file, F_OK) == 0) {
231 *compression = FP_UNCOMPRESSED;
232 return Fopen(file, mode);
233 } else if (access(rp=savecat(file, ".gz"), bits) == 0) {
234 gzip: *compression = FP_GZIPPED;
235 } else if (access(rp=savecat(file, ".bz2"), bits) == 0) {
236 bz2: *compression = FP_BZIP2ED;
237 } else {
238 *compression = FP_UNCOMPRESSED;
239 return Fopen(file, mode);
241 if (access(rp, W_OK) < 0)
242 *compression |= FP_READONLY;
243 if ((input = open(rp, bits & W_OK ? O_RDWR : O_RDONLY)) < 0
244 && ((omode&O_CREAT) == 0 || errno != ENOENT))
245 return NULL;
246 open: if ((output = Ftemp(&tempfn, "Rz", "w+", 0600, 0)) == NULL) {
247 perror(catgets(catd, CATSET, 167, "tmpfile"));
248 close(input);
249 return NULL;
251 unlink(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 close(input);
256 Fclose(output);
257 return NULL;
259 } else {
260 if ((input = creat(rp, 0666)) < 0) {
261 Fclose(output);
262 return NULL;
265 close(input);
266 fflush(output);
267 if (omode & O_APPEND) {
268 int flags;
270 if ((flags = fcntl(fileno(output), F_GETFL)) != -1)
271 fcntl(fileno(output), F_SETFL, flags | O_APPEND);
272 offset = ftell(output);
273 } else {
274 rewind(output);
275 offset = 0;
277 register_file(output, omode, 0, 0, *compression, rp, offset);
278 return output;
281 FILE *
282 Popen(const char *cmd, const char *mode, const char *shell, int newfd1)
284 int p[2];
285 int myside, hisside, fd0, fd1;
286 int pid;
287 char mod[2] = { '0', '\0' };
288 sigset_t nset;
289 FILE *fp;
291 if (pipe(p) < 0)
292 return NULL;
293 fcntl(p[READ], F_SETFD, FD_CLOEXEC);
294 fcntl(p[WRITE], F_SETFD, FD_CLOEXEC);
295 if (*mode == 'r') {
296 myside = p[READ];
297 fd0 = -1;
298 hisside = fd1 = p[WRITE];
299 mod[0] = *mode;
300 } else if (*mode == 'W') {
301 myside = p[WRITE];
302 hisside = fd0 = p[READ];
303 fd1 = newfd1;
304 mod[0] = 'w';
305 } else {
306 myside = p[WRITE];
307 hisside = fd0 = p[READ];
308 fd1 = -1;
309 mod[0] = 'w';
311 sigemptyset(&nset);
312 if (shell == NULL) {
313 pid = start_command(cmd, &nset, fd0, fd1, NULL, NULL, NULL);
314 } else {
315 pid = start_command(shell, &nset, fd0, fd1, "-c", cmd, NULL);
317 if (pid < 0) {
318 close(p[READ]);
319 close(p[WRITE]);
320 return NULL;
322 close(hisside);
323 if ((fp = fdopen(myside, mod)) != NULL)
324 register_file(fp, 0, 1, pid, FP_UNCOMPRESSED, NULL, 0L);
325 return fp;
329 Pclose(FILE *ptr)
331 int i;
332 sigset_t nset, oset;
334 i = file_pid(ptr);
335 if (i < 0)
336 return 0;
337 unregister_file(ptr);
338 fclose(ptr);
339 sigemptyset(&nset);
340 sigaddset(&nset, SIGINT);
341 sigaddset(&nset, SIGHUP);
342 sigprocmask(SIG_BLOCK, &nset, &oset);
343 i = wait_child(i);
344 sigprocmask(SIG_SETMASK, &oset, (sigset_t *)NULL);
345 return i;
348 void
349 close_all_files(void)
352 while (fp_head)
353 if (fp_head->pipe)
354 Pclose(fp_head->fp);
355 else
356 Fclose(fp_head->fp);
359 static void
360 register_file(FILE *fp, int omode, int pipe, int pid, int compressed,
361 const char *realfile, long offset)
363 struct fp *fpp;
365 fpp = (struct fp*)smalloc(sizeof *fpp);
366 fpp->fp = fp;
367 fpp->omode = omode;
368 fpp->pipe = pipe;
369 fpp->pid = pid;
370 fpp->link = fp_head;
371 fpp->compressed = compressed;
372 fpp->realfile = realfile ? sstrdup(realfile) : NULL;
373 fpp->offset = offset;
374 fp_head = fpp;
377 static enum okay
378 compress(struct fp *fpp)
380 int output;
381 char *command[2];
382 enum okay ok;
384 if (fpp->omode == O_RDONLY)
385 return OKAY;
386 fflush(fpp->fp);
387 clearerr(fpp->fp);
388 fseek(fpp->fp, fpp->offset, SEEK_SET);
389 if ((fpp->compressed&FP_MASK) == FP_IMAP) {
390 return imap_append(fpp->realfile, fpp->fp);
392 if ((fpp->compressed&FP_MASK) == FP_MAILDIR) {
393 return maildir_append(fpp->realfile, fpp->fp);
395 if ((output = open(fpp->realfile,
396 (fpp->omode|O_CREAT)&~O_EXCL,
397 0666)) < 0) {
398 fprintf(stderr, "Fatal: cannot create ");
399 perror(fpp->realfile);
400 return STOP;
402 if ((fpp->omode & O_APPEND) == 0)
403 ftruncate(output, 0);
404 switch (fpp->compressed & FP_MASK) {
405 case FP_GZIPPED:
406 command[0] = "gzip"; command[1] = "-c"; break;
407 case FP_BZIP2ED:
408 command[0] = "bzip2"; command[1] = "-c"; break;
409 default:
410 command[0] = "cat"; command[1] = NULL; break;
412 if (run_command(command[0], 0, fileno(fpp->fp), output,
413 command[1], NULL, NULL) < 0)
414 ok = STOP;
415 else
416 ok = OKAY;
417 close(output);
418 return ok;
421 static int
422 decompress(int compression, int input, int output)
424 char *command[2];
427 * Note that it is not possible to handle 'pack' or 'compress'
428 * formats because appending data does not work with them.
430 switch (compression & FP_MASK) {
431 case FP_GZIPPED: command[0] = "gzip"; command[1] = "-cd"; break;
432 case FP_BZIP2ED: command[0] = "bzip2"; command[1] = "-cd"; break;
433 case FP_IMAP: return 0;
434 case FP_MAILDIR: return 0;
435 default: command[0] = "cat"; command[1] = NULL;
437 return run_command(command[0], 0, input, output,
438 command[1], NULL, NULL);
441 static enum okay
442 unregister_file(FILE *fp)
444 struct fp **pp, *p;
445 enum okay ok = OKAY;
447 for (pp = &fp_head; (p = *pp) != (struct fp *)NULL; pp = &p->link)
448 if (p->fp == fp) {
449 if ((p->compressed&FP_MASK) != FP_UNCOMPRESSED)
450 ok = compress(p);
451 *pp = p->link;
452 free(p);
453 return ok;
455 panic(catgets(catd, CATSET, 153, "Invalid file pointer"));
456 /*NOTREACHED*/
457 return STOP;
460 static int
461 file_pid(FILE *fp)
463 struct fp *p;
465 for (p = fp_head; p; p = p->link)
466 if (p->fp == fp)
467 return (p->pid);
468 return -1;
472 * Run a command without a shell, with optional arguments and splicing
473 * of stdin and stdout. The command name can be a sequence of words.
474 * Signals must be handled by the caller.
475 * "Mask" contains the signals to ignore in the new process.
476 * SIGINT is enabled unless it's in the mask.
478 /*VARARGS4*/
480 run_command(char *cmd, sigset_t *mask, int infd, int outfd,
481 char *a0, char *a1, char *a2)
483 int pid;
485 if ((pid = start_command(cmd, mask, infd, outfd, a0, a1, a2)) < 0)
486 return -1;
487 return wait_command(pid);
490 /*VARARGS4*/
492 start_command(const char *cmd, sigset_t *mask, int infd, int outfd,
493 const char *a0, const char *a1, const char *a2)
495 int pid;
497 if ((pid = fork()) < 0) {
498 perror("fork");
499 return -1;
501 if (pid == 0) {
502 char *argv[100];
503 int i = getrawlist(cmd, strlen(cmd),
504 argv, sizeof argv / sizeof *argv, 0);
506 if ((argv[i++] = (char *)a0) != NULL &&
507 (argv[i++] = (char *)a1) != NULL &&
508 (argv[i++] = (char *)a2) != NULL)
509 argv[i] = NULL;
510 prepare_child(mask, infd, outfd);
511 execvp(argv[0], argv);
512 perror(argv[0]);
513 _exit(1);
515 return pid;
518 void
519 prepare_child(sigset_t *nset, int infd, int outfd)
521 int i;
522 sigset_t fset;
525 * All file descriptors other than 0, 1, and 2 are supposed to be
526 * close-on-exec.
528 if (infd >= 0)
529 dup2(infd, 0);
530 if (outfd >= 0)
531 dup2(outfd, 1);
532 if (nset) {
533 for (i = 1; i < NSIG; i++)
534 if (sigismember(nset, i))
535 safe_signal(i, SIG_IGN);
536 if (!sigismember(nset, SIGINT))
537 safe_signal(SIGINT, SIG_DFL);
539 sigfillset(&fset);
540 sigprocmask(SIG_UNBLOCK, &fset, (sigset_t *)NULL);
543 static int
544 wait_command(int pid)
547 if (wait_child(pid) < 0 && (value("bsdcompat") || value("bsdmsgs"))) {
548 printf(catgets(catd, CATSET, 154, "Fatal error in process.\n"));
549 return -1;
551 return 0;
554 static struct child *
555 findchild(int pid)
557 struct child **cpp;
559 for (cpp = &child; *cpp != (struct child *)NULL && (*cpp)->pid != pid;
560 cpp = &(*cpp)->link)
562 if (*cpp == (struct child *)NULL) {
563 *cpp = (struct child *) smalloc(sizeof (struct child));
564 (*cpp)->pid = pid;
565 (*cpp)->done = (*cpp)->free = 0;
566 (*cpp)->link = (struct child *)NULL;
568 return *cpp;
571 static void
572 delchild(struct child *cp)
574 struct child **cpp;
576 for (cpp = &child; *cpp != cp; cpp = &(*cpp)->link)
578 *cpp = cp->link;
579 free(cp);
582 /*ARGSUSED*/
583 void
584 sigchild(int signo)
586 int pid;
587 int status;
588 struct child *cp;
590 again:
591 while ((pid = waitpid(-1, (int*)&status, WNOHANG)) > 0) {
592 cp = findchild(pid);
593 if (cp->free)
594 delchild(cp);
595 else {
596 cp->done = 1;
597 cp->status = status;
600 if (pid == -1 && errno == EINTR)
601 goto again;
604 int wait_status;
607 * Mark a child as don't care.
609 void
610 free_child(int pid)
612 sigset_t nset, oset;
613 struct child *cp = findchild(pid);
614 sigemptyset(&nset);
615 sigaddset(&nset, SIGCHLD);
616 sigprocmask(SIG_BLOCK, &nset, &oset);
618 if (cp->done)
619 delchild(cp);
620 else
621 cp->free = 1;
622 sigprocmask(SIG_SETMASK, &oset, (sigset_t *)NULL);
626 * Wait for a specific child to die.
628 #if 0
630 * This version is correct code, but causes harm on some loosing
631 * systems. So we use the second one instead.
633 int
634 wait_child(int pid)
636 sigset_t nset, oset;
637 struct child *cp = findchild(pid);
638 sigemptyset(&nset);
639 sigaddset(&nset, SIGCHLD);
640 sigprocmask(SIG_BLOCK, &nset, &oset);
642 while (!cp->done)
643 sigsuspend(&oset);
644 wait_status = cp->status;
645 delchild(cp);
646 sigprocmask(SIG_SETMASK, &oset, (sigset_t *)NULL);
648 if (WIFEXITED(wait_status) && (WEXITSTATUS(wait_status) == 0))
649 return 0;
650 return -1;
652 #endif
653 int
654 wait_child(int pid)
656 pid_t term;
657 struct child *cp;
658 struct sigaction nact, oact;
660 nact.sa_handler = SIG_DFL;
661 sigemptyset(&nact.sa_mask);
662 nact.sa_flags = SA_NOCLDSTOP;
663 sigaction(SIGCHLD, &nact, &oact);
665 cp = findchild(pid);
666 if (!cp->done) {
667 do {
668 term = wait(&wait_status);
669 if (term == -1 && errno == EINTR)
670 continue;
671 if (term == 0 || term == -1)
672 break;
673 cp = findchild(term);
674 if (cp->free || term == pid) {
675 delchild(cp);
676 } else {
677 cp->done = 1;
678 cp->status = wait_status;
680 } while (term != pid);
681 } else {
682 wait_status = cp->status;
683 delchild(cp);
686 sigaction(SIGCHLD, &oact, NULL);
688 * Make sure no zombies are left.
690 sigchild(SIGCHLD);
692 if (WIFEXITED(wait_status) && (WEXITSTATUS(wait_status) == 0))
693 return 0;
694 return -1;