2 * Heirloom mailx - a mail user agent derived from Berkeley Mail.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
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
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
41 static char sccsid
[] = "@(#)popen.c 2.20 (gritter) 3/4/06";
78 static struct fp
*fp_head
;
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.
104 safe_signal(int signum
, sighandler_type handler
)
106 struct sigaction nact
, oact
;
108 nact
.sa_handler
= handler
;
109 sigemptyset(&nact
.sa_mask
);
112 nact
.sa_flags
|= SA_RESTART
;
114 if (sigaction(signum
, &nact
, &oact
) != 0)
116 return oact
.sa_handler
;
120 scan_mode(const char *mode
, int *omode
)
123 if (!strcmp(mode
, "r")) {
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+")) {
135 } else if (!strcmp(mode
, "w+")) {
136 *omode
= O_RDWR
| O_CREAT
| O_EXCL
;
138 fprintf(stderr
, catgets(catd
, CATSET
, 152,
139 "Internal error: bad stdio open mode %s\n"), mode
);
147 safe_fopen(const char *file
, const char *mode
, int *omode
)
151 if (scan_mode(mode
, omode
) < 0)
153 if ((fd
= open(file
, *omode
, 0666)) < 0)
155 return fdopen(fd
, mode
);
159 Fopen(const char *file
, const char *mode
)
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
);
172 Fdopen(int fd
, const char *mode
)
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
);
189 if (unregister_file(fp
) == OKAY
)
193 return i
== 3 ? 0 : EOF
;
197 Zopen(const char *file
, const char *mode
, int *compression
)
210 if (scan_mode(mode
, &omode
) < 0)
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
;
223 if ((extension
= strrchr(file
, '.')) != NULL
) {
225 if (strcmp(extension
, ".gz") == 0)
227 if (strcmp(extension
, ".bz2") == 0)
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
;
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
))
246 open
: if ((output
= Ftemp(&tempfn
, "Rz", "w+", 0600, 0)) == NULL
) {
247 perror(catgets(catd
, CATSET
, 167, "tmpfile"));
252 if (input
>= 0 || (*compression
&FP_MASK
) == FP_IMAP
||
253 (*compression
&FP_MASK
) == FP_MAILDIR
) {
254 if (decompress(*compression
, input
, fileno(output
)) < 0) {
260 if ((input
= creat(rp
, 0666)) < 0) {
267 if (omode
& O_APPEND
) {
270 if ((flags
= fcntl(fileno(output
), F_GETFL
)) != -1)
271 fcntl(fileno(output
), F_SETFL
, flags
| O_APPEND
);
272 offset
= ftell(output
);
277 register_file(output
, omode
, 0, 0, *compression
, rp
, offset
);
282 Popen(const char *cmd
, const char *mode
, const char *shell
, int newfd1
)
285 int myside
, hisside
, fd0
, fd1
;
287 char mod
[2] = { '0', '\0' };
293 fcntl(p
[READ
], F_SETFD
, FD_CLOEXEC
);
294 fcntl(p
[WRITE
], F_SETFD
, FD_CLOEXEC
);
298 hisside
= fd1
= p
[WRITE
];
300 } else if (*mode
== 'W') {
302 hisside
= fd0
= p
[READ
];
307 hisside
= fd0
= p
[READ
];
313 pid
= start_command(cmd
, &nset
, fd0
, fd1
, NULL
, NULL
, NULL
);
315 pid
= start_command(shell
, &nset
, fd0
, fd1
, "-c", cmd
, NULL
);
323 if ((fp
= fdopen(myside
, mod
)) != NULL
)
324 register_file(fp
, 0, 1, pid
, FP_UNCOMPRESSED
, NULL
, 0L);
337 unregister_file(ptr
);
340 sigaddset(&nset
, SIGINT
);
341 sigaddset(&nset
, SIGHUP
);
342 sigprocmask(SIG_BLOCK
, &nset
, &oset
);
344 sigprocmask(SIG_SETMASK
, &oset
, (sigset_t
*)NULL
);
349 close_all_files(void)
360 register_file(FILE *fp
, int omode
, int pipe
, int pid
, int compressed
,
361 const char *realfile
, long offset
)
365 fpp
= (struct fp
*)smalloc(sizeof *fpp
);
371 fpp
->compressed
= compressed
;
372 fpp
->realfile
= realfile
? sstrdup(realfile
) : NULL
;
373 fpp
->offset
= offset
;
378 compress(struct fp
*fpp
)
384 if (fpp
->omode
== O_RDONLY
)
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
,
398 fprintf(stderr
, "Fatal: cannot create ");
399 perror(fpp
->realfile
);
402 if ((fpp
->omode
& O_APPEND
) == 0)
403 ftruncate(output
, 0);
404 switch (fpp
->compressed
& FP_MASK
) {
406 command
[0] = "gzip"; command
[1] = "-c"; break;
408 command
[0] = "bzip2"; command
[1] = "-c"; break;
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)
422 decompress(int compression
, int input
, int output
)
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
);
442 unregister_file(FILE *fp
)
447 for (pp
= &fp_head
; (p
= *pp
) != (struct fp
*)NULL
; pp
= &p
->link
)
449 if ((p
->compressed
&FP_MASK
) != FP_UNCOMPRESSED
)
455 panic(catgets(catd
, CATSET
, 153, "Invalid file pointer"));
465 for (p
= fp_head
; p
; p
= p
->link
)
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.
480 run_command(char *cmd
, sigset_t
*mask
, int infd
, int outfd
,
481 char *a0
, char *a1
, char *a2
)
485 if ((pid
= start_command(cmd
, mask
, infd
, outfd
, a0
, a1
, a2
)) < 0)
487 return wait_command(pid
);
492 start_command(const char *cmd
, sigset_t
*mask
, int infd
, int outfd
,
493 const char *a0
, const char *a1
, const char *a2
)
497 if ((pid
= fork()) < 0) {
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
)
510 prepare_child(mask
, infd
, outfd
);
511 execvp(argv
[0], argv
);
519 prepare_child(sigset_t
*nset
, int infd
, int outfd
)
525 * All file descriptors other than 0, 1, and 2 are supposed to be
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
);
540 sigprocmask(SIG_UNBLOCK
, &fset
, (sigset_t
*)NULL
);
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"));
554 static struct child
*
559 for (cpp
= &child
; *cpp
!= (struct child
*)NULL
&& (*cpp
)->pid
!= pid
;
562 if (*cpp
== (struct child
*)NULL
) {
563 *cpp
= (struct child
*) smalloc(sizeof (struct child
));
565 (*cpp
)->done
= (*cpp
)->free
= 0;
566 (*cpp
)->link
= (struct child
*)NULL
;
572 delchild(struct child
*cp
)
576 for (cpp
= &child
; *cpp
!= cp
; cpp
= &(*cpp
)->link
)
591 while ((pid
= waitpid(-1, (int*)&status
, WNOHANG
)) > 0) {
600 if (pid
== -1 && errno
== EINTR
)
607 * Mark a child as don't care.
613 struct child
*cp
= findchild(pid
);
615 sigaddset(&nset
, SIGCHLD
);
616 sigprocmask(SIG_BLOCK
, &nset
, &oset
);
622 sigprocmask(SIG_SETMASK
, &oset
, (sigset_t
*)NULL
);
626 * Wait for a specific child to die.
630 * This version is correct code, but causes harm on some loosing
631 * systems. So we use the second one instead.
637 struct child
*cp
= findchild(pid
);
639 sigaddset(&nset
, SIGCHLD
);
640 sigprocmask(SIG_BLOCK
, &nset
, &oset
);
644 wait_status
= cp
->status
;
646 sigprocmask(SIG_SETMASK
, &oset
, (sigset_t
*)NULL
);
648 if (WIFEXITED(wait_status
) && (WEXITSTATUS(wait_status
) == 0))
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
);
668 term
= wait(&wait_status
);
669 if (term
== -1 && errno
== EINTR
)
671 if (term
== 0 || term
== -1)
673 cp
= findchild(term
);
674 if (cp
->free
|| term
== pid
) {
678 cp
->status
= wait_status
;
680 } while (term
!= pid
);
682 wait_status
= cp
->status
;
686 sigaction(SIGCHLD
, &oact
, NULL
);
688 * Make sure no zombies are left.
692 if (WIFEXITED(wait_status
) && (WEXITSTATUS(wait_status
) == 0))