Option -S for controlling signals' logging

This commit is contained in:
Gerhard Rieger 2023-06-17 15:31:26 +02:00
parent 02483ff39e
commit eeabf31e04
4 changed files with 123 additions and 20 deletions

View file

@ -13,6 +13,10 @@ Features:
The number of warnings has been reduced, e.g.removing a non existing The number of warnings has been reduced, e.g.removing a non existing
file does in most cases no longer log a warning. file does in most cases no longer log a warning.
New option -S <mask> controls catching and logging of signals that are
not internally used by Socat.
Tests: SIGTERM_NOLOG SIG31_LOG
Corrections: Corrections:
When a sub process (EXEC, SYSTEM) terminated with exit code other than 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/ 0, its last sent data might have been lost depending on timing of read/

View file

@ -176,6 +176,15 @@ label(option_s)dit(bf(tt(-s)))
option, socat() is sloppy with errors and tries to continue. Even with this option, socat() is sloppy with errors and tries to continue. Even with this
option, socat will exit on fatals, and will abort connection attempts when option, socat will exit on fatals, and will abort connection attempts when
security checks failed. security checks failed.
label(option_S)dit(bf(tt(-S))tt(<signals-bitmap>))
Changes the set of signals that are caught by socat() just for printing an
log message. This catching is useful to get the information about the signal
into socat()s log, but prevents core dump or other standard actions. The
default set of these signals is SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT,
SIGBUS, SIGFPE, SIGSEGV, and SIGTERM; replace this set (0x89de on Linux)
with a bitmap (e.g., SIGFPE has value 8 and its bit is 0x0080).nl()
Note: Signals SIGHUP, SIGINT, SIGQUIT, SIGUSR1, SIGPIPE, SIGALRM, SIGTERM, and
SIGCHLD may be handled specially anyway.
label(option_t)dit(bf(tt(-t))tt(<timeout>)) label(option_t)dit(bf(tt(-t))tt(<timeout>))
When one channel has reached EOF, the write part of the other channel is shut When one channel has reached EOF, the write part of the other channel is shut
down. Then, socat() waits <timeout> [link(timeval)(TYPE_TIMEVAL)] seconds down. Then, socat() waits <timeout> [link(timeval)(TYPE_TIMEVAL)] seconds

41
socat.c
View file

