New option children-shutup

This commit is contained in:
Gerhard Rieger 2023-10-26 18:50:29 +02:00
parent fe4444a70b
commit 8c9b185890
18 changed files with 128 additions and 23 deletions

View file

@ -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:

View file

@ -1628,6 +1628,8 @@ label(OPTION_COOL_WRITE)dit(bf(tt(cool-write[=<bool>])))
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[=<bool>])))
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=<count>)))
label(OPTION_ACCEPT_TIMEOUT)dit(bf(tt(accept-timeout=<seconds>)))
End waiting for a connection after <seconds> [link(timeval)(TYPE_TIMEVAL)]
with error status.
label(OPTION_MAX_CHILDREN)dit(bf(tt(max-children=<count>)))
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=<count>)))
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()

25
error.c
View file

@ -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));

61
test.sh
View file

@ -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
##################################################################################

View file

@ -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;
}

View file

@ -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;

View file

@ -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;

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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);

View file

@ -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;

View file

@ -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;
}

View file

@ -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);

4
xio.h
View file

@ -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);

View file

@ -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;
}

View file

@ -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)*/

View file

@ -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 */