mirror of
https://github.com/moparisthebest/socat
synced 2024-12-21 22:48:48 -05:00
RECVFROM addresses with FORK option hung after processing the first packet
This commit is contained in:
parent
fe1337fe5f
commit
d086001911
3
CHANGES
3
CHANGES
@ -4,6 +4,9 @@ corrections:
|
||||
with the first received packet an error occurred:
|
||||
socket_init(): unknown address family 0
|
||||
|
||||
RECVFROM addresses with FORK option hung after processing the first
|
||||
packet.
|
||||
|
||||
####################### V 1.6.0.1:
|
||||
|
||||
new features:
|
||||
|
48
test.sh
48
test.sh
@ -8195,6 +8195,54 @@ PORT=$((PORT+1))
|
||||
N=$((N+1))
|
||||
|
||||
|
||||
# there was a bug in *-recvfrom with fork: due to an error in the appropriate
|
||||
# signal handler the master process would hang after forking off the first
|
||||
# child process.
|
||||
NAME=UDP4RECVFROM_FORK
|
||||
case "$TESTS" in
|
||||
*%functions%*|*%ip4%*|*%udp%*|*%dgram%*|*%$NAME%*)
|
||||
TEST="$NAME: test if UDP4-RECVFROM handles more than one packet"
|
||||
# idea: run a UDP4-RECVFROM process with fork and -T. Send it one packet;
|
||||
# send it a second packet and check if this is processed properly. If yes, the
|
||||
# test succeeded.
|
||||
tf="$td/test$N.stdout"
|
||||
te="$td/test$N.stderr"
|
||||
tdiff="$td/test$N.diff"
|
||||
tsp=$PORT
|
||||
ts="$LOCALHOST:$tsp"
|
||||
da=$(date)
|
||||
CMD1="$SOCAT $opts -T 2 UDP4-RECVFROM:$tsp,reuseaddr,fork PIPE"
|
||||
CMD2="$SOCAT $opts -T 1 - UDP4-SENDTO:$ts"
|
||||
printf "test $F_n $TEST... " $N
|
||||
$CMD1 >/dev/null 2>"${te}1" &
|
||||
pid1=$!
|
||||
waitudp4port $tsp 1
|
||||
echo "$da" |$CMD2 >/dev/null 2>>"${te}2" # this should always work
|
||||
rc2a=$?
|
||||
sleep 1
|
||||
echo "$da" |$CMD2 >"$tf" 2>>"${te}3" # this would fail when bug
|
||||
rc2b=$?
|
||||
kill $pid1 2>/dev/null; wait
|
||||
if [ $rc2b -ne 0 ]; then
|
||||
$PRINTF "$NO_RESULT\n"
|
||||
numCANT=$((numCANT+1))
|
||||
elif ! echo "$da" |diff - "$tf" >"$tdiff"; then
|
||||
$PRINTF "$FAILED: $SOCAT:\n"
|
||||
echo "$CMD1 &"
|
||||
echo "$CMD2"
|
||||
cat "${te}1" "${te}2" "${te}3"
|
||||
cat "$tdiff"
|
||||
numFAIL=$((numFAIL+1))
|
||||
else
|
||||
$PRINTF "$OK\n"
|
||||
if [ -n "$debug" ]; then cat "${te}1" "${te}2" "${te}3"; fi
|
||||
numOK=$((numOK+1))
|
||||
fi ;;
|
||||
esac
|
||||
PORT=$((PORT+1))
|
||||
N=$((N+1))
|
||||
|
||||
|
||||
echo "summary: $((N-1)) tests; $numOK ok, $numFAIL failed, $numCANT could not be performed"
|
||||
|
||||
if [ "$numFAIL" -gt 0 ]; then
|
||||
|
75
xio-socket.c
75
xio-socket.c
@ -509,8 +509,26 @@ int _xioopen_dgram_sendto(/* them is already in xfd->peersa */
|
||||
}
|
||||
|
||||
|
||||
static pid_t xio_waitingfor;
|
||||
static bool xio_hashappened;
|
||||
/* when the recvfrom address (with option fork) receives a packet it keeps this
|
||||
packet in the IP stacks input queue and forks a sub process. The sub process
|
||||
then reads this packet for processing its data.
|
||||
There is a problem because the parent process would find the same packet
|
||||
again if it calls select()/poll() before the client process reads the
|
||||
packet.
|
||||
To solve this problem we implement the following mechanism:
|
||||
The sub process sends a SIGUSR1 when it has read the packet (or a SIGCHLD if
|
||||
it dies before). The parent process waits until it receives that signal and
|
||||
only then continues to listen.
|
||||
To prevent a signal from another process to trigger our loop, we pass the
|
||||
pid of the sub process to the signal handler in xio_waitingfor. The signal
|
||||
handler sets xio_hashappened if the pid matched.
|
||||
*/
|
||||
static pid_t xio_waitingfor; /* info from recv loop to signal handler:
|
||||
indicates the pid that of the child process
|
||||
that should send us the USR1 signal */
|
||||
static bool xio_hashappened; /* info from signal handler to loop: child
|
||||
process has read ("consumed") the packet */
|
||||
/* this is the signal handler for USR1 and CHLD */
|
||||
void xiosigaction_hasread(int signum, siginfo_t *siginfo, void *ucontext) {
|
||||
pid_t pid;
|
||||
int _errno;
|
||||
@ -519,30 +537,35 @@ void xiosigaction_hasread(int signum, siginfo_t *siginfo, void *ucontext) {
|
||||
Debug5("xiosigaction_hasread(%d, {%d,%d,%d,"F_pid"}, )",
|
||||
signum, siginfo->si_signo, siginfo->si_errno, siginfo->si_code,
|
||||
siginfo->si_pid);
|
||||
_errno = errno;
|
||||
do {
|
||||
pid = Waitpid(-1, &status, WNOHANG);
|
||||
if (pid == 0) {
|
||||
Msg(wassig?E_INFO:E_WARN,
|
||||
"waitpid(-1, {}, WNOHANG): no child has exited");
|
||||
Info("childdied() finished");
|
||||
errno = _errno;
|
||||
return;
|
||||
} else if (pid < 0 && errno == ECHILD) {
|
||||
Msg1(wassig?E_INFO:E_WARN,
|
||||
"waitpid(-1, {}, WNOHANG): %s", strerror(errno));
|
||||
Info("childdied() finished");
|
||||
errno = _errno;
|
||||
return;
|
||||
}
|
||||
wassig = true;
|
||||
if (pid < 0) {
|
||||
Warn2("waitpid(-1, {%d}, WNOHANG): %s", status, strerror(errno));
|
||||
Info("childdied() finished");
|
||||
errno = _errno;
|
||||
return;
|
||||
}
|
||||
} while (1);
|
||||
if (signum == SIGCHLD) {
|
||||
_errno = errno;
|
||||
do {
|
||||
pid = Waitpid(-1, &status, WNOHANG);
|
||||
if (pid == 0) {
|
||||
Msg(wassig?E_INFO:E_WARN,
|
||||
"waitpid(-1, {}, WNOHANG): no child has exited");
|
||||
Info("childdied() finished");
|
||||
errno = _errno;
|
||||
Debug("xiosigaction_hasread() ->");
|
||||
return;
|
||||
} else if (pid < 0 && errno == ECHILD) {
|
||||
Msg1(wassig?E_INFO:E_WARN,
|
||||
"waitpid(-1, {}, WNOHANG): %s", strerror(errno));
|
||||
Info("childdied() finished");
|
||||
errno = _errno;
|
||||
Debug("xiosigaction_hasread() ->");
|
||||
return;
|
||||
}
|
||||
wassig = true;
|
||||
if (pid < 0) {
|
||||
Warn2("waitpid(-1, {%d}, WNOHANG): %s", status, strerror(errno));
|
||||
Info("childdied() finished");
|
||||
errno = _errno;
|
||||
Debug("xiosigaction_hasread() ->");
|
||||
return;
|
||||
}
|
||||
} while (1);
|
||||
}
|
||||
if (xio_waitingfor == siginfo->si_pid) {
|
||||
xio_hashappened = true;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user