@ -39,6 +39,7 @@ struct {
int sniffleft; /* -1 or an FD for teeing data arriving on xfd1 */ int sniffleft; /* -1 or an FD for teeing data arriving on xfd1 */
int sniffright; /* -1 or an FD for teeing data arriving on xfd2 */ int sniffright; /* -1 or an FD for teeing data arriving on xfd2 */
xiolock_t lock; /* a lock file */ xiolock_t lock; /* a lock file */
unsigned long log_sigs; /* signals to be caught just for logging */
} socat_opts = { } socat_opts = {
8192, /* bufsiz */ 8192, /* bufsiz */
false, /* verbose */ false, /* verbose */
@ -54,6 +55,7 @@ struct {
-1, /* sniffleft */ -1, /* sniffleft */
-1, /* sniffright */ -1, /* sniffright */
{ NULL, 0 }, /* lock */ { NULL, 0 }, /* lock */
1<<SIGHUP | 1<<SIGINT | 1<<SIGQUIT | 1<<SIGILL | 1<<SIGABRT | 1<<SIGBUS | 1<<SIGFPE | 1<<SIGSEGV | 1<<SIGTERM, /* log_sigs */
}; };
void socat_usage(FILE *fd); void socat_usage(FILE *fd);
@ -241,6 +243,18 @@ int main(int argc, const char *argv[]) {
break; break;
case 's': if (arg1[0][2]) { socat_opt_hint(stderr, arg1[0][1], arg1[0][2]); Exit(1); } case 's': if (arg1[0][2]) { socat_opt_hint(stderr, arg1[0][1], arg1[0][2]); Exit(1); }
diag_set_int('e', E_FATAL); break; diag_set_int('e', E_FATAL); break;
case 'S': /* do not catch signals */
if (arg1[0][2]) {
a = *arg1+2;
} else {
++arg1, --argc;
if ((a = *arg1) == NULL) {
Error("option -S requires an argument; use option \"-h\" for help");
Exit(1);
}
}
socat_opts.log_sigs = Strtoul(a, (char **)&a, 0, "-S");
break;
case 't': if (arg1[0][2]) { case 't': if (arg1[0][2]) {
a = *arg1+2; a = *arg1+2;
} else { } else {
@ -370,19 +384,17 @@ int main(int argc, const char *argv[]) {
{ {
struct sigaction act; struct sigaction act;
sigfillset(&act.sa_mask); int i, m;
sigfillset(&act.sa_mask); /* while in sighandler block all signals */
act.sa_flags = 0; act.sa_flags = 0;
act.sa_handler = socat_signal; act.sa_handler = socat_signal;
/* not sure which signals should be caught and print a message */ /* not sure which signals should be caught and print a message */
Sigaction(SIGHUP, &act, NULL); for (i = 0, m = 1; i < 8*sizeof(unsigned long); ++i, m <<= 1) {
Sigaction(SIGINT, &act, NULL); if (socat_opts.log_sigs & m) {
Sigaction(SIGQUIT, &act, NULL); Sigaction(i, &act, NULL);
Sigaction(SIGILL, &act, NULL); }
Sigaction(SIGABRT, &act, NULL); }
Sigaction(SIGBUS, &act, NULL);
Sigaction(SIGFPE, &act, NULL);
Sigaction(SIGSEGV, &act, NULL);
Sigaction(SIGTERM, &act, NULL);
} }
Signal(SIGPIPE, SIG_IGN); Signal(SIGPIPE, SIG_IGN);
@ -429,7 +441,7 @@ void socat_usage(FILE *fd) {
fputs(" -lf<logfile> log to file\n", fd); fputs(" -lf<logfile> log to file\n", fd);
fputs(" -ls log to stderr (default if no other log)\n", fd); fputs(" -ls log to stderr (default if no other log)\n", fd);
fputs(" -lm[facility] mixed log mode (stderr during initialization, then syslog)\n", fd); fputs(" -lm[facility] mixed log mode (stderr during initialization, then syslog)\n", fd);
fputs(" -lp<progname> set the program name used for logging\n", fd); fputs(" -lp<progname> set the program name used for logging and vars\n", fd);
fputs(" -lu use microseconds for logging timestamps\n", fd); fputs(" -lu use microseconds for logging timestamps\n", fd);
fputs(" -lh add hostname to log messages\n", fd); fputs(" -lh add hostname to log messages\n", fd);
fputs(" -v verbose text dump of data traffic\n", fd); fputs(" -v verbose text dump of data traffic\n", fd);
@ -438,6 +450,7 @@ void socat_usage(FILE *fd) {
fputs(" -R <file> raw dump of data flowing from right to left\n", fd); fputs(" -R <file> raw dump of data flowing from right to left\n", fd);
fputs(" -b<size_t> set data buffer size (8192)\n", fd); fputs(" -b<size_t> set data buffer size (8192)\n", fd);
fputs(" -s sloppy (continue on error)\n", fd); fputs(" -s sloppy (continue on error)\n", fd);
fputs(" -S<sigmask> log these signals, override default\n", fd);
fputs(" -t<timeout> wait seconds before closing second channel\n", fd); fputs(" -t<timeout> wait seconds before closing second channel\n", fd);
fputs(" -T<timeout> total inactivity timeout in seconds\n", fd); fputs(" -T<timeout> total inactivity timeout in seconds\n", fd);
fputs(" -u unidirectional mode (left to right)\n", fd); fputs(" -u unidirectional mode (left to right)\n", fd);
@ -1593,11 +1606,7 @@ void socat_signal(int signum) {
diag_in_handler = 1; diag_in_handler = 1;
Notice1("socat_signal(): handling signal %d", signum); Notice1("socat_signal(): handling signal %d", signum);
switch (signum) { switch (signum) {
case SIGILL: default:
case SIGABRT:
case SIGBUS:
case SIGFPE:
case SIGSEGV:
diag_immediate_exit = 1; diag_immediate_exit = 1;
case SIGQUIT: case SIGQUIT:
case SIGPIPE: case SIGPIPE:

89
test.sh
View file

@ -4267,7 +4267,7 @@ case "$TESTS" in
*%$N%*|*%functions%*|*%$NAME%*) *%$N%*|*%functions%*|*%$NAME%*)
if ! eval $NUMCOND; then : if ! eval $NUMCOND; then :
elif ! F=$(testfeats STDIO EXEC); then elif ! F=$(testfeats STDIO EXEC); then
$PRINTF "test $F_n $TEST... ${YELLOW}Feature $F not available in $SOCAT${NORMAL}\n" $N $PRINTF "test $F_n $TEST... ${YELLOW}Feature $F not configured in $SOCAT${NORMAL}\n" $N
numCANT=$((numCANT+1)) numCANT=$((numCANT+1))
listCANT="$listCANT $N" listCANT="$listCANT $N"
elif ! A=$(testaddrs STDIO EXEC); then elif ! A=$(testaddrs STDIO EXEC); then
@ -9271,7 +9271,7 @@ case "$TESTS" in
TEST="$NAME: UDP/IPv6 multicast" TEST="$NAME: UDP/IPv6 multicast"
if ! eval $NUMCOND; then :; if ! eval $NUMCOND; then :;
elif ! f=$(testfeats ip6 udp); then elif ! f=$(testfeats ip6 udp); then
$PRINTF "test $F_n $TEST... ${YELLOW}Feature $f not available in $SOCAT${NORMAL}\n" $N $PRINTF "test $F_n $TEST... ${YELLOW}Feature $f not configured in $SOCAT${NORMAL}\n" $N
numCANT=$((numCANT+1)) numCANT=$((numCANT+1))
listCANT="$listCANT $N" listCANT="$listCANT $N"
elif ! a=$(testaddrs - STDIO UDP6-RECV UDP6-SENDTO); then elif ! a=$(testaddrs - STDIO UDP6-RECV UDP6-SENDTO); then
@ -16127,7 +16127,7 @@ TEST="$NAME: ${KEYW}-RECVFROM with fork option"
# When the second record is stored before the first one the test succeeded. # When the second record is stored before the first one the test succeeded.
if ! eval $NUMCOND; then :; if ! eval $NUMCOND; then :;
elif ! F=$(testfeats $FEAT STDIO SYSTEM); then elif ! F=$(testfeats $FEAT STDIO SYSTEM); then
$PRINTF "test $F_n $TEST... ${YELLOW}Feature $F not available${NORMAL}\n" $N $PRINTF "test $F_n $TEST... ${YELLOW}Feature $F not configured${NORMAL}\n" $N
numCANT=$((numCANT+1)) numCANT=$((numCANT+1))
listCANT="$listCANT $N" listCANT="$listCANT $N"
elif ! A=$(testaddrs - STDIO SYSTEM $PROTO-RECVFROM $PROTO-SENDTO); then elif ! A=$(testaddrs - STDIO SYSTEM $PROTO-RECVFROM $PROTO-SENDTO); then
@ -16205,6 +16205,87 @@ UDP6 UDP udp4 [::1] PORT
UNIX unix unix $td/test\$N.server - UNIX unix unix $td/test\$N.server -
" "
# Test if option -S turns off logging of SIGTERM
NAME=SIGTERM_NOLOG
case "$TESTS" in
*%$N%*|*%functions%*|*%signal%*|*%$NAME%*)
TEST="$NAME: Option -S can turn off logging of SIGTERM"
# Start Socat with option -S 0x0000, kill it with SIGTERM
# When no logging entry regarding this signal is there, the test succeeded
if ! eval $NUMCOND; then :;
else
tf="$td/test$N.stdout"
te="$td/test$N.stderr"
tdiff="$td/test$N.diff"
da="test$N $(date) $RANDOM"
newport tcp4 # or whatever proto, or drop this line
CMD0="$TRACE $SOCAT $opts -S 0x0000 PIPE PIPE"
printf "test $F_n $TEST... " $N
$CMD0 >/dev/null 2>"${te}0" &
pid0=$!
relsleep 1 # give process time to start
kill -TERM $pid0 2>/dev/null; wait
if ! grep -q "exiting on signal" ${te}0; then
$PRINTF "$OK\n"
if [ "$VERBOSE" ]; then echo "$CMD0 &"; fi
if [ "$DEBUG" ]; then cat "${te}0" >&2; fi
if [ "$DEBUG" ]; then echo "kill -TERM <pid>" >&2; fi
numOK=$((numOK+1))
else
$PRINTF "$FAILED\n"
echo "$CMD0 &"
cat "${te}0" >&2
echo "kill -TERM <pid>" >&2
numFAIL=$((numFAIL+1))
listFAIL="$listFAIL $N"
namesFAIL="$namesFAIL $NAME"
fi
fi # NUMCOND
;;
esac
N=$((N+1))
# Test if option -S turns on logging of signal 31
NAME=SIG31_LOG
case "$TESTS" in
*%$N%*|*%functions%*|*%signal%*|*%$NAME%*)
TEST="$NAME: Option -S can turn on logging of signal 31"
# Start Socat with option -S 0x80000000, kill it with -31
# When a logging entry regarding this signal is there, the test succeeded
if ! eval $NUMCOND; then :;
else
tf="$td/test$N.stdout"
te="$td/test$N.stderr"
tdiff="$td/test$N.diff"
da="test$N $(date) $RANDOM"
newport tcp4 # or whatever proto, or drop this line
CMD0="$TRACE $SOCAT $opts -S 0x80000000 PIPE PIPE"
printf "test $F_n $TEST... " $N
$CMD0 >/dev/null 2>"${te}0" &
pid0=$!
relsleep 1 # give process time to start
kill -31 $pid0 2>/dev/null; wait
if grep -q "exiting on signal" ${te}0; then
$PRINTF "$OK\n"
if [ "$VERBOSE" ]; then echo "$CMD0 &"; fi
if [ "$DEBUG" ]; then cat "${te}0" >&2; fi
if [ "$DEBUG" ]; then echo "kill -31 <pid>" >&2; fi
numOK=$((numOK+1))
else
$PRINTF "$FAILED\n"
echo "$CMD0 &"
cat "${te}0" >&2
echo "kill -31 <pid>" >&2
numFAIL=$((numFAIL+1))
listFAIL="$listFAIL $N"
namesFAIL="$namesFAIL $NAME"
fi
fi # NUMCOND
;;
esac
N=$((N+1))
# end of common tests # end of common tests
################################################################################## ##################################################################################
@ -16355,7 +16436,7 @@ elif ! $(type systemd-socket-activate >/dev/null 2>&1); then
numCANT=$((numCANT+1)) numCANT=$((numCANT+1))
listCANT="$listCANT $N" listCANT="$listCANT $N"
elif ! F=$(testfeats STDIO IP4 TCP PIPE); then elif ! F=$(testfeats STDIO IP4 TCP PIPE); then
$PRINTF "test $F_n $TEST... ${YELLOW}Feature $F not available in $SOCAT${NORMAL}\n" $N $PRINTF "test $F_n $TEST... ${YELLOW}Feature $F not configured in $SOCAT${NORMAL}\n" $N
numCANT=$((numCANT+1)) numCANT=$((numCANT+1))
listCANT="$listCANT $N" listCANT="$listCANT $N"
elif ! A=$(testaddrs - TCP4 TCP4-LISTEN PIPE); then elif ! A=$(testaddrs - TCP4 TCP4-LISTEN PIPE); then