diff --git a/CHANGES b/CHANGES index 6cbac90..ff7b477 100644 --- a/CHANGES +++ b/CHANGES @@ -53,6 +53,10 @@ Features: Socat now installs as socat1 and is referenced by symbolic link socat, same with man page (socat1.1 by socat.1) + New option children-shutup[=1|2...] decreases severity of log + messages in LISTEN and CONNECT type sub processes. + Test: CHILDREN_SHUTUP + Corrections: When a sub process (EXEC, SYSTEM) terminated with exit code other than 0, its last sent data might have been lost depending on timing of read/ @@ -76,7 +80,7 @@ Corrections: In previous Socat versions errors on shutdown() were ignored (info level). - Now Socat handles EPIPE and ECONNRESET as error to indicate possible + Now Socat handles EPIPE and ECONNRESET as errors to indicate possible failure of data transfer. Coding: diff --git a/doc/socat.yo b/doc/socat.yo index d206051..40555e9 100644 --- a/doc/socat.yo +++ b/doc/socat.yo @@ -1628,6 +1628,8 @@ label(OPTION_COOL_WRITE)dit(bf(tt(cool-write[=]))) abort the connection. Use this option only with option link(fork)(OPTION_FORK) because otherwise it might cause socat() to exit with code 0 even on failure.nl() + This option is deprecated, consider using + link(option children-shutup)(OPTION_CHILDRED_SHUTUP) instead. label(OPTION_END_CLOSE)dit(bf(tt(end-close[=]))) Changes the (address dependent) method of ending a connection to just close the file descriptors. This is useful when the connection is to be reused by @@ -2514,16 +2516,15 @@ label(OPTION_BACKLOG)dit(bf(tt(backlog=))) label(OPTION_ACCEPT_TIMEOUT)dit(bf(tt(accept-timeout=))) End waiting for a connection after [link(timeval)(TYPE_TIMEVAL)] with error status. -label(OPTION_MAX_CHILDREN)dit(bf(tt(max-children=))) - Limits the number of concurrent child processes [link(int)(TYPE_INT)]. - Default is no limit. enddit() startdit()enddit()nl() label(GROUP_CHILD)em(bf(CHILD option group)) -Options for addresses with multiple connections via child processes. +Addresses of LISTEN and CONNECT type take the +link(fork)(OPTION_FORK) option to handle multiple connections via child +processes. startdit() label(OPTION_FORK)dit(bf(tt(fork))) After establishing a connection, handles its channel in a child process and @@ -2537,6 +2538,17 @@ label(OPTION_FORK)dit(bf(tt(fork))) inherited by the child process.nl() On some operating systems (e.g. FreeBSD) this option does not work for UDP-LISTEN addresses.nl() +label(OPTION_MAX_CHILDREN)dit(bf(tt(max-children=))) + Limits the number of concurrent child processes [link(int)(TYPE_INT)]. + Default is no limit. +label(OPTION_CHILDRED_SHUTUP)dit(bf(tt(children-shutup[=1|2|..]))) + Decreases the severity of log messages produced by child processes. For + example, with value 1 notices are logged as info (or dropped depending on + link(option -dX)(option_d)), and errors are logged as warnings but still + cause termination of the child process.nl() + This option is intended to reduce logging of high volume servers or + proxies.nl() + This option succeeds link(option cool-write)(OPTION_COOL_WRITE). enddit() startdit()enddit()nl() diff --git a/error.c b/error.c index 863f4d8..46d2714 100644 --- a/error.c +++ b/error.c @@ -29,6 +29,7 @@ int syslevel[] = { struct diag_opts { const char *progname; int msglevel; + int shutup; /* decrease msglevel by this value */ int exitlevel; int syslog; FILE *logfile; @@ -45,7 +46,7 @@ static void _diag_exit(int status); struct diag_opts diagopts = - { NULL, E_WARN, E_ERROR, 0, NULL, LOG_DAEMON, false, 0, false, NULL, true } ; + { NULL, E_WARN, 0, E_ERROR, 0, NULL, LOG_DAEMON, false, 0, false, NULL, true } ; static void msg2( #if HAVE_CLOCK_GETTIME @@ -215,6 +216,10 @@ void diag_set_int(char what, int arg) { diagopts.hostname = strdup(ubuf.nodename); } break; + case 'u': + diagopts.shutup = arg; + diagopts.exitlevel -= arg; + break; default: msg(E_ERROR, "unknown diagnostic option %c", what); } } @@ -283,7 +288,12 @@ void msg(int level, const char *format, ...) { diag_flush(); } - if (level < diagopts.msglevel) { return; } + level -= diagopts.shutup; /* decrease severity of messages? */ + + /* Just ignore this call when level too low for both logging and exiting */ + if (level < diagopts.msglevel && level < diagopts.exitlevel) + return; + va_start(ap, format); /* we do only a minimum in the outer parts which may run in a signal handler @@ -299,7 +309,10 @@ void msg(int level, const char *format, ...) { #endif diag_dgram.level = level; diag_dgram.exitcode = diagopts.exitstatus; - vsnprintf_r(diag_dgram.text, sizeof(diag_dgram.text), format, ap); + if (level >= diagopts.msglevel) + vsnprintf_r(diag_dgram.text, sizeof(diag_dgram.text), format, ap); + else + diag_dgram.text[0] = '\0'; if (diagopts.signalsafe && diag_in_handler && !diag_immediate_msg) { send(diag_sock_send, &diag_dgram, sizeof(diag_dgram)-TEXTLEN + strlen(diag_dgram.text)+1, 0 /* for canonical reasons */ @@ -337,9 +350,10 @@ void msg2( struct tm struct_tm; #endif #define MSGLEN 512 - char buff[MSGLEN+2], *bufp = buff, *syslp; + char buff[MSGLEN+2], *bufp = buff, *syslp = NULL; size_t bytes; + if (text[0] != '\0') { #if HAVE_CLOCK_GETTIME epoch = now->tv_sec; #elif HAVE_PROTOTYPE_LIB_gettimeofday @@ -391,8 +405,9 @@ void msg2( bufp = strchr(bufp, '\0'); strcpy(bufp, "\n"); _msg(level, buff, syslp); + } if (level >= diagopts.exitlevel) { - if (E_NOTICE >= diagopts.msglevel) { + if (E_NOTICE >= diagopts.msglevel && text[0] != '\0') { if ((syslp - buff) + 16 > MSGLEN+1) syslp = buff + MSGLEN - 15; snprintf_r(syslp, 16, "N exit(%d)\n", exitcode?exitcode:(diagopts.exitstatus?diagopts.exitstatus:1)); diff --git a/test.sh b/test.sh index 1ab7ef2..60f5054 100755 --- a/test.sh +++ b/test.sh @@ -15521,6 +15521,67 @@ esac N=$((N+1)) +# Test the children-shutup option +NAME=CHILDREN_SHUTUP +case "$TESTS" in +*%$N%*|*%functions%*|*%exec%*|*%fork%*|*%socket%*|*%unix%*|*%$NAME%*) +TEST="$NAME: test the children-shutup option" +# Run a UNIX domain listening server with options fork and children-shutup, and +# an TCP client to invalid port that will fail. +# Connect to the server and check if it logs the connect failure as warning. +if ! eval $NUMCOND; then :; +elif ! F=$(testfeats UNIX LISTEN EXEC FILE); then + $PRINTF "test $F_n $TEST... ${YELLOW}Feature $F not available${NORMAL}\n" $N + numCANT=$((numCANT+1)) + listCANT="$listCANT $N" +elif ! A=$(testaddrs UNIX-LISTEN TCP4 FILE UNIX-CONNECT); then + $PRINTF "test $F_n $TEST... ${YELLOW}Address $A not available${NORMAL}\n" $N + numCANT=$((numCANT+1)) + listCANT="$listCANT $N" +elif ! o=$(testoptions fork children-shutup) >/dev/null; then + $PRINTF "test $F_n $TEST... ${YELLOW}Option $o not available${NORMAL}\n" $N + numCANT=$((numCANT+1)) + listCANT="$listCANT $N" +else +newport tcp4 +ts="$td/test$N.sock" +tf="$td/test$N.stdout" +te="$td/test$N.stderr" +tdiff="$td/test$N.diff" +da="test$N $(date) $RANDOM" +CMD0="$TRACE $SOCAT $opts UNIX-LISTEN:$ts,fork,children-shutup TCP4:localhost:$PORT" +CMD1="$TRACE $SOCAT $opts -u FILE:/dev/null UNIX-CONNECT:$ts" +printf "test $F_n $TEST... " $N +$CMD0 >/dev/null 2>"${te}0" & +pid0=$! +waitunixport $ts 1 +$CMD1 2>"${te}1" +rc1=$? +kill $pid0 2>/dev/null; wait +relsleep 1 # child process might need more time +if grep -q " W connect" ${te}0; then + $PRINTF "$OK\n" + if [ "$VERBOSE" ]; then echo "$CMD0 &"; fi + if [ "$DEBUG" ]; then cat "${te}0" >&2; fi + if [ "$VERBOSE" ]; then echo "$CMD1"; fi + if [ "$DEBUG" ]; then cat "${te}1" >&2; fi + numOK=$((numOK+1)) +else + $PRINTF "$FAILED\n" + echo "$CMD0 &" + cat "${te}0" >&2 + echo "$CMD1" + cat "${te}1" >&2 + numFAIL=$((numFAIL+1)) + listFAIL="$listFAIL $N" + namesFAIL="$namesFAIL $NAME" +fi +fi # NUMCOND + ;; +esac +N=$((N+1)) + + # end of common tests ################################################################################## diff --git a/xio-ipapp.c b/xio-ipapp.c index 30f6f8f..b6c7866 100644 --- a/xio-ipapp.c +++ b/xio-ipapp.c @@ -110,7 +110,7 @@ int xioopen_ipapp_connect(int argc, const char *argv[], struct opt *opts, level = E_WARN; /* most users won't expect a problem here, so Notice is too weak */ } - while ((pid = xio_fork(false, level)) < 0) { + while ((pid = xio_fork(false, level, xfd->para.socket.shutup)) < 0) { if (xfd->forever || --xfd->retry) { Nanosleep(&xfd->intervall, NULL); continue; } diff --git a/xio-listen.c b/xio-listen.c index 84771f3..4392242 100644 --- a/xio-listen.c +++ b/xio-listen.c @@ -19,7 +19,8 @@ /***** LISTEN options *****/ const struct optdesc opt_backlog = { "backlog", NULL, OPT_BACKLOG, GROUP_LISTEN, PH_LISTEN, TYPE_INT, OFUNC_SPEC }; const struct optdesc opt_fork = { "fork", NULL, OPT_FORK, GROUP_CHILD, PH_PASTACCEPT, TYPE_BOOL, OFUNC_SPEC }; -const struct optdesc opt_max_children = { "max-children", NULL, OPT_MAX_CHILDREN, GROUP_CHILD, PH_PASTACCEPT, TYPE_INT, OFUNC_SPEC }; +const struct optdesc opt_max_children = { "max-children", NULL, OPT_MAX_CHILDREN, GROUP_CHILD, PH_PASTACCEPT, TYPE_INT, OFUNC_SPEC }; +const struct optdesc opt_children_shutup = { "children-shutup", "child-shutup", OPT_CHILDREN_SHUTUP, GROUP_CHILD, PH_PASTACCEPT, TYPE_INT, OFUNC_OFFSET, XIO_OFFSETOF(para.socket.shutup) }; /**/ #if (WITH_UDP || WITH_TCP) const struct optdesc opt_range = { "range", NULL, OPT_RANGE, GROUP_RANGE, PH_ACCEPT, TYPE_STRING, OFUNC_SPEC }; @@ -343,7 +344,10 @@ int _xioopen_listen(struct single *xfd, int xioflags, struct sockaddr *us, sockl sigaddset(&mask_sigchld, SIGCHLD); Sigprocmask(SIG_BLOCK, &mask_sigchld, NULL); - if ((pid = xio_fork(false, level==E_ERROR?level:E_WARN)) < 0) { + if ((pid = + xio_fork(false, level==E_ERROR?level:E_WARN, + xfd->para.socket.shutup)) + < 0) { Close(xfd->fd); Sigprocmask(SIG_UNBLOCK, &mask_sigchld, NULL); return STAT_RETRYLATER; diff --git a/xio-listen.h b/xio-listen.h index 58bc53d..028d157 100644 --- a/xio-listen.h +++ b/xio-listen.h @@ -8,6 +8,7 @@ extern const struct optdesc opt_backlog; extern const struct optdesc opt_fork; extern const struct optdesc opt_max_children; +extern const struct optdesc opt_children_shutup; extern const struct optdesc opt_range; extern const struct optdesc opt_accept_timeout; diff --git a/xio-openssl.c b/xio-openssl.c index acd54f0..e83c203 100644 --- a/xio-openssl.c +++ b/xio-openssl.c @@ -409,7 +409,7 @@ static int if (xfd->forever || xfd->retry) { level = E_WARN; } - while ((pid = xio_fork(false, level)) < 0) { + while ((pid = xio_fork(false, level, xfd->para.socket.shutup)) < 0) { if (xfd->forever || --xfd->retry) { Nanosleep(&xfd->intervall, NULL); continue; } diff --git a/xio-progcall.c b/xio-progcall.c index 82822dc..42e407e 100644 --- a/xio-progcall.c +++ b/xio-progcall.c @@ -385,7 +385,7 @@ int _xioopen_foxec(int xioflags, /* XIO_RDONLY etc. */ if (withfork) { Socketpair(PF_UNIX, SOCK_STREAM, 0, trigger); - pid = xio_fork(true, E_ERROR); + pid = xio_fork(true, E_ERROR, 0); if (pid < 0) { return -1; } diff --git a/xio-proxy.c b/xio-proxy.c index 941f1fe..0e7b9b4 100644 --- a/xio-proxy.c +++ b/xio-proxy.c @@ -199,7 +199,7 @@ static int xioopen_proxy_connect(int argc, const char *argv[], struct opt *opts, if (xfd->forever || xfd->retry) { level = E_WARN; } - while ((pid = xio_fork(false, level)) < 0) { + while ((pid = xio_fork(false, level, xfd->para.socket.shutup)) < 0) { if (xfd->forever || --xfd->retry) { Nanosleep(&xfd->intervall, NULL); continue; } diff --git a/xio-socket.c b/xio-socket.c index 612a3c5..2d0c9f8 100644 --- a/xio-socket.c +++ b/xio-socket.c @@ -993,7 +993,7 @@ int xioopen_connect(struct single *xfd, union sockaddr_union *us, size_t uslen, so Notice is too weak */ } - while ((pid = xio_fork(false, level)) < 0) { + while ((pid = xio_fork(false, level, xfd->para.socket.shutup)) < 0) { --xfd->retry; if (xfd->forever || xfd->retry) { dropopts(opts, PH_ALL); opts = copyopts(opts0, GROUP_ALL); @@ -1287,7 +1287,7 @@ int _xioopen_dgram_recvfrom(struct single *xfd, int xioflags, Error1("socketpair(PF_UNIX, SOCK_STREAM, 0, ...): %s", strerror(errno)); } - if ((pid = xio_fork(false, level)) < 0) { + if ((pid = xio_fork(false, level, xfd->para.socket.shutup)) < 0) { Close(trigger[0]); Close(trigger[1]); Close(xfd->fd); diff --git a/xio-socks.c b/xio-socks.c index 519f80c..a9c8840 100644 --- a/xio-socks.c +++ b/xio-socks.c @@ -175,7 +175,7 @@ static int xioopen_socks4_connect(int argc, const char *argv[], struct opt *opts level = E_WARN; /* most users won't expect a problem here, so Notice is too weak */ } - while ((pid = xio_fork(false, level)) < 0) { + while ((pid = xio_fork(false, level, xfd->para.socket.shutup)) < 0) { if (xfd->forever || --xfd->retry) { Nanosleep(&xfd->intervall, NULL); continue; diff --git a/xio-udp.c b/xio-udp.c index becef73..095fe16 100644 --- a/xio-udp.c +++ b/xio-udp.c @@ -214,7 +214,7 @@ int _xioopen_ipdgram_listen(struct single *sfd, sockaddr_info(&them->soa, themlen, infobuff, sizeof(infobuff))); if (dofork) { - pid = xio_fork(false, E_ERROR); + pid = xio_fork(false, E_ERROR, sfd->para.socket.shutup); if (pid < 0) { return STAT_RETRYLATER; } diff --git a/xio-unix.c b/xio-unix.c index 12a6c57..17d41bc 100644 --- a/xio-unix.c +++ b/xio-unix.c @@ -315,7 +315,7 @@ static int xioopen_unix_connect(int argc, const char *argv[], struct opt *opts, so Notice is too weak */ } - while ((pid = xio_fork(false, level)) < 0) { + while ((pid = xio_fork(false, level, xfd->para.socket.shutup)) < 0) { --xfd->retry; if (xfd->forever || xfd->retry) { dropopts(opts, PH_ALL); opts = copyopts(opts0, GROUP_ALL); diff --git a/xio.h b/xio.h index ace56b1..2660db6 100644 --- a/xio.h +++ b/xio.h @@ -223,6 +223,7 @@ typedef struct single { #if _WITH_IP4 || _WITH_IP6 struct para_ip ip; #endif /* _WITH_IP4 || _WITH_IP6 */ + int shutup; /* up to here, keep consistent copy in openssl part !!! */ #if WITH_UNIX struct { @@ -263,6 +264,7 @@ typedef struct single { #if _WITH_IP4 || _WITH_IP6 struct para_ip ip; #endif /* _WITH_IP4 || _WITH_IP6 */ + int shutup; /* end of the para.socket structure copy */ SSL_CTX* ctx; /* for freeing on close */ SSL *ssl; @@ -440,7 +442,7 @@ extern int num_child; extern int xioinitialize(void); extern int xioinitialize2(void); -extern pid_t xio_fork(bool subchild, int level); +extern pid_t xio_fork(bool subchild, int level, int shutup); extern int xio_forked_inchild(void); extern int xiosetopt(char what, const char *arg); extern int xioinqopt(char what, char *arg, size_t n); diff --git a/xioinitialize.c b/xioinitialize.c index 4e1412f..880f828 100644 --- a/xioinitialize.c +++ b/xioinitialize.c @@ -205,7 +205,10 @@ int xio_forked_inchild(void) { /* subchild != 0 means that the current process is already a child process of the master process and thus the new sub child process should not set the SOCAT_PID variable */ -pid_t xio_fork(bool subchild, int level) { +pid_t xio_fork(bool subchild, + int level, /* log level */ + int shutup) /* decrease log level in child process */ +{ pid_t pid; const char *forkwaitstring; int forkwaitsecs = 0; @@ -240,6 +243,7 @@ pid_t xio_fork(bool subchild, int level) { if (xio_forked_inchild() != 0) { Exit(1); } + diag_set_int('u', shutup); return 0; } diff --git a/xioopts.c b/xioopts.c index 03c1243..67017ba 100644 --- a/xioopts.c +++ b/xioopts.c @@ -289,6 +289,7 @@ const struct optname optionnames[] = { IF_OPENSSL("cert", &opt_openssl_certificate) IF_OPENSSL("certificate", &opt_openssl_certificate) IF_TERMIOS("cfmakeraw", &opt_termios_cfmakeraw) + IF_ANY ("children-shutup", &opt_children_shutup) IF_ANY ("chroot", &opt_chroot) IF_ANY ("chroot-early", &opt_chroot_early) /*IF_TERMIOS("cibaud", &opt_cibaud)*/ diff --git a/xioopts.h b/xioopts.h index 4b95a6d..dd98ae7 100644 --- a/xioopts.h +++ b/xioopts.h @@ -247,6 +247,7 @@ enum e_optcode { # endif OPT_BSDLY, /* termios.c_oflag */ #endif + OPT_CHILDREN_SHUTUP, OPT_CHROOT, /* chroot() past file system access */ OPT_CHROOT_EARLY, /* chroot() before file system access */ /*OPT_CIBAUD,*/ /* termios.c_cflag */