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
54 # define _SET_CLOEXEC(FD) do fcntl((FD), F_SETFD, FD_CLOEXEC); while (0)
56 # define _SET_CLOEXEC(FD) do {} while (0)
87 static struct fp
*fp_head
;
88 static struct child
*_popen_child
;
90 static int scan_mode(char const *mode
, int *omode
);
91 static void register_file(FILE *fp
, int omode
, int ispipe
, int pid
,
92 int compressed
, char const *realfile
, long offset
);
93 static enum okay
_compress(struct fp
*fpp
);
94 static int _decompress(int compression
, int infd
, int outfd
);
95 static enum okay
unregister_file(FILE *fp
);
96 static int file_pid(FILE *fp
);
99 static void _sigchld(int signo
);
101 static int wait_command(int pid
);
102 static struct child
*findchild(int pid
);
103 static void delchild(struct child
*cp
);
106 scan_mode(char const *mode
, int *omode
)
113 {"w", O_WRONLY
| O_CREAT
| O_TRUNC
},
114 {"wx", O_WRONLY
| O_CREAT
| O_EXCL
},
115 {"a", O_WRONLY
| O_APPEND
| O_CREAT
},
116 {"a+", O_RDWR
| O_APPEND
},
118 {"w+", O_RDWR
| O_CREAT
| O_EXCL
}
124 for (i
= 0; UICMP(z
, i
, <, NELEM(maps
)); ++i
)
125 if (!strcmp(maps
[i
].mode
, mode
)) {
126 *omode
= maps
[i
].omode
;
131 alert(_("Internal error: bad stdio open mode %s\n"), mode
);
133 *omode
= 0; /* (silence CC) */
141 register_file(FILE *fp
, int omode
, int ispipe
, int pid
, int compressed
,
142 char const *realfile
, long offset
)
147 fpp
= smalloc(sizeof *fpp
);
153 fpp
->compressed
= compressed
;
154 fpp
->realfile
= (realfile
!= NULL
) ? sstrdup(realfile
) : NULL
;
155 fpp
->offset
= offset
;
161 _compress(struct fp
*fpp
)
168 if (fpp
->omode
== O_RDONLY
) {
176 if (fseek(fpp
->fp
, fpp
->offset
, SEEK_SET
) == -1)
180 if ((fpp
->compressed
& FP_MASK
) == FP_IMAP
) {
181 rv
= imap_append(fpp
->realfile
, fpp
->fp
);
185 if ((fpp
->compressed
& FP_MASK
) == FP_MAILDIR
) {
186 rv
= maildir_append(fpp
->realfile
, fpp
->fp
);
190 outfd
= open(fpp
->realfile
, (fpp
->omode
| O_CREAT
) & ~O_EXCL
, 0666);
192 fprintf(stderr
, "Fatal: cannot create ");
193 perror(fpp
->realfile
);
196 if (!(fpp
->omode
& O_APPEND
))
198 switch (fpp
->compressed
& FP_MASK
) {
200 cmd
[0] = "gzip"; cmd
[1] = "-c"; break;
202 cmd
[0] = "bzip2"; cmd
[1] = "-c"; break;
204 cmd
[0] = "xz"; cmd
[1] = "-c"; break;
206 cmd
[0] = "cat"; cmd
[1] = NULL
; break;
208 if (run_command(cmd
[0], 0, fileno(fpp
->fp
), outfd
, cmd
[1], NULL
, NULL
) >= 0)
217 _decompress(int compression
, int infd
, int outfd
)
223 switch (compression
& FP_MASK
) {
224 case FP_GZIP
: cmd
[0] = "gzip"; cmd
[1] = "-cd"; break;
225 case FP_BZIP2
: cmd
[0] = "bzip2"; cmd
[1] = "-cd"; break;
226 case FP_XZ
: cmd
[0] = "xz"; cmd
[1] = "-cd"; break;
227 default: cmd
[0] = "cat"; cmd
[1] = NULL
; break;
233 rv
= run_command(cmd
[0], 0, infd
, outfd
, cmd
[1], NULL
, NULL
);
240 unregister_file(FILE *fp
)
246 for (pp
= &fp_head
; (p
= *pp
) != NULL
; pp
= &p
->link
)
248 if ((p
->compressed
& FP_MASK
) != FP_RAW
) /* TODO ;} */
251 if (p
->realfile
!= NULL
)
261 (_("Invalid file pointer"));
276 for (p
= fp_head
; p
; p
= p
->link
)
290 NYD_X
; /* Signal handler */
294 pid
= (int)waitpid((pid_t
)-1, &status
, WNOHANG
);
296 if (pid
== -1 && errno
== EINTR
)
311 wait_command(int pid
)
316 if (!wait_child(pid
, NULL
)) {
317 if (ok_blook(bsdcompat
) || ok_blook(bsdmsgs
))
318 fprintf(stderr
, _("Fatal error in process.\n"));
325 static struct child
*
331 for (cpp
= &_popen_child
; *cpp
!= NULL
&& (*cpp
)->pid
!= pid
;
335 *cpp
= smalloc(sizeof **cpp
);
337 (*cpp
)->done
= (*cpp
)->free
= 0;
345 delchild(struct child
*cp
)
350 for (cpp
= &_popen_child
; *cpp
!= cp
; cpp
= &(*cpp
)->link
)
358 command_manager_start(void)
360 struct sigaction nact
, oact
;
363 nact
.sa_handler
= &_sigchld
;
364 sigemptyset(&nact
.sa_mask
);
373 if (sigaction(SIGCHLD
, &nact
, &oact
) != 0)
374 panic("Cannot install signal handler for child process management");
379 safe_fopen(char const *file
, char const *oflags
, int *xflags
)
383 NYD2_ENTER
; /* (only for Fopen() and once in lex.c) */
385 if (scan_mode(oflags
, &osflags
) < 0)
387 osflags
|= O_CLOEXEC
;
391 if ((fd
= open(file
, osflags
, 0666)) == -1)
395 fp
= fdopen(fd
, oflags
);
402 Fopen(char const *file
, char const *oflags
)
408 if ((fp
= safe_fopen(file
, oflags
, &osflags
)) != NULL
)
409 register_file(fp
, osflags
, 0, 0, FP_RAW
, NULL
, 0L);
415 Fdopen(int fd
, char const *oflags
)
421 scan_mode(oflags
, &osflags
);
422 osflags
|= O_CLOEXEC
;
424 if ((fp
= fdopen(fd
, oflags
)) != NULL
)
425 register_file(fp
, osflags
, 0, 0, FP_RAW
, NULL
, 0L);
436 if (unregister_file(fp
) == OKAY
)
441 return (i
== 3 ? 0 : EOF
);
445 Zopen(char const *file
, char const *oflags
, int *compression
) /* FIXME MESS! */
448 int _compression
, osflags
, mode
, infd
;
454 if (compression
== NULL
)
455 compression
= &_compression
;
457 if (scan_mode(oflags
, &osflags
) < 0)
459 rof
= OF_RDWR
| OF_UNLINK
;
460 if (osflags
& O_APPEND
)
462 if (osflags
== O_RDONLY
) {
464 *compression
= FP_READONLY
;
471 if ((osflags
& O_APPEND
) && ((p
= which_protocol(file
)) == PROTO_IMAP
||
472 p
== PROTO_MAILDIR
)) {
473 *compression
|= (p
== PROTO_IMAP
) ? FP_IMAP
: FP_MAILDIR
;
474 osflags
= O_RDWR
| O_APPEND
| O_CREAT
;
479 if ((ext
= strrchr(file
, '.')) != NULL
) {
480 if (!strcmp(ext
, ".gz"))
481 *compression
|= FP_GZIP
;
482 else if (!strcmp(ext
, ".xz"))
483 *compression
|= FP_XZ
;
484 else if (!strcmp(ext
, ".bz2"))
485 *compression
|= FP_BZIP2
;
490 *compression
|= FP_RAW
;
491 rv
= Fopen(file
, oflags
);
494 if ((infd
= open(file
, (mode
& W_OK
) ? O_RDWR
: O_RDONLY
)) == -1 &&
495 (!(osflags
& O_CREAT
) || errno
!= ENOENT
))
499 if ((rv
= Ftmp(NULL
, "zopen", rof
, 0600)) == NULL
) {
500 perror(_("tmpfile"));
503 if (infd
>= 0 || (*compression
& FP_MASK
) == FP_IMAP
||
504 (*compression
& FP_MASK
) == FP_MAILDIR
) {
505 if (_decompress(*compression
, infd
, fileno(rv
)) < 0) {
515 if ((infd
= creat(file
, 0666)) == -1) {
525 if (!(osflags
& O_APPEND
))
527 if ((offset
= ftell(rv
)) == -1) {
532 register_file(rv
, osflags
, 0, 0, *compression
, file
, offset
);
539 Ftmp(char **fn
, char const *prefix
, enum oflags oflags
, int mode
)
547 cp
= smalloc(strlen(tempdir
) + 1 + sizeof("mail") + strlen(prefix
) + 7 +1);
548 cp
= sstpcpy(cp
, tempdir
);
550 cp
= sstpcpy(cp
, "mail");
553 cp
= sstpcpy(cp
, prefix
);
555 /* TODO Ftmp(): unroll our own creation loop with atoi(random()) */
556 sstpcpy(cp
, ".XXXXXX");
560 if ((fd
= mkstemp(cp_base
)) == -1)
562 if (mode
!= (S_IRUSR
| S_IWUSR
) && fchmod(fd
, mode
) == -1)
564 if (oflags
& OF_APPEND
) {
567 if ((f
= fcntl(fd
, F_GETFL
)) == -1 ||
568 fcntl(fd
, F_SETFL
, f
| O_APPEND
) == -1) {
574 if (!(oflags
& OF_REGISTER
))
575 fcntl(fd
, F_SETFD
, FD_CLOEXEC
);
577 if (mktemp(cp_base
) == NULL
)
579 if ((fd
= open(cp_base
, O_CREAT
| O_EXCL
| O_RDWR
| O_CLOEXEC
|
580 (oflags
& OF_APPEND
? O_APPEND
: 0), mode
)) == -1)
582 if (!(oflags
& OF_REGISTER
))
586 fp
= (*((oflags
& OF_REGISTER
) ? &Fdopen
: &fdopen
))(fd
,
587 (oflags
& OF_RDWR
? "w+" : "w"));
588 if (fp
== NULL
|| (oflags
& OF_UNLINK
)) {
597 if (fp
== NULL
|| !(oflags
& OF_HOLDSIGS
))
602 if ((cp
= cp_base
) != NULL
)
608 Ftmp_release(char **fn
)
637 pipe_cloexec(int fd
[2])
643 if (pipe2(fd
, O_CLOEXEC
) == -1)
648 fcntl(fd
[0], F_SETFD
, FD_CLOEXEC
);
649 fcntl(fd
[1], F_SETFD
, FD_CLOEXEC
);
658 Popen(char const *cmd
, char const *mode
, char const *sh
,
659 char const **env_addon
, int newfd1
)
661 int p
[2], myside
, hisside
, fd0
, fd1
, pid
;
662 char mod
[2] = {'0', '\0'};
667 if (!pipe_cloexec(p
))
673 hisside
= fd1
= p
[WRITE
];
675 } else if (*mode
== 'W') {
677 hisside
= fd0
= p
[READ
];
682 hisside
= fd0
= p
[READ
];
688 pid
= start_command(cmd
, &nset
, fd0
, fd1
, NULL
, NULL
, NULL
, env_addon
);
690 pid
= start_command(sh
, &nset
, fd0
, fd1
, "-c", cmd
, NULL
, env_addon
);
698 if ((rv
= fdopen(myside
, mod
)) != NULL
)
699 register_file(rv
, 0, 1, pid
, FP_RAW
, NULL
, 0L);
706 Pclose(FILE *ptr
, bool_t dowait
)
716 unregister_file(ptr
);
720 sigaddset(&nset
, SIGINT
);
721 sigaddset(&nset
, SIGHUP
);
722 sigprocmask(SIG_BLOCK
, &nset
, &oset
);
723 rv
= wait_child(pid
, NULL
);
724 sigprocmask(SIG_SETMASK
, &oset
, NULL
);
735 close_all_files(void)
738 while (fp_head
!= NULL
)
740 Pclose(fp_head
->fp
, TRU1
);
747 run_command(char const *cmd
, sigset_t
*mask
, int infd
, int outfd
,
748 char const *a0
, char const *a1
, char const *a2
)
753 if ((rv
= start_command(cmd
, mask
, infd
, outfd
, a0
, a1
, a2
, NULL
)) < 0)
756 rv
= wait_command(rv
);
762 start_command(char const *cmd
, sigset_t
*mask
, int infd
, int outfd
,
763 char const *a0
, char const *a1
, char const *a2
,
764 char const **env_addon
)
769 if ((rv
= fork()) == -1) {
772 } else if (rv
== 0) {
776 if (env_addon
!= NULL
) { /* TODO env_addon; should have struct child */
777 extern char **environ
;
778 size_t ei
, ei_orig
, ai
, ai_orig
;
781 /* TODO note we don't check the POSIX limit:
782 * the total space used to store the environment and the arguments to
783 * the process is limited to {ARG_MAX} bytes */
784 for (ei
= 0; environ
[ei
] != NULL
; ++ei
)
787 for (ai
= 0; env_addon
[ai
] != NULL
; ++ai
)
790 env
= ac_alloc(sizeof(*env
) * (ei
+ ai
+1));
791 memcpy(env
, environ
, sizeof(*env
) * ei
);
793 /* Replace all those keys that yet exist */
795 char const *ee
, *kvs
;
799 kvs
= strchr(ee
, '=');
801 kl
= PTR2SIZE(kvs
- ee
);
803 for (ei
= ei_orig
; ei
-- > 0;) {
804 char const *ekvs
= strchr(env
[ei
], '=');
805 if (ekvs
!= NULL
&& kl
== PTR2SIZE(ekvs
- env
[ei
]) &&
806 !memcmp(ee
, env
[ei
], kl
)) {
807 env
[ei
] = UNCONST(ee
);
808 env_addon
[ai
] = NULL
;
814 /* And append the rest */
815 for (ei
= ei_orig
, ai
= ai_orig
; ai
-- > 0;)
816 if (env_addon
[ai
] != NULL
)
817 env
[ei
++] = UNCONST(env_addon
[ai
]);
823 i
= getrawlist(cmd
, strlen(cmd
), argv
, NELEM(argv
), 0);
825 if ((argv
[i
++] = UNCONST(a0
)) != NULL
&&
826 (argv
[i
++] = UNCONST(a1
)) != NULL
&&
827 (argv
[i
++] = UNCONST(a2
)) != NULL
)
829 prepare_child(mask
, infd
, outfd
);
830 execvp(argv
[0], argv
);
839 prepare_child(sigset_t
*nset
, int infd
, int outfd
)
845 /* All file descriptors other than 0, 1, and 2 are supposed to be cloexec */
852 for (i
= 1; i
< NSIG
; ++i
)
853 if (sigismember(nset
, i
))
854 safe_signal(i
, SIG_IGN
);
855 if (!sigismember(nset
, SIGINT
))
856 safe_signal(SIGINT
, SIG_DFL
);
860 sigprocmask(SIG_SETMASK
, &fset
, NULL
);
872 sigaddset(&nset
, SIGCHLD
);
873 sigprocmask(SIG_BLOCK
, &nset
, &oset
);
881 sigprocmask(SIG_SETMASK
, &oset
, NULL
);
886 wait_child(int pid
, int *wait_status
)
895 sigaddset(&nset
, SIGCHLD
);
896 sigprocmask(SIG_BLOCK
, &nset
, &oset
);
904 sigprocmask(SIG_SETMASK
, &oset
, NULL
);
906 if (wait_status
!= NULL
)
908 rv
= (WIFEXITED(ws
) && WEXITSTATUS(ws
) == 0);