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 - 2018 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
6 * SPDX-License-Identifier: BSD-3-Clause
9 * Copyright (c) 1980, 1993
10 * The Regents of the University of California. All rights reserved.
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39 #ifndef HAVE_AMALGAMATION
57 FP_MASK
= (1u<<7) - 1,
58 /* TODO FP_UNLINK: should be in a separated process so that unlinking
59 * TODO the temporary "garbage" is "safe"(r than it is like that) */
67 struct termios
*fp_tios
;
68 n_sighdl_t fp_osigint
; /* Only if FP_TERMIOS */
79 static struct fp
*fp_head
;
80 static struct child
*_popen_child
;
82 /* TODO Rather temporary: deal with job control with FD_PASS */
83 static struct termios a_popen_tios
;
84 static sighandler_type a_popen_otstp
, a_popen_ottin
, a_popen_ottou
;
85 static volatile int a_popen_hadsig
;
87 static int a_popen_scan_mode(char const *mode
, int *omode
);
88 static void register_file(FILE *fp
, int omode
, int pid
,
89 int flags
, char const *realfile
, long offset
,
90 char const *save_cmd
, struct termios
*tiosp
,
92 static enum okay
_file_save(struct fp
*fpp
);
93 static int a_popen_file_load(int flags
, int infd
, int outfd
,
94 char const *load_cmd
);
95 static enum okay
unregister_file(FILE *fp
, struct termios
**tiosp
,
97 static int file_pid(FILE *fp
);
99 /* TODO Rather temporary: deal with job control with FD_PASS */
100 static void a_popen_jobsigs_up(void);
101 static void a_popen_jobsigs_down(void);
102 static void a_popen_jobsig(int sig
);
105 static void a_popen_sigchld(int signo
);
107 static struct child
*a_popen_child_find(int pid
, bool_t create
);
108 static void a_popen_child_del(struct child
*cp
);
111 a_popen_scan_mode(char const *mode
, int *omode
){
117 {"w", O_WRONLY
| O_CREAT
| n_O_NOXY_BITS
| O_TRUNC
},
118 {"wx", O_WRONLY
| O_CREAT
| O_EXCL
},
119 {"a", O_WRONLY
| O_APPEND
| O_CREAT
| n_O_NOXY_BITS
},
120 {"a+", O_RDWR
| O_APPEND
| O_CREAT
| n_O_NOXY_BITS
},
122 {"w+", O_RDWR
| O_CREAT
| O_EXCL
}
127 for(i
= 0; UICMP(z
, i
, <, n_NELEM(maps
)); ++i
)
128 if(!strcmp(maps
[i
].mode
, mode
)){
129 *omode
= maps
[i
].omode
;
134 DBG( n_alert(_("Internal error: bad stdio open mode %s"), mode
); )
135 n_err_no
= n_ERR_INVAL
;
136 *omode
= 0; /* (silence CC) */
144 register_file(FILE *fp
, int omode
, int pid
, int flags
,
145 char const *realfile
, long offset
, char const *save_cmd
,
146 struct termios
*tiosp
, n_sighdl_t osigint
)
151 assert(!(flags
& FP_UNLINK
) || realfile
!= NULL
);
152 assert(!(flags
& FP_TERMIOS
) || tiosp
!= NULL
);
154 fpp
= n_alloc(sizeof *fpp
);
160 fpp
->realfile
= (realfile
!= NULL
) ? sstrdup(realfile
) : NULL
;
161 fpp
->save_cmd
= (save_cmd
!= NULL
) ? sstrdup(save_cmd
) : NULL
;
162 fpp
->fp_tios
= tiosp
;
163 fpp
->fp_osigint
= osigint
;
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
));
195 if ((fpp
->flags
& FP_MASK
) == FP_IMAP
) {
196 rv
= imap_append(fpp
->realfile
, fpp
->fp
, fpp
->offset
);
202 if ((fpp
->flags
& FP_MASK
) == FP_MAILDIR
) {
203 rv
= maildir_append(fpp
->realfile
, fpp
->fp
, fpp
->offset
);
208 outfd
= open(fpp
->realfile
,
209 ((fpp
->omode
| O_CREAT
| (fpp
->omode
& O_APPEND
? 0 : O_TRUNC
) |
210 n_O_NOXY_BITS
) & ~O_EXCL
), 0666);
213 n_err(_("Fatal: cannot create %s: %s\n"),
214 n_shexp_quote_cp(fpp
->realfile
, FAL0
), n_err_to_doc(outfd
));
219 switch(fpp
->flags
& FP_MASK
){
221 if(n_poption
& n_PO_D_V
)
222 n_err(_("Using `filetype' handler %s to save %s\n"),
223 n_shexp_quote_cp(fpp
->save_cmd
, FAL0
),
224 n_shexp_quote_cp(fpp
->realfile
, FAL0
));
225 cmd
[0] = ok_vlook(SHELL
);
227 cmd
[2] = fpp
->save_cmd
;
234 if (n_child_run(cmd
[0], 0, fileno(fpp
->fp
), outfd
,
235 cmd
[1], cmd
[2], NULL
, NULL
, NULL
) >= 0)
245 a_popen_file_load(int flags
, int infd
, int outfd
, char const *load_cmd
){
251 switch(flags
& FP_MASK
){
257 cmd
[0] = ok_vlook(SHELL
);
267 rv
= n_child_run(cmd
[0], 0, infd
, outfd
, cmd
[1], cmd
[2], NULL
, NULL
, NULL
);
274 unregister_file(FILE *fp
, struct termios
**tiosp
, n_sighdl_t
*osigint
)
283 for (pp
= &fp_head
; (p
= *pp
) != NULL
; pp
= &p
->link
)
285 switch (p
->flags
& FP_MASK
) {
293 if ((p
->flags
& FP_UNLINK
) && unlink(p
->realfile
))
297 if (p
->save_cmd
!= NULL
)
299 if (p
->realfile
!= NULL
)
301 if (p
->flags
& FP_TERMIOS
) {
304 *osigint
= p
->fp_osigint
;
311 DBGOR(n_panic
, n_alert
)(_("Invalid file pointer"));
326 for (p
= fp_head
; p
; p
= p
->link
)
336 a_popen_jobsigs_up(void){
342 sigprocmask(SIG_BLOCK
, &nset
, &oset
);
343 a_popen_otstp
= safe_signal(SIGTSTP
, &a_popen_jobsig
);
344 a_popen_ottin
= safe_signal(SIGTTIN
, &a_popen_jobsig
);
345 a_popen_ottou
= safe_signal(SIGTTOU
, &a_popen_jobsig
);
347 /* This assumes oset contains nothing but SIGCHLD, so to say */
348 sigdelset(&oset
, SIGTSTP
);
349 sigdelset(&oset
, SIGTTIN
);
350 sigdelset(&oset
, SIGTTOU
);
351 sigprocmask(SIG_SETMASK
, &oset
, NULL
);
356 a_popen_jobsigs_down(void){
362 sigprocmask(SIG_BLOCK
, &nset
, &oset
);
363 safe_signal(SIGTSTP
, a_popen_otstp
);
364 safe_signal(SIGTTIN
, a_popen_ottin
);
365 safe_signal(SIGTTOU
, a_popen_ottou
);
367 sigaddset(&oset
, SIGTSTP
);
368 sigaddset(&oset
, SIGTTIN
);
369 sigaddset(&oset
, SIGTTOU
);
370 sigprocmask(SIG_SETMASK
, &oset
, NULL
);
375 a_popen_jobsig(int sig
){
376 sighandler_type oldact
;
379 NYD_X
; /* Signal handler */
381 hadsig
= (a_popen_hadsig
!= 0);
384 n_TERMCAP_SUSPEND(TRU1
);
386 oldact
= safe_signal(sig
, SIG_DFL
);
389 sigaddset(&nset
, sig
);
390 sigprocmask(SIG_UNBLOCK
, &nset
, NULL
);
392 sigprocmask(SIG_BLOCK
, &nset
, NULL
);
394 safe_signal(sig
, oldact
);
398 a_popen_sigchld(int signo
){
402 NYD_X
; /* Signal handler */
406 pid
= waitpid(-1, &status
, WNOHANG
);
408 if (pid
== -1 && n_err_no
== n_ERR_INTR
)
413 if ((cp
= a_popen_child_find(pid
, FAL0
)) != NULL
) {
416 cp
->pid
= -1; /* XXX Was _delchild(cp);# */
424 static struct child
*
425 a_popen_child_find(int pid
, bool_t create
){
426 struct child
**cpp
, *cp
;
429 for(cpp
= &_popen_child
; (cp
= *cpp
) != NULL
&& cp
->pid
!= pid
;
433 if(cp
== NULL
&& create
)
434 (*cpp
= cp
= n_calloc(1, sizeof *cp
))->pid
= pid
;
440 a_popen_child_del(struct child
*cp
){
452 if(*(cpp
= &(*cpp
)->link
) == NULL
){
453 DBG( n_err("! a_popen_child_del(): implementation error\n"); )
461 n_child_manager_start(void)
463 struct sigaction nact
, oact
;
466 nact
.sa_handler
= &a_popen_sigchld
;
467 sigemptyset(&nact
.sa_mask
);
468 nact
.sa_flags
= SA_RESTART
473 if (sigaction(SIGCHLD
, &nact
, &oact
) != 0)
474 n_panic(_("Cannot install signal handler for child process management"));
479 safe_fopen(char const *file
, char const *oflags
, int *xflags
)
483 NYD2_ENTER
; /* (only for Fopen() and once in go.c) */
485 if (a_popen_scan_mode(oflags
, &osflags
) < 0)
487 osflags
|= _O_CLOEXEC
;
491 if ((fd
= open(file
, osflags
, 0666)) == -1)
495 fp
= fdopen(fd
, oflags
);
502 Fopen(char const *file
, char const *oflags
)
508 if ((fp
= safe_fopen(file
, oflags
, &osflags
)) != NULL
)
509 register_file(fp
, osflags
, 0, FP_RAW
, NULL
, 0L, NULL
, NULL
,NULL
);
515 Fdopen(int fd
, char const *oflags
, bool_t nocloexec
)
521 a_popen_scan_mode(oflags
, &osflags
);
523 osflags
|= _O_CLOEXEC
; /* Ensured to be set by caller as documented! */
525 if ((fp
= fdopen(fd
, oflags
)) != NULL
)
526 register_file(fp
, osflags
, 0, FP_RAW
, NULL
, 0L, NULL
, NULL
,NULL
);
537 if (unregister_file(fp
, NULL
, NULL
) == OKAY
)
542 return (i
== 3 ? 0 : EOF
);
546 n_fopen_any(char const *file
, char const *oflags
, /* TODO should take flags */
547 enum n_fopen_state
*fs_or_null
){ /* TODO as bits, return state */
548 /* TODO Support file locking upon open time */
552 int osflags
, flags
, omode
, infd
;
553 char const *cload
, *csave
;
554 enum n_fopen_state fs
;
559 fs
= n_FOPEN_STATE_NONE
;
560 cload
= csave
= NULL
;
562 if(a_popen_scan_mode(oflags
, &osflags
) < 0)
566 rof
= OF_RDWR
| OF_UNLINK
;
567 if(osflags
& O_APPEND
)
569 omode
= (osflags
== O_RDONLY
) ? R_OK
: R_OK
| W_OK
;
571 /* We don't want to find mbox.bz2 when doing "copy * mbox", but only for
572 * "file mbox", so don't try hooks when writing */
573 p
= which_protocol(csave
= file
, TRU1
, ((omode
& W_OK
) == 0), &file
);
574 fs
= (enum n_fopen_state
)p
;
582 osflags
= O_RDWR
| O_APPEND
| O_CREAT
| n_O_NOXY_BITS
;
586 n_err_no
= n_ERR_OPNOTSUPP
;
589 case n_PROTO_MAILDIR
:
591 if(fs_or_null
!= NULL
&& !access(file
, F_OK
))
592 fs
|= n_FOPEN_STATE_EXISTS
;
594 osflags
= O_RDWR
| O_APPEND
| O_CREAT
| n_O_NOXY_BITS
;
598 n_err_no
= n_ERR_OPNOTSUPP
;
602 struct n_file_type ft
;
604 if(!(osflags
& O_EXCL
) && fs_or_null
!= NULL
&& !access(file
, F_OK
))
605 fs
|= n_FOPEN_STATE_EXISTS
;
607 if(n_filetype_exists(&ft
, file
)){
609 cload
= ft
.ft_load_dat
;
610 csave
= ft
.ft_save_dat
;
611 /* Cause truncation for compressor/hook output files */
612 osflags
&= ~O_APPEND
;
614 if((infd
= open(file
, (omode
& W_OK
? O_RDWR
: O_RDONLY
))) != -1){
615 fs
|= n_FOPEN_STATE_EXISTS
;
616 if(n_poption
& n_PO_D_V
)
617 n_err(_("Using `filetype' handler %s to load %s\n"),
618 n_shexp_quote_cp(cload
, FAL0
), n_shexp_quote_cp(file
, FAL0
));
619 }else if(!(osflags
& O_CREAT
) || n_err_no
!= n_ERR_NOENT
)
623 rv
= Fopen(file
, oflags
);
624 if((osflags
& O_EXCL
) && rv
== NULL
)
625 fs
|= n_FOPEN_STATE_EXISTS
;
631 /* Note rv is not yet register_file()d, fclose() it in error path! */
632 if((rv
= Ftmp(NULL
, "fopenany", rof
)) == NULL
){
633 n_perr(_("tmpfile"), 0);
637 if(flags
& (FP_IMAP
| FP_MAILDIR
))
640 if(a_popen_file_load(flags
, infd
, fileno(rv
), cload
) < 0){
650 if((infd
= creat(file
, 0666)) == -1){
661 if(!(osflags
& O_APPEND
))
663 if((offset
= ftell(rv
)) == -1){
669 register_file(rv
, osflags
, 0, flags
, file
, offset
, csave
, NULL
,NULL
);
671 if(fs_or_null
!= NULL
)
678 Ftmp(char **fn
, char const *namehint
, enum oflags oflags
)
680 /* The 8 is arbitrary but leaves room for a six character suffix (the
681 * POSIX minimum path length is 14, though we don't check that XXX).
682 * 8 should be more than sufficient given that we use base64url encoding
683 * for our random string */
684 enum {_RANDCHARS
= 8u};
687 size_t maxname
, xlen
, i
;
694 assert(namehint
!= NULL
);
695 assert((oflags
& OF_WRONLY
) || (oflags
& OF_RDWR
));
696 assert(!(oflags
& OF_RDONLY
));
697 assert(!(oflags
& OF_REGISTER_UNLINK
) || (oflags
& OF_REGISTER
));
702 tmpdir
= ok_vlook(TMPDIR
);
707 if ((pc
= pathconf(tmpdir
, _PC_NAME_MAX
)) != -1)
708 maxname
= (size_t)pc
;
712 if ((oflags
& OF_SUFFIX
) && *namehint
!= '\0') {
713 if ((xlen
= strlen(namehint
)) > maxname
- _RANDCHARS
) {
714 n_err_no
= n_ERR_NAMETOOLONG
;
720 /* Prepare the template string once, then iterate over the random range */
722 cp
= n_lofi_alloc(strlen(tmpdir
) + 1 + maxname
+1);
723 cp
= sstpcpy(cp
, tmpdir
);
726 char *x
= sstpcpy(cp
, VAL_UAGENT
);
728 if (!(oflags
& OF_SUFFIX
))
729 x
= sstpcpy(x
, namehint
);
731 i
= PTR2SIZE(x
- cp
);
732 if (i
> maxname
- xlen
- _RANDCHARS
) {
733 size_t j
= maxname
- xlen
- _RANDCHARS
;
737 if ((oflags
& OF_SUFFIX
) && xlen
> 0)
738 memcpy(x
+ _RANDCHARS
, namehint
, xlen
);
740 x
[xlen
+ _RANDCHARS
] = '\0';
744 osoflags
= O_CREAT
| O_EXCL
| _O_CLOEXEC
;
745 osoflags
|= (oflags
& OF_WRONLY
) ? O_WRONLY
: O_RDWR
;
746 if (oflags
& OF_APPEND
)
747 osoflags
|= O_APPEND
;
749 for(relesigs
= TRU1
, i
= 0;; ++i
){
750 memcpy(cp
, n_random_create_cp(_RANDCHARS
, NULL
), _RANDCHARS
);
754 if((fd
= open(cp_base
, osoflags
, 0600)) != -1){
758 if(i
>= FTMP_OPEN_TRIES
){
765 if (oflags
& OF_REGISTER
) {
766 char const *osflags
= (oflags
& OF_RDWR
? "w+" : "w");
769 a_popen_scan_mode(osflags
, &osflagbits
); /* TODO osoflags&xy ?!!? */
770 if ((fp
= fdopen(fd
, osflags
)) != NULL
)
771 register_file(fp
, osflagbits
| _O_CLOEXEC
, 0,
772 (FP_RAW
| (oflags
& OF_REGISTER_UNLINK
? FP_UNLINK
: 0)),
773 cp_base
, 0L, NULL
, NULL
,NULL
);
775 fp
= fdopen(fd
, (oflags
& OF_RDWR
? "w+" : "w"));
777 if (fp
== NULL
|| (oflags
& OF_UNLINK
)) {
781 }else if(fp
!= NULL
){
782 /* We will succeed and keep the file around for further usage, likely
783 * another stream will be opened for pure reading purposes (this is true
784 * at the time of this writing. A restrictive umask(2) settings may have
785 * turned the path inaccessible, so ensure it may be read at least!
786 * TODO once ok_vlook() can return an integer, look up *umask* first! */
787 (void)fchmod(fd
, S_IWUSR
| S_IRUSR
);
791 i
= strlen(cp_base
) +1;
792 cp
= (oflags
& OF_FN_AUTOREC
) ? n_autorec_alloc(i
) : n_alloc(i
);
793 memcpy(cp
, cp_base
, i
);
796 n_lofi_free(cp_base
);
798 if (relesigs
&& (fp
== NULL
|| !(oflags
& OF_HOLDSIGS
)))
805 if((cp
= cp_base
) != NULL
)
811 Ftmp_release(char **fn
)
827 Ftmp_free(char **fn
) /* TODO DROP: OF_REGISTER_FREEPATH! */
840 pipe_cloexec(int fd
[2]){
847 if(pipe2(fd
, O_CLOEXEC
) != -1)
851 n_fd_cloexec_set(fd
[0]);
852 n_fd_cloexec_set(fd
[1]);
861 Popen(char const *cmd
, char const *mode
, char const *sh
,
862 char const **env_addon
, int newfd1
)
864 int p
[2], myside
, hisside
, fd0
, fd1
, pid
;
868 struct termios
*tiosp
;
872 /* First clean up child structures */
874 struct child
**cpp
, *cp
;
877 for (cpp
= &_popen_child
; *cpp
!= NULL
;) {
878 if ((*cpp
)->pid
== -1) {
890 n_UNINIT(osigint
, SIG_ERR
);
891 mod
[0] = '0', mod
[1] = '\0';
893 if (!pipe_cloexec(p
))
898 fd0
= n_CHILD_FD_PASS
;
899 hisside
= fd1
= p
[WRITE
];
901 } else if (*mode
== 'W') {
903 hisside
= fd0
= p
[READ
];
908 hisside
= fd0
= p
[READ
];
909 fd1
= n_CHILD_FD_PASS
;
913 /* In interactive mode both STDIN and STDOUT point to the terminal. If we
914 * pass through the TTY restore terminal attributes after pipe returns.
915 * XXX It shouldn't matter which FD we actually use in this case */
916 if ((n_psonce
& n_PSO_INTERACTIVE
) && (fd0
== n_CHILD_FD_PASS
||
917 fd1
== n_CHILD_FD_PASS
)) {
918 osigint
= n_signal(SIGINT
, SIG_IGN
);
919 tiosp
= n_alloc(sizeof *tiosp
);
920 tcgetattr(STDIN_FILENO
, tiosp
);
921 n_TERMCAP_SUSPEND(TRU1
);
926 if (cmd
== (char*)-1) {
927 if ((pid
= n_child_fork()) == -1)
928 n_perr(_("fork"), 0);
930 union {char const *ccp
; int (*ptf
)(void); int es
;} u
;
931 n_child_prepare(&nset
, fd0
, fd1
);
934 /* TODO should close all other open FDs except stds and reset memory */
935 /* Standard I/O drives me insane! All we need is a sync operation
936 * that causes n_stdin to forget about any read buffer it may have.
937 * We cannot use fflush(3), this works with Musl and Solaris, but not
938 * with GlibC. (For at least pipes.) We cannot use fdreopen(),
939 * because this function does not exist! Luckily (!!!) we only use
940 * n_stdin not stdin in our child, otherwise all bets were off!
941 * TODO (Unless we would fiddle around with FILE* directly:
942 * TODO #ifdef __GLIBC__
943 * TODO n_stdin->_IO_read_ptr = n_stdin->_IO_read_end;
945 * TODO n_stdin->_r = 0;
946 * TODO #elif n_OS_SOLARIS || n_OS_SUNOS
947 * TODO n_stdin->_cnt = 0;
949 * TODO ) which should have additional config test for sure! */
950 n_stdin
= fdopen(STDIN_FILENO
, "r");
951 /*n_stdout = fdopen(STDOUT_FILENO, "w");*/
952 /*n_stderr = fdopen(STDERR_FILENO, "w");*/
958 } else if (sh
== NULL
) {
959 pid
= n_child_start(cmd
, &nset
, fd0
, fd1
, NULL
, NULL
, NULL
, env_addon
);
961 pid
= n_child_start(sh
, &nset
, fd0
, fd1
, "-c", cmd
, NULL
, env_addon
);
969 if ((rv
= fdopen(myside
, mod
)) != NULL
)
970 register_file(rv
, 0, pid
,
971 (tiosp
== NULL
? FP_PIPE
: FP_PIPE
| FP_TERMIOS
),
972 NULL
, 0L, NULL
, tiosp
, osigint
);
976 if(rv
== NULL
&& tiosp
!= NULL
){
977 n_TERMCAP_RESUME(TRU1
);
978 tcsetattr(STDIN_FILENO
, TCSAFLUSH
, tiosp
);
980 n_signal(SIGINT
, osigint
);
987 Pclose(FILE *ptr
, bool_t dowait
)
990 struct termios
*tiosp
;
999 unregister_file(ptr
, &tiosp
, &osigint
);
1004 rv
= n_child_wait(pid
, NULL
);
1006 n_TERMCAP_RESUME(TRU1
);
1007 tcsetattr(STDIN_FILENO
, TCSAFLUSH
, tiosp
);
1008 n_signal(SIGINT
, osigint
);
1024 n_psignal(FILE *fp
, int sig
){
1028 if((rv
= file_pid(fp
)) >= 0){
1031 if((cp
= a_popen_child_find(rv
, FAL0
)) != NULL
){
1032 if((rv
= kill(rv
, sig
)) != 0)
1044 char const *env_add
[2], *pager
;
1048 assert(n_psonce
& n_PSO_INTERACTIVE
);
1050 pager
= n_pager_get(env_add
+ 0);
1053 if ((rv
= Popen(pager
, "w", NULL
, env_add
, n_CHILD_FD_PASS
)) == NULL
)
1060 n_pager_close(FILE *fp
)
1066 sh
= safe_signal(SIGPIPE
, SIG_IGN
);
1067 rv
= Pclose(fp
, TRU1
);
1068 safe_signal(SIGPIPE
, sh
);
1074 close_all_files(void)
1077 while (fp_head
!= NULL
)
1078 if ((fp_head
->flags
& FP_MASK
) == FP_PIPE
)
1079 Pclose(fp_head
->fp
, TRU1
);
1081 Fclose(fp_head
->fp
);
1085 /* TODO The entire n_child_ series should be replaced with an object, but
1086 * TODO at least have carrier arguments. We anyway need a command manager
1087 * TODO that keeps track and knows how to handle job control ++++! */
1090 n_child_run(char const *cmd
, sigset_t
*mask_or_null
, int infd
, int outfd
,
1091 char const *a0_or_null
, char const *a1_or_null
, char const *a2_or_null
,
1092 char const **env_addon_or_null
, int *wait_status_or_null
)
1094 sigset_t nset
, oset
;
1095 sighandler_type soldint
;
1097 enum {a_NONE
= 0, a_INTIGN
= 1<<0, a_TTY
= 1<<1} f
;
1101 n_UNINIT(soldint
, SIG_ERR
);
1103 /* TODO Of course this is a joke given that during a "p*" the PAGER may
1104 * TODO be up and running while we play around like this... but i guess
1105 * TODO this can't be helped at all unless we perform complete and true
1106 * TODO process group separation and ensure we don't deadlock us out
1107 * TODO via TTY jobcontrol signal storms (could this really happen?).
1108 * TODO Or have a built-in pager. Or query any necessity BEFORE we start
1109 * TODO any action, and shall we find we need to run programs dump it
1110 * TODO all into a temporary file which is then passed through to the
1111 * TODO PAGER. Ugh. That still won't help for "needsterminal" anyway */
1112 if(infd
== n_CHILD_FD_PASS
|| outfd
== n_CHILD_FD_PASS
){
1113 soldint
= safe_signal(SIGINT
, SIG_IGN
);
1116 if(n_psonce
& n_PSO_INTERACTIVE
){
1118 tcgetattr((n_psonce
& n_PSO_TTYIN
? STDIN_FILENO
: STDOUT_FILENO
),
1120 n_TERMCAP_SUSPEND(FAL0
);
1122 sigdelset(&nset
, SIGCHLD
);
1123 sigdelset(&nset
, SIGINT
);
1124 /* sigdelset(&nset, SIGPIPE); TODO would need a handler */
1125 sigprocmask(SIG_BLOCK
, &nset
, &oset
);
1127 a_popen_jobsigs_up();
1131 if((rv
= n_child_start(cmd
, mask_or_null
, infd
, outfd
, a0_or_null
,
1132 a1_or_null
, a2_or_null
, env_addon_or_null
)) < 0){
1139 if(n_child_wait(rv
, &ws
))
1141 else if(wait_status_or_null
== NULL
|| !WIFEXITED(ws
)){
1142 if(ok_blook(bsdcompat
) || ok_blook(bsdmsgs
))
1143 n_err(_("Fatal error in process\n"));
1147 if(wait_status_or_null
!= NULL
)
1148 *wait_status_or_null
= ws
;
1152 a_popen_jobsigs_down();
1153 n_TERMCAP_RESUME(a_popen_hadsig
? TRU1
: FAL0
);
1154 tcsetattr(((n_psonce
& n_PSO_TTYIN
) ? STDIN_FILENO
: STDOUT_FILENO
),
1155 ((n_psonce
& n_PSO_TTYIN
) ? TCSAFLUSH
: TCSADRAIN
), &a_popen_tios
);
1156 sigprocmask(SIG_SETMASK
, &oset
, NULL
);
1159 if(soldint
!= SIG_IGN
)
1160 safe_signal(SIGINT
, soldint
);
1170 n_child_start(char const *cmd
, sigset_t
*mask_or_null
, int infd
, int outfd
,
1171 char const *a0_or_null
, char const *a1_or_null
, char const *a2_or_null
,
1172 char const **env_addon_or_null
)
1177 if ((rv
= n_child_fork()) == -1) {
1179 n_perr(_("fork"), 0);
1182 } else if (rv
== 0) {
1186 if (env_addon_or_null
!= NULL
) {
1187 extern char **environ
;
1188 size_t ei
, ei_orig
, ai
, ai_orig
;
1191 /* TODO note we don't check the POSIX limit:
1192 * the total space used to store the environment and the arguments to
1193 * the process is limited to {ARG_MAX} bytes */
1194 for (ei
= 0; environ
[ei
] != NULL
; ++ei
)
1197 for (ai
= 0; env_addon_or_null
[ai
] != NULL
; ++ai
)
1200 env
= n_lofi_alloc(sizeof(*env
) * (ei
+ ai
+1));
1201 memcpy(env
, environ
, sizeof(*env
) * ei
);
1203 /* Replace all those keys that yet exist */
1205 char const *ee
, *kvs
;
1208 ee
= env_addon_or_null
[ai
];
1209 kvs
= strchr(ee
, '=');
1210 assert(kvs
!= NULL
);
1211 kl
= PTR2SIZE(kvs
- ee
);
1213 for (ei
= ei_orig
; ei
-- > 0;) {
1214 char const *ekvs
= strchr(env
[ei
], '=');
1215 if (ekvs
!= NULL
&& kl
== PTR2SIZE(ekvs
- env
[ei
]) &&
1216 !memcmp(ee
, env
[ei
], kl
)) {
1217 env
[ei
] = n_UNCONST(ee
);
1218 env_addon_or_null
[ai
] = NULL
;
1224 /* And append the rest */
1225 for (ei
= ei_orig
, ai
= ai_orig
; ai
-- > 0;)
1226 if (env_addon_or_null
[ai
] != NULL
)
1227 env
[ei
++] = n_UNCONST(env_addon_or_null
[ai
]);
1233 i
= (int)getrawlist(TRU1
, argv
, n_NELEM(argv
), cmd
, strlen(cmd
));
1235 if ((argv
[i
++] = n_UNCONST(a0_or_null
)) != NULL
&&
1236 (argv
[i
++] = n_UNCONST(a1_or_null
)) != NULL
&&
1237 (argv
[i
++] = n_UNCONST(a2_or_null
)) != NULL
)
1239 n_child_prepare(mask_or_null
, infd
, outfd
);
1240 execvp(argv
[0], argv
);
1251 /* Strictly speaking we should do so in the waitpid(2) case too, but since
1252 * we explicitly waitpid(2) on the pid if just the structure exists, which
1253 * n_child_wait() does in the parent, all is fine */
1254 #if n_SIGSUSPEND_NOT_WAITPID
1255 sigset_t nset
, oset
;
1261 #if n_SIGSUSPEND_NOT_WAITPID
1263 sigprocmask(SIG_BLOCK
, &nset
, &oset
);
1266 cp
= a_popen_child_find(0, TRU1
);
1268 if((cp
->pid
= pid
= fork()) == -1){
1269 a_popen_child_del(cp
);
1270 n_perr(_("fork"), 0);
1273 #if n_SIGSUSPEND_NOT_WAITPID
1274 sigprocmask(SIG_SETMASK
, &oset
, NULL
);
1281 n_child_prepare(sigset_t
*nset_or_null
, int infd
, int outfd
)
1287 /* All file descriptors other than 0, 1, and 2 are supposed to be cloexec */
1288 /* TODO WHAT IS WITH STDERR_FILENO DAMN? */
1289 if ((i
= (infd
== n_CHILD_FD_NULL
)))
1290 infd
= open(n_path_devnull
, O_RDONLY
);
1292 dup2(infd
, STDIN_FILENO
);
1297 if ((i
= (outfd
== n_CHILD_FD_NULL
)))
1298 outfd
= open(n_path_devnull
, O_WRONLY
);
1300 dup2(outfd
, STDOUT_FILENO
);
1305 if (nset_or_null
!= NULL
) {
1306 for (i
= 1; i
< NSIG
; ++i
)
1307 if (sigismember(nset_or_null
, i
))
1308 safe_signal(i
, SIG_IGN
);
1309 if (!sigismember(nset_or_null
, SIGINT
))
1310 safe_signal(SIGINT
, SIG_DFL
);
1314 sigprocmask(SIG_SETMASK
, &fset
, NULL
);
1319 n_child_free(int pid
){
1320 sigset_t nset
, oset
;
1325 sigaddset(&nset
, SIGCHLD
);
1326 sigprocmask(SIG_BLOCK
, &nset
, &oset
);
1328 if((cp
= a_popen_child_find(pid
, FAL0
)) != NULL
){
1330 a_popen_child_del(cp
);
1335 sigprocmask(SIG_SETMASK
, &oset
, NULL
);
1340 n_child_wait(int pid
, int *wait_status_or_null
){
1341 #if !n_SIGSUSPEND_NOT_WAITPID
1350 #if !n_SIGSUSPEND_NOT_WAITPID
1352 sigaddset(&nset
, SIGCHLD
);
1353 sigprocmask(SIG_BLOCK
, &nset
, &oset
);
1356 if((cp
= a_popen_child_find(pid
, FAL0
)) != NULL
){
1357 #if n_SIGSUSPEND_NOT_WAITPID
1359 sigdelset(&nset
, SIGCHLD
);
1361 sigsuspend(&nset
); /* TODO we should allow more than SIGCHLD!! */
1365 waitpid(pid
, &ws
, 0);
1369 a_popen_child_del(cp
);
1373 #if !n_SIGSUSPEND_NOT_WAITPID
1374 sigprocmask(SIG_SETMASK
, &oset
, NULL
);
1377 if(wait_status_or_null
!= NULL
)
1378 *wait_status_or_null
= ws
;
1379 rv
= (WIFEXITED(ws
) && WEXITSTATUS(ws
) == 0);