diff --git a/CHANGES b/CHANGES index 4a635b1..df0bf2a 100644 --- a/CHANGES +++ b/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: diff --git a/VERSION b/VERSION index 475d203..bb1254e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -"1.6.0.1+ip4bind+recvfromfork+x64" +"1.6.0.1+ip4bind+recvfromfork+x64+execstderr" diff --git a/error.c b/error.c index a3adf6c..6284177 100644 --- a/error.c +++ b/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; +} diff --git a/error.h b/error.h index 537987f..45168ed 100644 --- a/error.h +++ b/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) */ diff --git a/test.sh b/test.sh index 9d2fff7..60ba633 100755 --- a/test.sh +++ b/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 diff --git a/xio-exec.c b/xio-exec.c index d562f12..0e2f09d 100644 --- a/xio-exec.c +++ b/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 */ diff --git a/xio-progcall.c b/xio-progcall.c index 1b9489f..0c9f8ed 100644 --- a/xio-progcall.c +++ b/xio-progcall.c @@ -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 */ } diff --git a/xio-progcall.h b/xio-progcall.h index 09fe607..706331f 100644 --- a/xio-progcall.h +++ b/xio-progcall.h @@ -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) */ diff --git a/xio-system.c b/xio-system.c index 71dd8eb..39a5193 100644 --- a/xio-system.c +++ b/xio-system.c @@ -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) {