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 - 2017 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
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
52 struct termios
*fp_tios
;
65 /* TODO FP_UNLINK: should be in a separated process so that unlinking
66 * TODO the temporary "garbage" is "safe"(r than it is like that) */
80 static struct fp
*fp_head
;
81 static struct child
*_popen_child
;
83 /* TODO Rather temporary: deal with job control with FD_PASS */
84 static struct termios a_popen_tios
;
85 static sighandler_type a_popen_otstp
, a_popen_ottin
, a_popen_ottou
;
86 static volatile int a_popen_hadsig
;
88 static int scan_mode(char const *mode
, int *omode
);
89 static void register_file(FILE *fp
, int omode
, int pid
,
90 int flags
, char const *realfile
, long offset
,
91 char const *save_cmd
, struct termios
*tiosp
);
92 static enum okay
_file_save(struct fp
*fpp
);
93 static int _file_load(int flags
, int infd
, int outfd
,
94 char const *load_cmd
);
95 static enum okay
unregister_file(FILE *fp
, struct termios
**tiosp
);
96 static int file_pid(FILE *fp
);
98 /* TODO Rather temporary: deal with job control with FD_PASS */
99 static void a_popen_jobsigs_up(void);
100 static void a_popen_jobsigs_down(void);
101 static void a_popen_jobsig(int sig
);
104 static void _sigchld(int signo
);
106 static struct child
*_findchild(int pid
, bool_t create
);
107 static void _delchild(struct child
*cp
);
110 scan_mode(char const *mode
, int *omode
)
117 {"w", O_WRONLY
| O_CREAT
| n_O_NOFOLLOW
| O_TRUNC
},
118 {"wx", O_WRONLY
| O_CREAT
| O_EXCL
},
119 {"a", O_WRONLY
| O_APPEND
| O_CREAT
| n_O_NOFOLLOW
},
120 {"a+", O_RDWR
| O_APPEND
},
122 {"w+", O_RDWR
| O_CREAT
| O_EXCL
}
128 for (i
= 0; UICMP(z
, i
, <, n_NELEM(maps
)); ++i
)
129 if (!strcmp(maps
[i
].mode
, mode
)) {
130 *omode
= maps
[i
].omode
;
135 n_alert(_("Internal error: bad stdio open mode %s"), mode
);
136 n_err_no
= n_ERR_INVAL
;
137 *omode
= 0; /* (silence CC) */
145 register_file(FILE *fp
, int omode
, int pid
, int flags
,
146 char const *realfile
, long offset
, char const *save_cmd
,
147 struct termios
*tiosp
)
152 assert(!(flags
& FP_UNLINK
) || realfile
!= NULL
);
153 assert(!(flags
& FP_TERMIOS
) || tiosp
!= NULL
);
155 fpp
= smalloc(sizeof *fpp
);
161 fpp
->realfile
= (realfile
!= NULL
) ? sstrdup(realfile
) : NULL
;
162 fpp
->save_cmd
= (save_cmd
!= NULL
) ? sstrdup(save_cmd
) : NULL
;
163 fpp
->fp_tios
= tiosp
;
164 fpp
->offset
= offset
;
170 _file_save(struct fp
*fpp
)
177 if (fpp
->omode
== O_RDONLY
) {
186 /* Ensure the I/O library doesn't optimize the fseek(3) away! */
187 if(!n_real_seek(fpp
->fp
, fpp
->offset
, SEEK_SET
)){
189 n_err(_("Fatal: cannot restore file position and save %s: %s\n"),
190 n_shexp_quote_cp(fpp
->realfile
, FAL0
), n_err_to_doc(outfd
));
194 if ((fpp
->flags
& FP_MASK
) == FP_MAILDIR
) {
195 rv
= maildir_append(fpp
->realfile
, fpp
->fp
, fpp
->offset
);
199 outfd
= open(fpp
->realfile
,
200 ((fpp
->omode
| O_CREAT
| (fpp
->omode
& O_APPEND
? 0 : O_TRUNC
) |
201 n_O_NOFOLLOW
) & ~O_EXCL
), 0666);
204 n_err(_("Fatal: cannot create %s: %s\n"),
205 n_shexp_quote_cp(fpp
->realfile
, FAL0
), n_err_to_doc(outfd
));
210 switch (fpp
->flags
& FP_MASK
) {
212 cmd
[0] = "gzip"; cmd
[1] = "-c"; break;
214 cmd
[0] = "bzip2"; cmd
[1] = "-c"; break;
216 cmd
[0] = "xz"; cmd
[1] = "-c"; break;
218 cmd
[0] = "cat"; cmd
[1] = NULL
; break;
220 cmd
[0] = ok_vlook(SHELL
);
222 cmd
[2] = fpp
->save_cmd
;
224 if (run_command(cmd
[0], 0, fileno(fpp
->fp
), outfd
,
225 cmd
[1], cmd
[2], NULL
, NULL
) >= 0)
235 _file_load(int flags
, int infd
, int outfd
, char const *load_cmd
)
242 switch (flags
& FP_MASK
) {
243 case FP_GZIP
: cmd
[0] = "gzip"; cmd
[1] = "-cd"; break;
244 case FP_BZIP2
: cmd
[0] = "bzip2"; cmd
[1] = "-cd"; break;
245 case FP_XZ
: cmd
[0] = "xz"; cmd
[1] = "-cd"; break;
246 default: cmd
[0] = "cat"; cmd
[1] = NULL
; break;
248 cmd
[0] = ok_vlook(SHELL
);
257 rv
= run_command(cmd
[0], 0, infd
, outfd
, cmd
[1], cmd
[2], NULL
, NULL
);
264 unregister_file(FILE *fp
, struct termios
**tiosp
)
273 for (pp
= &fp_head
; (p
= *pp
) != NULL
; pp
= &p
->link
)
275 switch (p
->flags
& FP_MASK
) {
283 if ((p
->flags
& FP_UNLINK
) && unlink(p
->realfile
))
287 if (p
->save_cmd
!= NULL
)
289 if (p
->realfile
!= NULL
)
291 if (p
->flags
& FP_TERMIOS
) {
300 DBGOR(n_panic
, n_alert
)(_("Invalid file pointer"));
315 for (p
= fp_head
; p
; p
= p
->link
)
325 a_popen_jobsigs_up(void){
331 sigprocmask(SIG_BLOCK
, &nset
, &oset
);
332 a_popen_otstp
= safe_signal(SIGTSTP
, &a_popen_jobsig
);
333 a_popen_ottin
= safe_signal(SIGTTIN
, &a_popen_jobsig
);
334 a_popen_ottou
= safe_signal(SIGTTOU
, &a_popen_jobsig
);
336 /* This assumes oset contains nothing but SIGCHLD, so to say */
337 sigdelset(&oset
, SIGTSTP
);
338 sigdelset(&oset
, SIGTTIN
);
339 sigdelset(&oset
, SIGTTOU
);
340 sigprocmask(SIG_SETMASK
, &oset
, NULL
);
345 a_popen_jobsigs_down(void){
351 sigprocmask(SIG_BLOCK
, &nset
, &oset
);
352 safe_signal(SIGTSTP
, a_popen_otstp
);
353 safe_signal(SIGTTIN
, a_popen_ottin
);
354 safe_signal(SIGTTOU
, a_popen_ottou
);
356 sigaddset(&oset
, SIGTSTP
);
357 sigaddset(&oset
, SIGTTIN
);
358 sigaddset(&oset
, SIGTTOU
);
359 sigprocmask(SIG_SETMASK
, &oset
, NULL
);
364 a_popen_jobsig(int sig
){
365 sighandler_type oldact
;
368 NYD_X
; /* Signal handler */
370 hadsig
= (a_popen_hadsig
!= 0);
373 n_TERMCAP_SUSPEND(TRU1
);
375 oldact
= safe_signal(sig
, SIG_DFL
);
378 sigaddset(&nset
, sig
);
379 sigprocmask(SIG_UNBLOCK
, &nset
, NULL
);
381 sigprocmask(SIG_BLOCK
, &nset
, NULL
);
383 safe_signal(sig
, oldact
);
392 NYD_X
; /* Signal handler */
396 pid
= waitpid(-1, &status
, WNOHANG
);
398 if (pid
== -1 && n_err_no
== n_ERR_INTR
)
403 if ((cp
= _findchild(pid
, FAL0
)) != NULL
) {
405 cp
->pid
= -1; /* XXX Was _delchild(cp);# */
414 static struct child
*
415 _findchild(int pid
, bool_t create
)
420 for (cpp
= &_popen_child
; *cpp
!= NULL
&& (*cpp
)->pid
!= pid
;
424 if (*cpp
== NULL
&& create
) {
425 *cpp
= smalloc(sizeof **cpp
);
427 (*cpp
)->done
= (*cpp
)->free
= 0;
435 _delchild(struct child
*cp
)
447 if (*(cpp
= &(*cpp
)->link
) == NULL
) {
448 DBG( n_err("! popen.c:_delchild(): implementation error\n"); )
456 command_manager_start(void)
458 struct sigaction nact
, oact
;
461 nact
.sa_handler
= &_sigchld
;
462 sigemptyset(&nact
.sa_mask
);
463 nact
.sa_flags
= SA_RESTART
468 if (sigaction(SIGCHLD
, &nact
, &oact
) != 0)
469 n_panic(_("Cannot install signal handler for child process management"));
474 safe_fopen(char const *file
, char const *oflags
, int *xflags
)
478 NYD2_ENTER
; /* (only for Fopen() and once in lex.c) */
480 if (scan_mode(oflags
, &osflags
) < 0)
482 osflags
|= _O_CLOEXEC
;
486 if ((fd
= open(file
, osflags
, 0666)) == -1)
490 fp
= fdopen(fd
, oflags
);
497 Fopen(char const *file
, char const *oflags
)
503 if ((fp
= safe_fopen(file
, oflags
, &osflags
)) != NULL
)
504 register_file(fp
, osflags
, 0, FP_RAW
, NULL
, 0L, NULL
, NULL
);
510 Fdopen(int fd
, char const *oflags
, bool_t nocloexec
)
516 scan_mode(oflags
, &osflags
);
518 osflags
|= _O_CLOEXEC
; /* Ensured to be set by caller as documented! */
520 if ((fp
= fdopen(fd
, oflags
)) != NULL
)
521 register_file(fp
, osflags
, 0, FP_RAW
, NULL
, 0L, NULL
, NULL
);
532 if (unregister_file(fp
, NULL
) == OKAY
)
537 return (i
== 3 ? 0 : EOF
);
541 Zopen(char const *file
, char const *oflags
) /* FIXME MESS! */
544 char const *cload
= NULL
, *csave
= NULL
;
545 int flags
, osflags
, mode
, infd
;
551 if (scan_mode(oflags
, &osflags
) < 0)
555 rof
= OF_RDWR
| OF_UNLINK
;
556 if (osflags
& O_APPEND
)
558 mode
= (osflags
== O_RDONLY
) ? R_OK
: R_OK
| W_OK
;
560 if ((osflags
& O_APPEND
) && ((p
= which_protocol(file
)) == PROTO_MAILDIR
)) {
562 osflags
= O_RDWR
| O_APPEND
| O_CREAT
| n_O_NOFOLLOW
;
567 if ((ext
= strrchr(file
, '.')) != NULL
) {
568 if (!asccasecmp(ext
, ".gz"))
570 else if (!asccasecmp(ext
, ".xz")) {
572 osflags
&= ~O_APPEND
;
574 } else if (!asccasecmp(ext
, ".bz2")) {
576 osflags
&= ~O_APPEND
;
580 #define _X1 "file-hook-load-"
582 #define _X2 "file-hook-save-"
583 size_t l
= strlen(++ext
);
584 char *vbuf
= ac_alloc(l
+ n_MAX(sizeof(_X1
), sizeof(_X2
)));
586 memcpy(vbuf
, _X1
, sizeof(_X1
) -1);
587 memcpy(vbuf
+ sizeof(_X1
) -1, ext
, l
);
588 vbuf
[sizeof(_X1
) -1 + l
] = '\0';
589 cload
= n_var_vlook(vbuf
, FAL0
);
590 memcpy(vbuf
, _X2
, sizeof(_X2
) -1);
591 memcpy(vbuf
+ sizeof(_X2
) -1, ext
, l
);
592 vbuf
[sizeof(_X2
) -1 + l
] = '\0';
593 csave
= n_var_vlook(vbuf
, FAL0
);
598 if ((csave
!= NULL
) && (cload
!= NULL
)) {
600 osflags
&= ~O_APPEND
;
602 } else if ((csave
!= NULL
) | (cload
!= NULL
)) {
603 n_alert(_("Only one of *mailbox-(load|save)-%s* is set! "
604 "Treating as plain text!"), ext
);
612 rv
= Fopen(file
, oflags
);
616 if ((infd
= open(file
, (mode
& W_OK
) ? O_RDWR
: O_RDONLY
)) == -1 &&
617 (!(osflags
& O_CREAT
) || n_err_no
!= n_ERR_NOENT
))
621 /* Note rv is not yet register_file()d, fclose() it in error path! */
622 if ((rv
= Ftmp(NULL
, "zopen", rof
)) == NULL
) {
623 n_perr(_("tmpfile"), 0);
627 if (flags
& FP_MAILDIR
)
629 else if (infd
>= 0) {
630 if (_file_load(flags
, infd
, fileno(rv
), cload
) < 0) {
640 if ((infd
= creat(file
, 0666)) == -1) {
651 if (!(osflags
& O_APPEND
))
653 if ((offset
= ftell(rv
)) == -1) {
659 register_file(rv
, osflags
, 0, flags
, file
, offset
, csave
, NULL
);
666 Ftmp(char **fn
, char const *namehint
, enum oflags oflags
)
668 /* The 8 is arbitrary but leaves room for a six character suffix (the
669 * POSIX minimum path length is 14, though we don't check that XXX).
670 * 8 should be more than sufficient given that we use base64url encoding
671 * for our random string */
672 enum {_RANDCHARS
= 8u};
675 size_t maxname
, xlen
, i
;
682 assert(namehint
!= NULL
);
683 assert((oflags
& OF_WRONLY
) || (oflags
& OF_RDWR
));
684 assert(!(oflags
& OF_RDONLY
));
685 assert(!(oflags
& OF_REGISTER_UNLINK
) || (oflags
& OF_REGISTER
));
690 tmpdir
= ok_vlook(TMPDIR
);
695 if ((pc
= pathconf(tmpdir
, _PC_NAME_MAX
)) != -1)
696 maxname
= (size_t)pc
;
700 if ((oflags
& OF_SUFFIX
) && *namehint
!= '\0') {
701 if ((xlen
= strlen(namehint
)) > maxname
- _RANDCHARS
) {
702 n_err_no
= n_ERR_NAMETOOLONG
;
708 /* Prepare the template string once, then iterate over the random range */
710 cp
= smalloc(strlen(tmpdir
) + 1 + maxname
+1);
711 cp
= sstpcpy(cp
, tmpdir
);
714 char *x
= sstpcpy(cp
, VAL_UAGENT
);
716 if (!(oflags
& OF_SUFFIX
))
717 x
= sstpcpy(x
, namehint
);
719 i
= PTR2SIZE(x
- cp
);
720 if (i
> maxname
- xlen
- _RANDCHARS
) {
721 size_t j
= maxname
- xlen
- _RANDCHARS
;
726 if ((oflags
& OF_SUFFIX
) && xlen
> 0)
727 memcpy(x
+ _RANDCHARS
, namehint
, xlen
);
729 x
[xlen
+ _RANDCHARS
] = '\0';
733 osoflags
= O_CREAT
| O_EXCL
| _O_CLOEXEC
;
734 osoflags
|= (oflags
& OF_WRONLY
) ? O_WRONLY
: O_RDWR
;
735 if (oflags
& OF_APPEND
)
736 osoflags
|= O_APPEND
;
739 memcpy(cp
, getrandstring(_RANDCHARS
), _RANDCHARS
);
744 if ((fd
= open(cp_base
, osoflags
, 0600)) != -1) {
748 if (i
>= FTMP_OPEN_TRIES
) {
756 if (oflags
& OF_REGISTER
) {
757 char const *osflags
= (oflags
& OF_RDWR
? "w+" : "w");
760 scan_mode(osflags
, &osflagbits
); /* TODO osoflags&xy ?!!? */
761 if ((fp
= fdopen(fd
, osflags
)) != NULL
)
762 register_file(fp
, osflagbits
| _O_CLOEXEC
, 0,
763 (FP_RAW
| (oflags
& OF_REGISTER_UNLINK
? FP_UNLINK
: 0)),
764 cp_base
, 0L, NULL
, NULL
);
766 fp
= fdopen(fd
, (oflags
& OF_RDWR
? "w+" : "w"));
768 if (fp
== NULL
|| (oflags
& OF_UNLINK
)) {
779 if (relesigs
&& (fp
== NULL
|| !(oflags
& OF_HOLDSIGS
)))
786 if ((cp
= cp_base
) != NULL
)
792 Ftmp_release(char **fn
)
808 Ftmp_free(char **fn
) /* TODO DROP: OF_REGISTER_FREEPATH! */
821 pipe_cloexec(int fd
[2])
827 if (pipe2(fd
, O_CLOEXEC
) == -1)
832 (void)fcntl(fd
[0], F_SETFD
, FD_CLOEXEC
);
833 (void)fcntl(fd
[1], F_SETFD
, FD_CLOEXEC
);
842 Popen(char const *cmd
, char const *mode
, char const *sh
,
843 char const **env_addon
, int newfd1
)
845 struct termios
*tiosp
;
846 int p
[2], myside
, hisside
, fd0
, fd1
, pid
;
847 char mod
[2] = {'0', '\0'};
852 /* First clean up child structures */
854 struct child
**cpp
, *cp
;
857 sigprocmask(SIG_BLOCK
, &nset
, &oset
);
859 for (cpp
= &_popen_child
; *cpp
!= NULL
;) {
860 if ((*cpp
)->pid
== -1) {
868 sigprocmask(SIG_SETMASK
, &oset
, NULL
);
871 if (!pipe_cloexec(p
))
876 fd0
= COMMAND_FD_PASS
;
877 hisside
= fd1
= p
[WRITE
];
879 } else if (*mode
== 'W') {
881 hisside
= fd0
= p
[READ
];
886 hisside
= fd0
= p
[READ
];
887 fd1
= COMMAND_FD_PASS
;
891 /* In interactive mode both STDIN and STDOUT point to the terminal. If we
892 * pass through the TTY restore terminal attributes after pipe returns.
893 * XXX It shouldn't matter which FD we actually use in this case */
894 if ((n_psonce
& n_PSO_INTERACTIVE
) && (fd0
== COMMAND_FD_PASS
||
895 fd1
== COMMAND_FD_PASS
)) {
896 tiosp
= smalloc(sizeof *tiosp
);
897 tcgetattr(STDIN_FILENO
, tiosp
);
898 n_TERMCAP_SUSPEND(TRU1
);
904 if (cmd
== (char*)-1) {
905 if ((pid
= fork_child()) == -1)
906 n_perr(_("fork"), 0);
908 union {char const *ccp
; int (*ptf
)(void); int es
;} u
;
909 prepare_child(&nset
, fd0
, fd1
);
916 } else if (sh
== NULL
) {
917 pid
= start_command(cmd
, &nset
, fd0
, fd1
, NULL
, NULL
, NULL
, env_addon
);
919 pid
= start_command(sh
, &nset
, fd0
, fd1
, "-c", cmd
, NULL
, env_addon
);
927 if ((rv
= fdopen(myside
, mod
)) != NULL
)
928 register_file(rv
, 0, pid
,
929 (tiosp
== NULL
? FP_PIPE
: FP_PIPE
| FP_TERMIOS
),
930 NULL
, 0L, NULL
, tiosp
);
939 Pclose(FILE *ptr
, bool_t dowait
)
941 struct termios
*tiosp
;
951 unregister_file(ptr
, &tiosp
);
956 sigaddset(&nset
, SIGINT
);
957 sigaddset(&nset
, SIGHUP
);
958 sigprocmask(SIG_BLOCK
, &nset
, &oset
);
959 rv
= wait_child(pid
, NULL
);
961 n_TERMCAP_RESUME(TRU1
);
962 tcsetattr(STDIN_FILENO
, TCSAFLUSH
, tiosp
);
964 sigprocmask(SIG_SETMASK
, &oset
, NULL
);
979 char const *env_add
[2], *pager
;
983 assert(n_psonce
& n_PSO_INTERACTIVE
);
985 pager
= n_pager_get(env_add
+ 0);
988 if ((rv
= Popen(pager
, "w", NULL
, env_add
, COMMAND_FD_PASS
)) == NULL
)
995 n_pager_close(FILE *fp
)
1001 sh
= safe_signal(SIGPIPE
, SIG_IGN
);
1002 rv
= Pclose(fp
, TRU1
);
1003 safe_signal(SIGPIPE
, sh
);
1009 close_all_files(void)
1012 while (fp_head
!= NULL
)
1013 if ((fp_head
->flags
& FP_MASK
) == FP_PIPE
)
1014 Pclose(fp_head
->fp
, TRU1
);
1016 Fclose(fp_head
->fp
);
1027 cp
= _findchild(0, TRU1
);
1029 if ((cp
->pid
= pid
= fork()) == -1) {
1031 n_perr(_("fork"), 0);
1038 run_command(char const *cmd
, sigset_t
*mask
, int infd
, int outfd
,
1039 char const *a0
, char const *a1
, char const *a2
, char const **env_addon
)
1041 sigset_t nset
, oset
;
1042 sighandler_type soldint
;
1047 /* TODO Of course this is a joke given that during a "p*" the PAGER may
1048 * TODO be up and running while we play around like this... but i guess
1049 * TODO this can't be helped at all unless we perform complete and true
1050 * TODO process group separation and ensure we don't deadlock us out
1051 * TODO via TTY jobcontrol signal storms (could this really happen?).
1052 * TODO Or have a builtin pager. Or query any necessity BEFORE we start
1053 * TODO any action, and shall we find we need to run programs dump it
1054 * TODO all into a temporary file which is then passed through to the
1055 * TODO PAGER. Ugh. That still won't help for "needsterminal" anyway */
1056 if ((tio_set
= ((n_psonce
& n_PSO_INTERACTIVE
) &&
1057 (infd
== COMMAND_FD_PASS
|| outfd
== COMMAND_FD_PASS
)))) {
1058 /* TODO Simply ignore SIGINT then, it surely will be ment for the program
1059 * TODO which takes the terminal */
1060 soldint
= safe_signal(SIGINT
, SIG_IGN
);
1061 tcgetattr((n_psonce
& n_PSO_TTYIN
? STDIN_FILENO
: STDOUT_FILENO
),
1063 n_TERMCAP_SUSPEND(FAL0
);
1065 sigdelset(&nset
, SIGCHLD
);
1066 sigdelset(&nset
, SIGINT
);
1067 /* sigdelset(&nset, SIGPIPE); TODO would need a handler */
1068 sigprocmask(SIG_BLOCK
, &nset
, &oset
);
1070 a_popen_jobsigs_up();
1073 if ((rv
= start_command(cmd
, mask
, infd
, outfd
, a0
, a1
, a2
, env_addon
)) < 0)
1076 if (wait_child(rv
, NULL
))
1079 if (ok_blook(bsdcompat
) || ok_blook(bsdmsgs
))
1080 n_err(_("Fatal error in process\n"));
1086 a_popen_jobsigs_down();
1087 tio_set
= ((n_psonce
& n_PSO_TTYIN
) != 0);
1088 n_TERMCAP_RESUME(a_popen_hadsig
? TRU1
: FAL0
);
1089 tcsetattr((tio_set
? STDIN_FILENO
: STDOUT_FILENO
),
1090 (tio_set
? TCSAFLUSH
: TCSADRAIN
), &a_popen_tios
);
1091 if(soldint
!= SIG_IGN
)
1092 safe_signal(SIGINT
, soldint
);
1093 sigprocmask(SIG_SETMASK
, &oset
, NULL
);
1100 start_command(char const *cmd
, sigset_t
*mask
, int infd
, int outfd
,
1101 char const *a0
, char const *a1
, char const *a2
,
1102 char const **env_addon
)
1107 if ((rv
= fork_child()) == -1) {
1108 n_perr(_("fork"), 0);
1110 } else if (rv
== 0) {
1114 if (env_addon
!= NULL
) { /* TODO env_addon; should have struct child */
1115 extern char **environ
;
1116 size_t ei
, ei_orig
, ai
, ai_orig
;
1119 /* TODO note we don't check the POSIX limit:
1120 * the total space used to store the environment and the arguments to
1121 * the process is limited to {ARG_MAX} bytes */
1122 for (ei
= 0; environ
[ei
] != NULL
; ++ei
)
1125 for (ai
= 0; env_addon
[ai
] != NULL
; ++ai
)
1128 env
= ac_alloc(sizeof(*env
) * (ei
+ ai
+1));
1129 memcpy(env
, environ
, sizeof(*env
) * ei
);
1131 /* Replace all those keys that yet exist */
1133 char const *ee
, *kvs
;
1137 kvs
= strchr(ee
, '=');
1138 assert(kvs
!= NULL
);
1139 kl
= PTR2SIZE(kvs
- ee
);
1141 for (ei
= ei_orig
; ei
-- > 0;) {
1142 char const *ekvs
= strchr(env
[ei
], '=');
1143 if (ekvs
!= NULL
&& kl
== PTR2SIZE(ekvs
- env
[ei
]) &&
1144 !memcmp(ee
, env
[ei
], kl
)) {
1145 env
[ei
] = n_UNCONST(ee
);
1146 env_addon
[ai
] = NULL
;
1152 /* And append the rest */
1153 for (ei
= ei_orig
, ai
= ai_orig
; ai
-- > 0;)
1154 if (env_addon
[ai
] != NULL
)
1155 env
[ei
++] = n_UNCONST(env_addon
[ai
]);
1161 i
= (int)getrawlist(TRU1
, argv
, n_NELEM(argv
), cmd
, strlen(cmd
));
1163 if ((argv
[i
++] = n_UNCONST(a0
)) != NULL
&&
1164 (argv
[i
++] = n_UNCONST(a1
)) != NULL
&&
1165 (argv
[i
++] = n_UNCONST(a2
)) != NULL
)
1167 prepare_child(mask
, infd
, outfd
);
1168 execvp(argv
[0], argv
);
1177 prepare_child(sigset_t
*nset
, int infd
, int outfd
)
1183 /* All file descriptors other than 0, 1, and 2 are supposed to be cloexec */
1184 /* TODO WHAT IS WITH STDERR_FILENO DAMN? */
1185 if ((i
= (infd
== COMMAND_FD_NULL
)))
1186 infd
= open("/dev/null", O_RDONLY
);
1188 dup2(infd
, STDIN_FILENO
);
1193 if ((i
= (outfd
== COMMAND_FD_NULL
)))
1194 outfd
= open("/dev/null", O_WRONLY
);
1196 dup2(outfd
, STDOUT_FILENO
);
1202 for (i
= 1; i
< NSIG
; ++i
)
1203 if (sigismember(nset
, i
))
1204 safe_signal(i
, SIG_IGN
);
1205 if (!sigismember(nset
, SIGINT
))
1206 safe_signal(SIGINT
, SIG_DFL
);
1210 sigprocmask(SIG_SETMASK
, &fset
, NULL
);
1217 sigset_t nset
, oset
;
1222 sigaddset(&nset
, SIGCHLD
);
1223 sigprocmask(SIG_BLOCK
, &nset
, &oset
);
1225 if ((cp
= _findchild(pid
, FAL0
)) != NULL
) {
1232 sigprocmask(SIG_SETMASK
, &oset
, NULL
);
1237 wait_child(int pid
, int *wait_status
)
1239 sigset_t nset
, oset
;
1246 sigaddset(&nset
, SIGCHLD
);
1247 sigprocmask(SIG_BLOCK
, &nset
, &oset
);
1249 cp
= _findchild(pid
, FAL0
);
1258 sigprocmask(SIG_SETMASK
, &oset
, NULL
);
1260 if (wait_status
!= NULL
)
1262 rv
= (WIFEXITED(ws
) && WEXITSTATUS(ws
) == 0);