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 - 2014 Steffen "Daode" Nurpmeso <sdaoden@users.sf.net>.
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
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
40 #ifndef HAVE_AMALGAMATION
69 static struct fp
*fp_head
;
78 static struct child
*_popen_child
;
80 static int scan_mode(const char *mode
, int *omode
);
81 static void register_file(FILE *fp
, int omode
, int ispipe
, int pid
,
82 int compressed
, const char *realfile
, long offset
);
83 static enum okay
compress(struct fp
*fpp
);
84 static int decompress(int compression
, int infd
, int outfd
);
85 static enum okay
unregister_file(FILE *fp
);
86 static int file_pid(FILE *fp
);
87 static int wait_command(int pid
);
88 static struct child
*findchild(int pid
);
89 static void delchild(struct child
*cp
);
92 scan_mode(const char *mode
, int *omode
)
95 if (!strcmp(mode
, "r")) {
97 } else if (!strcmp(mode
, "w")) {
98 *omode
= O_WRONLY
| O_CREAT
| O_TRUNC
;
99 } else if (!strcmp(mode
, "wx")) {
100 *omode
= O_WRONLY
| O_CREAT
| O_EXCL
;
101 } else if (!strcmp(mode
, "a")) {
102 *omode
= O_WRONLY
| O_APPEND
| O_CREAT
;
103 } else if (!strcmp(mode
, "a+")) {
104 *omode
= O_RDWR
| O_APPEND
;
105 } else if (!strcmp(mode
, "r+")) {
107 } else if (!strcmp(mode
, "w+")) {
108 *omode
= O_RDWR
| O_CREAT
| O_EXCL
;
110 fprintf(stderr
, tr(152,
111 "Internal error: bad stdio open mode %s\n"), mode
);
113 *omode
= 0; /* (silence CC) */
120 safe_fopen(const char *file
, const char *mode
, int *omode
)
124 if (scan_mode(mode
, omode
) < 0)
126 if ((fd
= open(file
, *omode
, 0666)) < 0)
128 return fdopen(fd
, mode
);
132 Fopen(const char *file
, const char *mode
)
137 if ((fp
= safe_fopen(file
, mode
, &omode
)) != NULL
) {
138 (void)fcntl(fileno(fp
), F_SETFD
, FD_CLOEXEC
);
139 register_file(fp
, omode
, 0, 0, FP_UNCOMPRESSED
, NULL
, 0L);
145 Fdopen(int fd
, const char *mode
)
150 scan_mode(mode
, &omode
);
151 if ((fp
= fdopen(fd
, mode
)) != NULL
) {
152 (void)fcntl(fileno(fp
), F_SETFD
, FD_CLOEXEC
);
153 register_file(fp
, omode
, 0, 0, FP_UNCOMPRESSED
, NULL
, 0L);
162 if (unregister_file(fp
) == OKAY
)
166 return i
== 3 ? 0 : EOF
;
170 Zopen(const char *file
, const char *mode
, int *compression
) /* TODO MESS!
171 * TODO maybe we shouldn't be simple and run commands but instead
172 * TODO links against any of available zlib.h, bzlib.h, lzma.h!
173 * TODO even libzip? Much faster, not that much more work, and we're
174 * TODO *so* fixed anyway!!
175 * TODO *or*: make it all hookable via BOXEXTENSION/DECOMPRESS etc.
176 * TODO by the user; but not like this, Coverity went grazy about it */
189 if (scan_mode(mode
, &omode
) < 0)
191 if (compression
== NULL
)
192 compression
= &_compression
;
193 bits
= R_OK
| (omode
== O_RDONLY
? 0 : W_OK
);
194 if (omode
& O_APPEND
&& ((p
= which_protocol(file
)) == PROTO_IMAP
||
195 p
== PROTO_MAILDIR
)) {
196 *compression
= p
== PROTO_IMAP
? FP_IMAP
: FP_MAILDIR
;
197 omode
= O_RDWR
| O_APPEND
| O_CREAT
;
202 if ((extension
= strrchr(file
, '.')) != NULL
) {
204 if (strcmp(extension
, ".gz") == 0)
206 if (strcmp(extension
, ".bz2") == 0)
210 if (access(file
, F_OK
) == 0) {
211 *compression
= FP_UNCOMPRESSED
;
212 return Fopen(file
, mode
);
213 } else if (access(rp
=savecat(file
, ".gz"), bits
) == 0) {
214 gzip
: *compression
= FP_GZIPPED
;
215 } else if (access(rp
=savecat(file
, ".bz2"), bits
) == 0) {
216 bz2
: *compression
= FP_BZIP2ED
;
218 *compression
= FP_UNCOMPRESSED
;
219 return Fopen(file
, mode
);
221 if (access(rp
, W_OK
) < 0)
222 *compression
|= FP_READONLY
;
224 if ((infd
= open(rp
, bits
& W_OK
? O_RDWR
: O_RDONLY
)) < 0
225 && ((omode
&O_CREAT
) == 0 || errno
!= ENOENT
))
227 open
: if ((outf
= Ftemp(&tempfn
, "Rz", "w+", 0600, 0)) == NULL
) {
228 perror(tr(167, "tmpfile"));
236 if (infd
>= 0 || (*compression
&FP_MASK
) == FP_IMAP
||
237 (*compression
&FP_MASK
) == FP_MAILDIR
) {
238 if (decompress(*compression
, infd
, fileno(outf
)) < 0) {
245 if ((infd
= creat(rp
, 0666)) < 0) {
254 if (omode
& O_APPEND
) {
257 if ((flags
= fcntl(fileno(outf
), F_GETFL
)) < 0 ||
258 fcntl(fileno(outf
), F_SETFL
, flags
|O_APPEND
)
259 < 0 || (offset
= ftell(outf
)) < 0) {
268 register_file(outf
, omode
, 0, 0, *compression
, rp
, offset
);
273 Ftemp(char **fn
, char const *prefix
, char const *mode
, int bits
,
281 cp
= smalloc(strlen(tempdir
) + 1 + sizeof("mail") + strlen(prefix
) +
283 cp
= sstpcpy(cp
, tempdir
);
285 cp
= sstpcpy(cp
, "mail");
288 cp
= sstpcpy(cp
, prefix
);
290 sstpcpy(cp
, ".XXXXXX");
293 if ((fd
= mkstemp(*fn
)) < 0)
295 if (bits
!= (S_IRUSR
|S_IWUSR
) && fchmod(fd
, bits
) < 0)
298 if (mktemp(*fn
) == NULL
)
300 if ((fd
= open(*fn
, O_CREAT
|O_EXCL
|O_RDWR
, bits
)) < 0)
305 fp
= Fdopen(fd
, mode
);
307 (void)fcntl(fd
, F_SETFD
, FD_CLOEXEC
);
308 fp
= fdopen(fd
, mode
);
327 pipe_cloexec(int fd
[2])
333 fcntl(fd
[0], F_SETFD
, FD_CLOEXEC
);
334 fcntl(fd
[1], F_SETFD
, FD_CLOEXEC
);
341 Popen(const char *cmd
, const char *mode
, const char *sh
, int newfd1
)
344 int myside
, hisside
, fd0
, fd1
;
346 char mod
[2] = { '0', '\0' };
350 if (! pipe_cloexec(p
))
356 hisside
= fd1
= p
[WRITE
];
358 } else if (*mode
== 'W') {
360 hisside
= fd0
= p
[READ
];
365 hisside
= fd0
= p
[READ
];
371 pid
= start_command(cmd
, &nset
, fd0
, fd1
, NULL
, NULL
, NULL
);
373 pid
= start_command(sh
, &nset
, fd0
, fd1
, "-c", cmd
, NULL
);
381 if ((fp
= fdopen(myside
, mod
)) != NULL
)
382 register_file(fp
, 0, 1, pid
, FP_UNCOMPRESSED
, NULL
, 0L);
387 Pclose(FILE *ptr
, bool_t dowait
)
396 unregister_file(ptr
);
400 sigaddset(&nset
, SIGINT
);
401 sigaddset(&nset
, SIGHUP
);
402 sigprocmask(SIG_BLOCK
, &nset
, &oset
);
403 rv
= wait_child(pid
, NULL
);
404 sigprocmask(SIG_SETMASK
, &oset
, (sigset_t
*)NULL
);
414 close_all_files(void)
418 Pclose(fp_head
->fp
, TRU1
);
424 register_file(FILE *fp
, int omode
, int ispipe
, int pid
, int compressed
,
425 const char *realfile
, long offset
)
429 fpp
= (struct fp
*)smalloc(sizeof *fpp
);
435 fpp
->compressed
= compressed
;
436 fpp
->realfile
= realfile
? sstrdup(realfile
) : NULL
;
437 fpp
->offset
= offset
;
442 compress(struct fp
*fpp
)
445 char const *command
[2];
448 if (fpp
->omode
== O_RDONLY
)
452 if (fseek(fpp
->fp
, fpp
->offset
, SEEK_SET
) < 0)
455 if ((fpp
->compressed
&FP_MASK
) == FP_IMAP
) {
456 return imap_append(fpp
->realfile
, fpp
->fp
);
459 if ((fpp
->compressed
&FP_MASK
) == FP_MAILDIR
) {
460 return maildir_append(fpp
->realfile
, fpp
->fp
);
462 if ((outfd
= open(fpp
->realfile
,
463 (fpp
->omode
|O_CREAT
)&~O_EXCL
,
465 fprintf(stderr
, "Fatal: cannot create ");
466 perror(fpp
->realfile
);
469 if ((fpp
->omode
& O_APPEND
) == 0)
471 switch (fpp
->compressed
& FP_MASK
) {
473 command
[0] = "gzip"; command
[1] = "-c"; break;
475 command
[0] = "bzip2"; command
[1] = "-c"; break;
477 command
[0] = "cat"; command
[1] = NULL
; break;
479 if (run_command(command
[0], 0, fileno(fpp
->fp
), outfd
,
480 command
[1], NULL
, NULL
) < 0)
489 decompress(int compression
, int infd
, int outfd
)
491 char const *command
[2];
494 * Note that it is not possible to handle 'pack' or 'compress'
495 * formats because appending data does not work with them.
497 switch (compression
& FP_MASK
) {
498 case FP_GZIPPED
: command
[0] = "gzip"; command
[1] = "-cd"; break;
499 case FP_BZIP2ED
: command
[0] = "bzip2"; command
[1] = "-cd"; break;
500 case FP_IMAP
: return 0;
501 case FP_MAILDIR
: return 0;
502 default: command
[0] = "cat"; command
[1] = NULL
;
504 return run_command(command
[0], 0, infd
, outfd
, command
[1], NULL
, NULL
);
508 unregister_file(FILE *fp
)
513 for (pp
= &fp_head
; (p
= *pp
) != (struct fp
*)NULL
; pp
= &p
->link
)
515 if ((p
->compressed
&FP_MASK
) != FP_UNCOMPRESSED
)
521 panic(tr(153, "Invalid file pointer"));
531 for (p
= fp_head
; p
; p
= p
->link
)
538 * Run a command without a shell, with optional arguments and splicing
539 * of stdin and stdout. The command name can be a sequence of words.
540 * Signals must be handled by the caller.
541 * "Mask" contains the signals to ignore in the new process.
542 * SIGINT is enabled unless it's in the mask.
546 run_command(char const *cmd
, sigset_t
*mask
, int infd
, int outfd
,
547 char const *a0
, char const *a1
, char const *a2
)
551 if ((pid
= start_command(cmd
, mask
, infd
, outfd
, a0
, a1
, a2
)) < 0)
553 return wait_command(pid
);
558 start_command(const char *cmd
, sigset_t
*mask
, int infd
, int outfd
,
559 const char *a0
, const char *a1
, const char *a2
)
563 if ((pid
= fork()) < 0) {
569 int i
= getrawlist(cmd
, strlen(cmd
),
570 argv
, sizeof argv
/ sizeof *argv
, 0);
572 if ((argv
[i
++] = UNCONST(a0
)) != NULL
&&
573 (argv
[i
++] = UNCONST(a1
)) != NULL
&&
574 (argv
[i
++] = UNCONST(a2
)) != NULL
)
576 prepare_child(mask
, infd
, outfd
);
577 execvp(argv
[0], argv
);
585 prepare_child(sigset_t
*nset
, int infd
, int outfd
)
590 /* All file descriptors other than 0, 1, and 2 are supposed to be
598 for (i
= 1; i
< NSIG
; i
++)
599 if (sigismember(nset
, i
))
600 safe_signal(i
, SIG_IGN
);
601 if (!sigismember(nset
, SIGINT
))
602 safe_signal(SIGINT
, SIG_DFL
);
606 sigprocmask(SIG_SETMASK
, &fset
, NULL
);
610 wait_command(int pid
)
614 if (!wait_child(pid
, NULL
)) {
615 if (ok_blook(bsdcompat
) || ok_blook(bsdmsgs
))
616 fprintf(stderr
, tr(154, "Fatal error in process.\n"));
622 static struct child
*
627 for (cpp
= &_popen_child
; *cpp
!= NULL
&& (*cpp
)->pid
!= pid
;
631 *cpp
= smalloc(sizeof (struct child
));
633 (*cpp
)->done
= (*cpp
)->free
= 0;
640 delchild(struct child
*cp
)
644 for (cpp
= &_popen_child
; *cpp
!= cp
; cpp
= &(*cpp
)->link
)
659 while ((pid
= waitpid(-1, &status
, WNOHANG
)) > 0) {
668 if (pid
== -1 && errno
== EINTR
)
673 * Mark a child as don't care.
682 sigaddset(&nset
, SIGCHLD
);
683 sigprocmask(SIG_BLOCK
, &nset
, &oset
);
691 sigprocmask(SIG_SETMASK
, &oset
, NULL
);
695 wait_child(int pid
, int *wait_status
)
702 sigaddset(&nset
, SIGCHLD
);
703 sigprocmask(SIG_BLOCK
, &nset
, &oset
);
711 sigprocmask(SIG_SETMASK
, &oset
, NULL
);
713 if (wait_status
!= NULL
)
715 return (WIFEXITED(ws
) && WEXITSTATUS(ws
) == 0);