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 - 2015 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. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 #ifndef HAVE_AMALGAMATION
76 static struct fp
*fp_head
;
77 static struct child
*_popen_child
;
79 static int scan_mode(char const *mode
, int *omode
);
80 static void register_file(FILE *fp
, int omode
, int ispipe
, int pid
,
81 int flags
, char const *realfile
, long offset
,
82 char const *save_cmd
);
83 static enum okay
_file_save(struct fp
*fpp
);
84 static int _file_load(int flags
, int infd
, int outfd
,
85 char const *load_cmd
);
86 static enum okay
unregister_file(FILE *fp
);
87 static int file_pid(FILE *fp
);
90 static void _sigchld(int signo
);
92 static int wait_command(int pid
);
93 static struct child
*_findchild(int pid
, bool_t create
);
94 static void _delchild(struct child
*cp
);
97 scan_mode(char const *mode
, int *omode
)
104 {"w", O_WRONLY
| O_CREAT
| O_TRUNC
},
105 {"wx", O_WRONLY
| O_CREAT
| O_EXCL
},
106 {"a", O_WRONLY
| O_APPEND
| O_CREAT
},
107 {"a+", O_RDWR
| O_APPEND
},
109 {"w+", O_RDWR
| O_CREAT
| O_EXCL
}
115 for (i
= 0; UICMP(z
, i
, <, NELEM(maps
)); ++i
)
116 if (!strcmp(maps
[i
].mode
, mode
)) {
117 *omode
= maps
[i
].omode
;
122 n_alert(_("Internal error: bad stdio open mode %s"), mode
);
124 *omode
= 0; /* (silence CC) */
132 register_file(FILE *fp
, int omode
, int ispipe
, int pid
, int flags
,
133 char const *realfile
, long offset
, char const *save_cmd
)
138 fpp
= smalloc(sizeof *fpp
);
145 fpp
->realfile
= (realfile
!= NULL
) ? sstrdup(realfile
) : NULL
;
146 fpp
->save_cmd
= (save_cmd
!= NULL
) ? sstrdup(save_cmd
) : NULL
;
147 fpp
->offset
= offset
;
153 _file_save(struct fp
*fpp
)
160 if (fpp
->omode
== O_RDONLY
) {
168 if (fseek(fpp
->fp
, fpp
->offset
, SEEK_SET
) == -1) {
170 n_err(_("Fatal: cannot restore file position and save %s: %s\n"),
171 fpp
->realfile
, strerror(outfd
));
176 if ((fpp
->flags
& FP_MASK
) == FP_IMAP
) {
177 rv
= imap_append(fpp
->realfile
, fpp
->fp
);
181 if ((fpp
->flags
& FP_MASK
) == FP_MAILDIR
) {
182 rv
= maildir_append(fpp
->realfile
, fpp
->fp
, fpp
->offset
);
186 /* Ensure the I/O library didn't optimize the fseek(3) away! */
187 if(lseek(infd
= fileno(fpp
->fp
), fpp
->offset
, SEEK_SET
) == -1){
189 n_err(_("Fatal: cannot restore file position and save %s: %s\n"),
190 fpp
->realfile
, strerror(outfd
));
194 outfd
= open(fpp
->realfile
,
195 ((fpp
->omode
| O_CREAT
| (fpp
->omode
& O_APPEND
? 0 : O_TRUNC
))
199 n_err(_("Fatal: cannot create %s: %s\n"),
200 fpp
->realfile
, strerror(outfd
));
205 switch (fpp
->flags
& FP_MASK
) {
207 cmd
[0] = "gzip"; cmd
[1] = "-c"; break;
209 cmd
[0] = "bzip2"; cmd
[1] = "-c"; break;
211 cmd
[0] = "xz"; cmd
[1] = "-c"; break;
213 cmd
[0] = "cat"; cmd
[1] = NULL
; break;
215 if ((cmd
[0] = ok_vlook(SHELL
)) == NULL
)
218 cmd
[2] = fpp
->save_cmd
;
220 if (run_command(cmd
[0], 0, infd
, outfd
, cmd
[1], cmd
[2], NULL
) >= 0)
230 _file_load(int flags
, int infd
, int outfd
, char const *load_cmd
)
237 switch (flags
& FP_MASK
) {
238 case FP_GZIP
: cmd
[0] = "gzip"; cmd
[1] = "-cd"; break;
239 case FP_BZIP2
: cmd
[0] = "bzip2"; cmd
[1] = "-cd"; break;
240 case FP_XZ
: cmd
[0] = "xz"; cmd
[1] = "-cd"; break;
241 default: cmd
[0] = "cat"; cmd
[1] = NULL
; break;
243 if ((cmd
[0] = ok_vlook(SHELL
)) == NULL
)
254 rv
= run_command(cmd
[0], 0, infd
, outfd
, cmd
[1], cmd
[2], NULL
);
261 unregister_file(FILE *fp
)
267 for (pp
= &fp_head
; (p
= *pp
) != NULL
; pp
= &p
->link
)
269 if ((p
->flags
& FP_MASK
) != FP_RAW
) /* TODO ;} */
272 if (p
->save_cmd
!= NULL
)
274 if (p
->realfile
!= NULL
)
279 DBGOR(n_panic
, n_alert
)(_("Invalid file pointer"));
294 for (p
= fp_head
; p
; p
= p
->link
)
309 NYD_X
; /* Signal handler */
313 pid
= waitpid(-1, &status
, WNOHANG
);
315 if (pid
== -1 && errno
== EINTR
)
320 if ((cp
= _findchild(pid
, FAL0
)) != NULL
) {
322 cp
->pid
= -1; /* XXX Was _delchild(cp);# */
332 wait_command(int pid
)
337 if (!wait_child(pid
, NULL
)) {
338 if (ok_blook(bsdcompat
) || ok_blook(bsdmsgs
))
339 n_err(_("Fatal error in process\n"));
346 static struct child
*
347 _findchild(int pid
, bool_t create
)
352 for (cpp
= &_popen_child
; *cpp
!= NULL
&& (*cpp
)->pid
!= pid
;
356 if (*cpp
== NULL
&& create
) {
357 *cpp
= smalloc(sizeof **cpp
);
359 (*cpp
)->done
= (*cpp
)->free
= 0;
367 _delchild(struct child
*cp
)
379 if (*(cpp
= &(*cpp
)->link
) == NULL
) {
380 DBG( n_err("! popen.c:_delchild(): implementation error\n"); )
388 command_manager_start(void)
390 struct sigaction nact
, oact
;
393 nact
.sa_handler
= &_sigchld
;
394 sigemptyset(&nact
.sa_mask
);
403 if (sigaction(SIGCHLD
, &nact
, &oact
) != 0)
404 n_panic(_("Cannot install signal handler for child process management"));
409 safe_fopen(char const *file
, char const *oflags
, int *xflags
)
413 NYD2_ENTER
; /* (only for Fopen() and once in lex.c) */
415 if (scan_mode(oflags
, &osflags
) < 0)
417 osflags
|= _O_CLOEXEC
;
421 if ((fd
= open(file
, osflags
, 0666)) == -1)
425 fp
= fdopen(fd
, oflags
);
432 Fopen(char const *file
, char const *oflags
)
438 if ((fp
= safe_fopen(file
, oflags
, &osflags
)) != NULL
)
439 register_file(fp
, osflags
, 0, 0, FP_RAW
, NULL
, 0L, NULL
);
445 Fdopen(int fd
, char const *oflags
, bool_t nocloexec
)
451 scan_mode(oflags
, &osflags
);
453 osflags
|= _O_CLOEXEC
; /* Ensured to be set by caller as documented! */
455 if ((fp
= fdopen(fd
, oflags
)) != NULL
)
456 register_file(fp
, osflags
, 0, 0, FP_RAW
, NULL
, 0L, NULL
);
467 if (unregister_file(fp
) == OKAY
)
472 return (i
== 3 ? 0 : EOF
);
476 Zopen(char const *file
, char const *oflags
) /* FIXME MESS! */
479 char const *cload
= NULL
, *csave
= NULL
;
480 int flags
, osflags
, mode
, infd
;
486 if (scan_mode(oflags
, &osflags
) < 0)
490 rof
= OF_RDWR
| OF_UNLINK
;
491 if (osflags
& O_APPEND
)
493 mode
= (osflags
== O_RDONLY
) ? R_OK
: R_OK
| W_OK
;
495 if ((osflags
& O_APPEND
) && ((p
= which_protocol(file
)) == PROTO_IMAP
||
496 p
== PROTO_MAILDIR
)) {
497 flags
|= (p
== PROTO_IMAP
) ? FP_IMAP
: FP_MAILDIR
;
498 osflags
= O_RDWR
| O_APPEND
| O_CREAT
;
503 if ((ext
= strrchr(file
, '.')) != NULL
) {
504 if (!asccasecmp(ext
, ".gz"))
506 else if (!asccasecmp(ext
, ".xz")) {
508 osflags
&= ~O_APPEND
;
510 } else if (!asccasecmp(ext
, ".bz2")) {
512 osflags
&= ~O_APPEND
;
516 #define _X1 "file-hook-load-"
518 #define _X2 "file-hook-save-"
519 size_t l
= strlen(++ext
);
520 char *vbuf
= ac_alloc(l
+ MAX(sizeof(_X1
), sizeof(_X2
)));
522 memcpy(vbuf
, _X1
, sizeof(_X1
) -1);
523 memcpy(vbuf
+ sizeof(_X1
) -1, ext
, l
);
524 vbuf
[sizeof(_X1
) -1 + l
] = '\0';
525 cload
= vok_vlook(vbuf
);
526 memcpy(vbuf
, _X2
, sizeof(_X2
) -1);
527 memcpy(vbuf
+ sizeof(_X2
) -1, ext
, l
);
528 vbuf
[sizeof(_X2
) -1 + l
] = '\0';
529 csave
= vok_vlook(vbuf
);
534 if ((csave
!= NULL
) && (cload
!= NULL
)) {
536 osflags
&= ~O_APPEND
;
538 } else if ((csave
!= NULL
) | (cload
!= NULL
)) {
539 n_alert(_("Only one of *mailbox-(load|save)-%s* is set! "
540 "Treating as plain text!"), ext
);
548 rv
= Fopen(file
, oflags
);
552 if ((infd
= open(file
, (mode
& W_OK
) ? O_RDWR
: O_RDONLY
)) == -1 &&
553 (!(osflags
& O_CREAT
) || errno
!= ENOENT
))
557 /* Note rv is not yet register_file()d, fclose() it in error path! */
558 if ((rv
= Ftmp(NULL
, "zopen", rof
, 0600)) == NULL
) {
559 n_perr(_("tmpfile"), 0);
563 if (flags
& (FP_IMAP
| FP_MAILDIR
))
565 else if (infd
>= 0) {
566 if (_file_load(flags
, infd
, fileno(rv
), cload
) < 0) {
576 if ((infd
= creat(file
, 0666)) == -1) {
587 if (!(osflags
& O_APPEND
))
589 if ((offset
= ftell(rv
)) == -1) {
595 register_file(rv
, osflags
, 0, 0, flags
, file
, offset
, csave
);
602 Ftmp(char **fn
, char const *prefix
, enum oflags oflags
, int mode
)
605 size_t maxname
, tries
;
610 assert((oflags
& OF_WRONLY
) || (oflags
& OF_RDWR
));
611 assert(!(oflags
& OF_RDONLY
));
618 if ((pc
= pathconf(tempdir
, _PC_NAME_MAX
)) != -1)
619 maxname
= (size_t)pc
;
624 cp
= smalloc(strlen(tempdir
) + 1 + maxname
+1);
625 cp
= sstpcpy(cp
, tempdir
);
628 osoflags
= O_CREAT
| O_EXCL
| _O_CLOEXEC
;
629 osoflags
|= (oflags
& OF_WRONLY
) ? O_WRONLY
: O_RDWR
;
630 if (oflags
& OF_APPEND
)
631 osoflags
|= O_APPEND
;
633 for (tries
= 0;; ++tries
) {
637 x
= sstpcpy(cp
, UAGENT
);
639 if (*prefix
!= '\0') {
640 x
= sstpcpy(x
, prefix
);
644 /* Calculate length of a random string addon */
645 i
= PTR2SIZE(x
- cp
);
646 if (i
>= maxname
>> 1) {
651 /* But don't be too fatalistic */
652 if (i
> 8 && tries
< FTMP_OPEN_TRIES
/ 2)
654 memcpy(x
, getrandstring(i
), i
+1);
657 if ((fd
= open(cp_base
, osoflags
, mode
)) != -1) {
661 if (tries
>= FTMP_OPEN_TRIES
) {
668 if (oflags
& OF_REGISTER
)
669 fp
= Fdopen(fd
, (oflags
& OF_RDWR
? "w+" : "w"), FAL0
);
671 fp
= fdopen(fd
, (oflags
& OF_RDWR
? "w+" : "w"));
673 if (fp
== NULL
|| (oflags
& OF_UNLINK
)) {
684 if (fp
== NULL
|| !(oflags
& OF_HOLDSIGS
))
691 if ((cp
= cp_base
) != NULL
)
697 Ftmp_release(char **fn
)
726 pipe_cloexec(int fd
[2])
732 if (pipe2(fd
, O_CLOEXEC
) == -1)
737 (void)fcntl(fd
[0], F_SETFD
, FD_CLOEXEC
);
738 (void)fcntl(fd
[1], F_SETFD
, FD_CLOEXEC
);
747 Popen(char const *cmd
, char const *mode
, char const *sh
,
748 char const **env_addon
, int newfd1
)
750 int p
[2], myside
, hisside
, fd0
, fd1
, pid
;
751 char mod
[2] = {'0', '\0'};
756 /* First clean up child structures */
758 struct child
**cpp
, *cp
;
761 sigprocmask(SIG_BLOCK
, &nset
, &oset
);
763 for (cpp
= &_popen_child
; *cpp
!= NULL
;) {
764 if ((*cpp
)->pid
== -1) {
772 sigprocmask(SIG_SETMASK
, &oset
, NULL
);
775 if (!pipe_cloexec(p
))
781 hisside
= fd1
= p
[WRITE
];
783 } else if (*mode
== 'W') {
785 hisside
= fd0
= p
[READ
];
790 hisside
= fd0
= p
[READ
];
797 if (cmd
== (char*)-1) {
798 if ((pid
= fork_child()) == -1)
799 n_perr(_("fork"), 0);
801 union {char const *ccp
; int (*ptf
)(void); int es
;} u
;
802 prepare_child(&nset
, fd0
, fd1
);
809 } else if (sh
== NULL
) {
810 pid
= start_command(cmd
, &nset
, fd0
, fd1
, NULL
, NULL
, NULL
, env_addon
);
812 pid
= start_command(sh
, &nset
, fd0
, fd1
, "-c", cmd
, NULL
, env_addon
);
820 if ((rv
= fdopen(myside
, mod
)) != NULL
)
821 register_file(rv
, 0, 1, pid
, FP_RAW
, NULL
, 0L, NULL
);
830 Pclose(FILE *ptr
, bool_t dowait
)
840 unregister_file(ptr
);
844 sigaddset(&nset
, SIGINT
);
845 sigaddset(&nset
, SIGHUP
);
846 sigprocmask(SIG_BLOCK
, &nset
, &oset
);
847 rv
= wait_child(pid
, NULL
);
848 sigprocmask(SIG_SETMASK
, &oset
, NULL
);
859 close_all_files(void)
862 while (fp_head
!= NULL
)
864 Pclose(fp_head
->fp
, TRU1
);
877 cp
= _findchild(0, TRU1
);
879 if ((cp
->pid
= pid
= fork()) == -1) {
881 n_perr(_("fork"), 0);
888 run_command(char const *cmd
, sigset_t
*mask
, int infd
, int outfd
,
889 char const *a0
, char const *a1
, char const *a2
)
894 if ((rv
= start_command(cmd
, mask
, infd
, outfd
, a0
, a1
, a2
, NULL
)) < 0)
897 rv
= wait_command(rv
);
903 start_command(char const *cmd
, sigset_t
*mask
, int infd
, int outfd
,
904 char const *a0
, char const *a1
, char const *a2
,
905 char const **env_addon
)
910 if ((rv
= fork_child()) == -1) {
911 n_perr(_("fork"), 0);
913 } else if (rv
== 0) {
917 if (env_addon
!= NULL
) { /* TODO env_addon; should have struct child */
918 extern char **environ
;
919 size_t ei
, ei_orig
, ai
, ai_orig
;
922 /* TODO note we don't check the POSIX limit:
923 * the total space used to store the environment and the arguments to
924 * the process is limited to {ARG_MAX} bytes */
925 for (ei
= 0; environ
[ei
] != NULL
; ++ei
)
928 for (ai
= 0; env_addon
[ai
] != NULL
; ++ai
)
931 env
= ac_alloc(sizeof(*env
) * (ei
+ ai
+1));
932 memcpy(env
, environ
, sizeof(*env
) * ei
);
934 /* Replace all those keys that yet exist */
936 char const *ee
, *kvs
;
940 kvs
= strchr(ee
, '=');
942 kl
= PTR2SIZE(kvs
- ee
);
944 for (ei
= ei_orig
; ei
-- > 0;) {
945 char const *ekvs
= strchr(env
[ei
], '=');
946 if (ekvs
!= NULL
&& kl
== PTR2SIZE(ekvs
- env
[ei
]) &&
947 !memcmp(ee
, env
[ei
], kl
)) {
948 env
[ei
] = UNCONST(ee
);
949 env_addon
[ai
] = NULL
;
955 /* And append the rest */
956 for (ei
= ei_orig
, ai
= ai_orig
; ai
-- > 0;)
957 if (env_addon
[ai
] != NULL
)
958 env
[ei
++] = UNCONST(env_addon
[ai
]);
964 i
= getrawlist(cmd
, strlen(cmd
), argv
, NELEM(argv
), 0);
966 if ((argv
[i
++] = UNCONST(a0
)) != NULL
&&
967 (argv
[i
++] = UNCONST(a1
)) != NULL
&&
968 (argv
[i
++] = UNCONST(a2
)) != NULL
)
970 prepare_child(mask
, infd
, outfd
);
971 execvp(argv
[0], argv
);
980 prepare_child(sigset_t
*nset
, int infd
, int outfd
)
986 /* All file descriptors other than 0, 1, and 2 are supposed to be cloexec */
988 dup2(infd
, STDIN_FILENO
);
990 dup2(outfd
, STDOUT_FILENO
);
993 for (i
= 1; i
< NSIG
; ++i
)
994 if (sigismember(nset
, i
))
995 safe_signal(i
, SIG_IGN
);
996 if (!sigismember(nset
, SIGINT
))
997 safe_signal(SIGINT
, SIG_DFL
);
1001 sigprocmask(SIG_SETMASK
, &fset
, NULL
);
1008 sigset_t nset
, oset
;
1013 sigaddset(&nset
, SIGCHLD
);
1014 sigprocmask(SIG_BLOCK
, &nset
, &oset
);
1016 if ((cp
= _findchild(pid
, FAL0
)) != NULL
) {
1023 sigprocmask(SIG_SETMASK
, &oset
, NULL
);
1028 wait_child(int pid
, int *wait_status
)
1030 sigset_t nset
, oset
;
1037 sigaddset(&nset
, SIGCHLD
);
1038 sigprocmask(SIG_BLOCK
, &nset
, &oset
);
1040 cp
= _findchild(pid
, FAL0
);
1049 sigprocmask(SIG_SETMASK
, &oset
, NULL
);
1051 if (wait_status
!= NULL
)
1053 rv
= (WIFEXITED(ws
) && WEXITSTATUS(ws
) == 0);