mirror of
https://repo.or.cz/socat.git
synced 2024-12-22 15:32:35 +00:00
EXEC and SYSTEM with stderr injected socat messages into the data stream
This commit is contained in:
parent
c4751d50ec
commit
ad4bd0d9db
9 changed files with 126 additions and 67 deletions
3
CHANGES
3
CHANGES
|
@ -9,6 +9,9 @@ corrections:
|
|||
|
||||
corrected a few mistakes that caused compiler warnings on 64bit hosts
|
||||
|
||||
EXEC and SYSTEM with stderr injected socat messages into the data
|
||||
stream. test: EXECSTDERRLOG
|
||||
|
||||
####################### V 1.6.0.1:
|
||||
|
||||
new features:
|
||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
"1.6.0.1+ip4bind+recvfromfork+x64"
|
||||
"1.6.0.1+ip4bind+recvfromfork+x64+execstderr"
|
||||
|
|
67
error.c
67
error.c
|
@ -39,7 +39,6 @@ struct diag_opts {
|
|||
const char *progname;
|
||||
int msglevel;
|
||||
int exitlevel;
|
||||
int logstderr;
|
||||
int syslog;
|
||||
FILE *logfile;
|
||||
int logfacility;
|
||||
|
@ -51,7 +50,7 @@ struct diag_opts {
|
|||
|
||||
|
||||
struct diag_opts diagopts =
|
||||
{ NULL, E_ERROR, E_ERROR, 1, 0, NULL, LOG_DAEMON, false, 0 } ;
|
||||
{ NULL, E_ERROR, E_ERROR, 0, NULL, LOG_DAEMON, false, 0 } ;
|
||||
|
||||
static void _msg(int level, const char *buff, const char *syslp);
|
||||
|
||||
|
@ -89,7 +88,21 @@ static struct wordent facilitynames[] = {
|
|||
} ;
|
||||
|
||||
|
||||
static int diaginitialized;
|
||||
static int diag_init(void) {
|
||||
if (diaginitialized) {
|
||||
return 0;
|
||||
}
|
||||
diaginitialized = 1;
|
||||
/* gcc with GNU libc refuses to set this in the initializer */
|
||||
diagopts.logfile = stderr;
|
||||
return 0;
|
||||
}
|
||||
#define DIAG_INIT ((void)(diaginitialized || diag_init()))
|
||||
|
||||
|
||||
void diag_set(char what, const char *arg) {
|
||||
DIAG_INIT;
|
||||
switch (what) {
|
||||
const struct wordent *keywd;
|
||||
|
||||
|
@ -104,14 +117,24 @@ void diag_set(char what, const char *arg) {
|
|||
}
|
||||
}
|
||||
openlog(diagopts.progname, LOG_PID, diagopts.logfacility);
|
||||
diagopts.logstderr = false; break;
|
||||
case 'f': if ((diagopts.logfile = fopen(arg, "a")) == NULL) {
|
||||
if (diagopts.logfile != NULL && diagopts.logfile != stderr) {
|
||||
fclose(diagopts.logfile);
|
||||
}
|
||||
diagopts.logfile = NULL;
|
||||
break;
|
||||
case 'f':
|
||||
if (diagopts.logfile != NULL && diagopts.logfile != stderr) {
|
||||
fclose(diagopts.logfile);
|
||||
}
|
||||
if ((diagopts.logfile = fopen(arg, "a")) == NULL) {
|
||||
Error2("cannot open log file \"%s\": %s", arg, strerror(errno));
|
||||
break;
|
||||
} else {
|
||||
diagopts.logstderr = false; break;
|
||||
}
|
||||
case 's': diagopts.logstderr = true; break; /* logging to stderr is default */
|
||||
case 's':
|
||||
if (diagopts.logfile != NULL && diagopts.logfile != stderr) {
|
||||
fclose(diagopts.logfile);
|
||||
}
|
||||
diagopts.logfile = stderr; break; /* logging to stderr is default */
|
||||
case 'p': diagopts.progname = arg;
|
||||
openlog(diagopts.progname, LOG_PID, diagopts.logfacility);
|
||||
break;
|
||||
|
@ -122,6 +145,7 @@ void diag_set(char what, const char *arg) {
|
|||
}
|
||||
|
||||
void diag_set_int(char what, int arg) {
|
||||
DIAG_INIT;
|
||||
switch (what) {
|
||||
case 'D': diagopts.msglevel = arg; break;
|
||||
case 'e': diagopts.exitlevel = arg; break;
|
||||
|
@ -138,9 +162,10 @@ void diag_set_int(char what, int arg) {
|
|||
}
|
||||
|
||||
int diag_get_int(char what) {
|
||||
DIAG_INIT;
|
||||
switch (what) {
|
||||
case 'y': return diagopts.syslog;
|
||||
case 's': return diagopts.logstderr;
|
||||
case 's': return diagopts.logfile == stderr;
|
||||
case 'd': case 'D': return diagopts.msglevel;
|
||||
case 'e': return diagopts.exitlevel;
|
||||
}
|
||||
|
@ -148,6 +173,7 @@ int diag_get_int(char what) {
|
|||
}
|
||||
|
||||
const char *diag_get_string(char what) {
|
||||
DIAG_INIT;
|
||||
switch (what) {
|
||||
case 'p': return diagopts.progname;
|
||||
}
|
||||
|
@ -170,6 +196,7 @@ void msg(int level, const char *format, ...) {
|
|||
size_t bytes;
|
||||
va_list ap;
|
||||
|
||||
DIAG_INIT;
|
||||
if (level < diagopts.msglevel) return;
|
||||
va_start(ap, format);
|
||||
#if HAVE_GETTIMEOFDAY || 1
|
||||
|
@ -236,9 +263,6 @@ void msg(int level, const char *format, ...) {
|
|||
|
||||
|
||||
static void _msg(int level, const char *buff, const char *syslp) {
|
||||
if (diagopts.logstderr) {
|
||||
fputs(buff, stderr); fflush(stderr);
|
||||
}
|
||||
if (diagopts.syslog) {
|
||||
/* prevent format string attacks (thanks to CoKi) */
|
||||
syslog(syslevel[level], "%s", syslp);
|
||||
|
@ -247,3 +271,24 @@ static void _msg(int level, const char *buff, const char *syslp) {
|
|||
fputs(buff, diagopts.logfile); fflush(diagopts.logfile);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* use a new log output file descriptor that is dup'ed from the current one.
|
||||
this is useful when socat logs to stderr but fd 2 should be redirected to
|
||||
serve other purposes */
|
||||
int diag_dup(void) {
|
||||
int newfd;
|
||||
|
||||
DIAG_INIT;
|
||||
if (diagopts.logfile == NULL) {
|
||||
return -1;
|
||||
}
|
||||
newfd = dup(fileno(diagopts.logfile));
|
||||
if (diagopts.logfile != stderr) {
|
||||
fclose(diagopts.logfile);
|
||||
}
|
||||
if (newfd >= 0) {
|
||||
diagopts.logfile = fdopen(newfd, "w");
|
||||
}
|
||||
return newfd;
|
||||
}
|
||||
|
|
5
error.h
5
error.h
|
@ -1,5 +1,5 @@
|
|||
/* source: error.h */
|
||||
/* Copyright Gerhard Rieger 2001-2007 */
|
||||
/* Copyright Gerhard Rieger 2001-2008 */
|
||||
/* Published under the GNU General Public License V.2, see file COPYING */
|
||||
|
||||
#ifndef __error_h_included
|
||||
|
@ -202,7 +202,8 @@ extern void diag_set(char what, const char *arg);
|
|||
extern void diag_set_int(char what, int arg);
|
||||
extern int diag_get_int(char what);
|
||||
extern const char *diag_get_string(char what);
|
||||
|
||||
extern int diag_dup(void);
|
||||
extern int diag_dup2(int newfd);
|
||||
extern void msg(int level, const char *format, ...);
|
||||
|
||||
#endif /* !defined(__error_h_included) */
|
||||
|
|
18
test.sh
18
test.sh
|
@ -5713,6 +5713,7 @@ esac
|
|||
N=$((N+1))
|
||||
|
||||
|
||||
# there was an error in address EXEC with options pipes,stderr
|
||||
NAME=EXECPIPESSTDERR
|
||||
case "$TESTS" in
|
||||
*%functions%*|*%$NAME%*)
|
||||
|
@ -5721,6 +5722,23 @@ testecho "$N" "$TEST" "" "exec:$CAT,pipes,stderr" "$opts"
|
|||
esac
|
||||
N=$((N+1))
|
||||
|
||||
# EXEC and SYSTEM with stderr injected socat messages into the data stream.
|
||||
NAME=EXECSTDERRLOG
|
||||
case "$TESTS" in
|
||||
*%functions%*|*%$NAME%*)
|
||||
TEST="$NAME: simple echo via exec of cat with pipes,stderr"
|
||||
SAVE_opts="$opts"
|
||||
# make sure at least two -d are there
|
||||
case "$opts" in
|
||||
*-d*-d*) ;;
|
||||
*-d*) opts="$opts -d" ;;
|
||||
*) opts="-d -d" ;;
|
||||
esac
|
||||
testecho "$N" "$TEST" "" "exec:$CAT,pipes,stderr" "$opts"
|
||||
opts="$SAVE_opts"
|
||||
esac
|
||||
N=$((N+1))
|
||||
|
||||
|
||||
NAME=SIMPLEPARSE
|
||||
case "$TESTS" in
|
||||
|
|
10
xio-exec.c
10
xio-exec.c
|
@ -1,5 +1,5 @@
|
|||
/* source: xio-exec.c */
|
||||
/* Copyright Gerhard Rieger 2001-2006 */
|
||||
/* Copyright Gerhard Rieger 2001-2008 */
|
||||
/* Published under the GNU General Public License V.2, see file COPYING */
|
||||
|
||||
/* this file contains the source for opening addresses of exec type */
|
||||
|
@ -32,6 +32,7 @@ static int xioopen_exec(int argc, const char *argv[], struct opt *opts,
|
|||
) {
|
||||
int status;
|
||||
bool dash = false;
|
||||
int duptostderr;
|
||||
|
||||
if (argc != 2) {
|
||||
Error3("\"%s:%s\": wrong number of parameters (%d instead of 1)", argv[0], argv[1], argc-1);
|
||||
|
@ -39,7 +40,7 @@ static int xioopen_exec(int argc, const char *argv[], struct opt *opts,
|
|||
|
||||
retropt_bool(opts, OPT_DASH, &dash);
|
||||
|
||||
status = _xioopen_foxec(xioflags, &fd->stream, groups, &opts);
|
||||
status = _xioopen_foxec(xioflags, &fd->stream, groups, &opts, &duptostderr);
|
||||
if (status < 0) return status;
|
||||
if (status == 0) { /* child */
|
||||
const char *ends[] = { " ", NULL };
|
||||
|
@ -118,6 +119,11 @@ static int xioopen_exec(int argc, const char *argv[], struct opt *opts,
|
|||
return STAT_NORETRY;
|
||||
}
|
||||
|
||||
/* only now redirect stderr */
|
||||
if (duptostderr >= 0) {
|
||||
diag_dup();
|
||||
Dup2(duptostderr, 2);
|
||||
}
|
||||
Notice1("execvp'ing \"%s\"", token);
|
||||
result = Execvp(token, pargv);
|
||||
/* here we come only if execvp() failed */
|
||||
|
|
|
@ -44,7 +44,8 @@ const struct optdesc opt_sigquit = { "sigquit", NULL, OPT_SIGQUIT, GROUP_P
|
|||
int _xioopen_foxec(int xioflags, /* XIO_RDONLY etc. */
|
||||
struct single *fd,
|
||||
unsigned groups,
|
||||
struct opt **copts /* in: opts; out: opts for child */
|
||||
struct opt **copts, /* in: opts; out: opts for child */
|
||||
int *duptostderr /* out: redirect stderr to output fd */
|
||||
) {
|
||||
struct opt *popts; /* parent process options */
|
||||
int numleft;
|
||||
|
@ -261,7 +262,7 @@ int _xioopen_foxec(int xioflags, /* XIO_RDONLY etc. */
|
|||
}
|
||||
if (tn == NULL) {
|
||||
Error("could not open pty");
|
||||
return STAT_NORETRY;
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
Info1("opened pseudo terminal %s", tn);
|
||||
|
@ -280,7 +281,7 @@ int _xioopen_foxec(int xioflags, /* XIO_RDONLY etc. */
|
|||
#endif /* HAVE_OPENPTY */
|
||||
free(*copts);
|
||||
if ((*copts = moveopts(popts, GROUP_TERMIOS|GROUP_FORK|GROUP_EXEC|GROUP_PROCESS)) == NULL) {
|
||||
return STAT_RETRYLATER;
|
||||
return -1;
|
||||
}
|
||||
applyopts_cloexec(ptyfd, popts);/*!*/
|
||||
/* exec:...,pty did not kill child process under some circumstances */
|
||||
|
@ -307,7 +308,7 @@ int _xioopen_foxec(int xioflags, /* XIO_RDONLY etc. */
|
|||
if (rw != XIO_WRONLY) {
|
||||
if (Pipe(rdpip) < 0) {
|
||||
Error2("pipe(%p): %s", rdpip, strerror(errno));
|
||||
return STAT_RETRYLATER;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
/*0 Info2("pipe({%d,%d})", rdpip[0], rdpip[1]);*/
|
||||
|
@ -315,7 +316,7 @@ int _xioopen_foxec(int xioflags, /* XIO_RDONLY etc. */
|
|||
free(*copts);
|
||||
if ((*copts = moveopts(popts, GROUP_FORK|GROUP_EXEC|GROUP_PROCESS))
|
||||
== NULL) {
|
||||
return STAT_RETRYLATER;
|
||||
return -1;
|
||||
}
|
||||
|
||||
popts2 = copyopts(popts, GROUP_ALL);
|
||||
|
@ -330,7 +331,7 @@ int _xioopen_foxec(int xioflags, /* XIO_RDONLY etc. */
|
|||
if (rw != XIO_RDONLY) {
|
||||
if (Pipe(wrpip) < 0) {
|
||||
Error2("pipe(%p): %s", wrpip, strerror(errno));
|
||||
return STAT_RETRYLATER;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
/*0 Info2("pipe({%d,%d})", wrpip[0], wrpip[1]);*/
|
||||
|
@ -364,13 +365,13 @@ int _xioopen_foxec(int xioflags, /* XIO_RDONLY etc. */
|
|||
if (result < 0) {
|
||||
Error5("socketpair(%d, %d, %d, %p): %s",
|
||||
d, type, protocol, sv, strerror(errno));
|
||||
return STAT_RETRYLATER;
|
||||
return -1;
|
||||
}
|
||||
/*0 Info5("socketpair(%d, %d, %d, {%d,%d})",
|
||||
d, type, protocol, sv[0], sv[1]);*/
|
||||
free(*copts);
|
||||
if ((*copts = moveopts(popts, GROUP_FORK|GROUP_EXEC|GROUP_PROCESS)) == NULL) {
|
||||
return STAT_RETRYLATER;
|
||||
return -1;
|
||||
}
|
||||
applyopts(sv[0], *copts, PH_PASTSOCKET);
|
||||
applyopts(sv[1], popts, PH_PASTSOCKET);
|
||||
|
@ -397,7 +398,7 @@ int _xioopen_foxec(int xioflags, /* XIO_RDONLY etc. */
|
|||
if (applyopts_single(fd, popts, PH_LATE) < 0) return -1;
|
||||
}
|
||||
/*0 if ((optpr = copyopts(*copts, GROUP_PROCESS)) == NULL)
|
||||
return STAT_RETRYLATER;*/
|
||||
return -1;*/
|
||||
retropt_bool(*copts, OPT_STDERR, &withstderr);
|
||||
|
||||
xiosetchilddied(); /* set SIGCHLD handler */
|
||||
|
@ -409,7 +410,7 @@ int _xioopen_foxec(int xioflags, /* XIO_RDONLY etc. */
|
|||
pid = Fork();
|
||||
if (pid < 0) {
|
||||
Error1("fork(): %s", strerror(errno));
|
||||
return STAT_RETRYLATER;
|
||||
return -1;
|
||||
}
|
||||
/* gdb recommends to have env controlled sleep after fork */
|
||||
if (forkwaitstring = getenv("SOCAT_FORK_WAIT")) {
|
||||
|
@ -438,13 +439,13 @@ int _xioopen_foxec(int xioflags, /* XIO_RDONLY etc. */
|
|||
if (rw != XIO_RDONLY && fdi != ttyfd) {
|
||||
if (Dup2(ttyfd, fdi) < 0) {
|
||||
Error3("dup2(%d, %d): %s", ttyfd, fdi, strerror(errno));
|
||||
return STAT_RETRYLATER; }
|
||||
return -1; }
|
||||
/*0 Info2("dup2(%d, %d)", ttyfd, fdi);*/
|
||||
}
|
||||
if (rw != XIO_WRONLY && fdo != ttyfd) {
|
||||
if (Dup2(ttyfd, fdo) < 0) {
|
||||
Error3("dup2(%d, %d): %s", ttyfd, fdo, strerror(errno));
|
||||
return STAT_RETRYLATER; }
|
||||
return -1; }
|
||||
/*0 Info2("dup2(%d, %d)", ttyfd, fdo);*/
|
||||
}
|
||||
if ((rw == XIO_RDONLY || fdi != ttyfd) &&
|
||||
|
@ -466,7 +467,7 @@ int _xioopen_foxec(int xioflags, /* XIO_RDONLY etc. */
|
|||
if (fdi == rdpip[1]) { /* a conflict here */
|
||||
if ((tmpi = Dup(wrpip[0])) < 0) {
|
||||
Error2("dup(%d): %s", wrpip[0], strerror(errno));
|
||||
return STAT_RETRYLATER;
|
||||
return -1;
|
||||
}
|
||||
/*0 Info2("dup(%d) -> %d", wrpip[0], tmpi);*/
|
||||
rdpip[1] = tmpi;
|
||||
|
@ -474,7 +475,7 @@ int _xioopen_foxec(int xioflags, /* XIO_RDONLY etc. */
|
|||
if (fdo == wrpip[0]) { /* a conflict here */
|
||||
if ((tmpo = Dup(rdpip[1])) < 0) {
|
||||
Error2("dup(%d): %s", rdpip[1], strerror(errno));
|
||||
return STAT_RETRYLATER;
|
||||
return -1;
|
||||
}
|
||||
/*0 Info2("dup(%d) -> %d", rdpip[1], tmpo);*/
|
||||
wrpip[0] = tmpo;
|
||||
|
@ -483,7 +484,7 @@ int _xioopen_foxec(int xioflags, /* XIO_RDONLY etc. */
|
|||
if (rw != XIO_WRONLY && rdpip[1] != fdo) {
|
||||
if (Dup2(rdpip[1], fdo) < 0) {
|
||||
Error3("dup2(%d, %d): %s", rdpip[1], fdo, strerror(errno));
|
||||
return STAT_RETRYLATER;
|
||||
return -1;
|
||||
}
|
||||
Close(rdpip[1]);
|
||||
/*0 Info2("dup2(%d, %d)", rdpip[1], fdo);*/
|
||||
|
@ -492,7 +493,7 @@ int _xioopen_foxec(int xioflags, /* XIO_RDONLY etc. */
|
|||
if (rw != XIO_RDONLY && wrpip[0] != fdi) {
|
||||
if (Dup2(wrpip[0], fdi) < 0) {
|
||||
Error3("dup2(%d, %d): %s", wrpip[0], fdi, strerror(errno));
|
||||
return STAT_RETRYLATER;
|
||||
return -1;
|
||||
}
|
||||
Close(wrpip[0]);
|
||||
/*0 Info2("dup2(%d, %d)", wrpip[0], fdi);*/
|
||||
|
@ -509,13 +510,13 @@ int _xioopen_foxec(int xioflags, /* XIO_RDONLY etc. */
|
|||
if (rw != XIO_RDONLY && fdi != sv[1]) {
|
||||
if (Dup2(sv[1], fdi) < 0) {
|
||||
Error3("dup2(%d, %d): %s", sv[1], fdi, strerror(errno));
|
||||
return STAT_RETRYLATER; }
|
||||
return -1; }
|
||||
/*0 Info2("dup2(%d, %d)", sv[1], fdi);*/
|
||||
}
|
||||
if (rw != XIO_WRONLY && fdo != sv[1]) {
|
||||
if (Dup2(sv[1], fdo) < 0) {
|
||||
Error3("dup2(%d, %d): %s", sv[1], fdo, strerror(errno));
|
||||
return STAT_RETRYLATER; }
|
||||
return -1; }
|
||||
/*0 Info2("dup2(%d, %d)", sv[1], fdo);*/
|
||||
}
|
||||
if (fdi != sv[1] && fdo != sv[1]) {
|
||||
|
@ -530,36 +531,6 @@ int _xioopen_foxec(int xioflags, /* XIO_RDONLY etc. */
|
|||
applyopts(-1, *copts, PH_LATE);
|
||||
applyopts(-1, *copts, PH_LATE2);
|
||||
}
|
||||
|
||||
/* what to do with stderr? */
|
||||
if (withstderr) {
|
||||
/* handle it just like ordinary process output, i.e. copy output fd */
|
||||
if (!withfork) {
|
||||
if (Dup2(fdo, 2) < 0) {
|
||||
Error2("dup2(%d, 2): %s", fdo, strerror(errno));
|
||||
}
|
||||
/*0 Info1("dup2(%d, 2)", fdo);*/
|
||||
} else
|
||||
#if HAVE_PTY
|
||||
if (usepty) {
|
||||
if (Dup2(ttyfd, 2) < 0) {
|
||||
Error2("dup2(%d, 2): %s", ttyfd, strerror(errno));
|
||||
}
|
||||
/*0 Info1("dup2(%d, 2)", ttyfd);*/
|
||||
} else
|
||||
#endif /* HAVE_PTY */
|
||||
if (usepipes) {
|
||||
if (Dup2(/*rdpip[1]*/ fdo, 2) < 0) {
|
||||
Error2("dup2(%d, 2): %s", /*rdpip[1]*/ fdo, strerror(errno));
|
||||
}
|
||||
/*0 Info1("dup2(%d, 2)", rdpip[1]);*/
|
||||
} else {
|
||||
if (Dup2(sv[1], 2) < 0) {
|
||||
Error2("dup2(%d, 2): %s", sv[1], strerror(errno));
|
||||
}
|
||||
/*0 Info1("dup2(%d, 2)", sv[1]);*/
|
||||
}
|
||||
}
|
||||
_xioopen_setdelayeduser();
|
||||
/* set group before user - maybe you are not permitted afterwards */
|
||||
if (retropt_gidt(*copts, OPT_SETGID, &group) >= 0) {
|
||||
|
@ -568,6 +539,12 @@ int _xioopen_foxec(int xioflags, /* XIO_RDONLY etc. */
|
|||
if (retropt_uidt(*copts, OPT_SETUID, &user) >= 0) {
|
||||
Setuid(user);
|
||||
}
|
||||
if (withstderr) {
|
||||
*duptostderr = fdo;
|
||||
} else {
|
||||
*duptostderr = -1;
|
||||
}
|
||||
|
||||
return 0; /* indicate child process */
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* source: xio-progcall.h */
|
||||
/* Copyright Gerhard Rieger 2001-2006 */
|
||||
/* Copyright Gerhard Rieger 2001-2008 */
|
||||
/* Published under the GNU General Public License V.2, see file COPYING */
|
||||
|
||||
#ifndef __xio_progcall_h_included
|
||||
|
@ -21,8 +21,11 @@ extern const struct optdesc opt_sigquit;
|
|||
extern int _xioopen_foxec(int rw, /* O_RDONLY etc. */
|
||||
struct single *fd,
|
||||
unsigned groups,
|
||||
struct opt **opts
|
||||
struct opt **opts,
|
||||
int *duptostderr
|
||||
);
|
||||
extern int setopt_path(struct opt *opts, char **path);
|
||||
extern
|
||||
int _xioopen_redir_stderr(int fdo);
|
||||
|
||||
#endif /* !defined(__xio_progcall_h_included) */
|
||||
|
|
|
@ -31,10 +31,11 @@ static int xioopen_system(int argc, const char *argv[], struct opt *opts,
|
|||
) {
|
||||
int status;
|
||||
char *path = NULL;
|
||||
int duptostderr;
|
||||
int result;
|
||||
const char *string = argv[1];
|
||||
|
||||
status = _xioopen_foxec(xioflags, &fd->stream, groups, &opts);
|
||||
status = _xioopen_foxec(xioflags, &fd->stream, groups, &opts, &duptostderr);
|
||||
if (status < 0) return status;
|
||||
if (status == 0) { /* child */
|
||||
int numleft;
|
||||
|
@ -50,6 +51,11 @@ static int xioopen_system(int argc, const char *argv[], struct opt *opts,
|
|||
return STAT_NORETRY;
|
||||
}
|
||||
|
||||
/* only now redirect stderr */
|
||||
if (duptostderr >= 0) {
|
||||
diag_dup();
|
||||
Dup2(duptostderr, 2);
|
||||
}
|
||||
Info1("executing shell command \"%s\"", string);
|
||||
result = System(string);
|
||||
if (result != 0) {
|
||||
|
|
Loading…
Reference in a new issue