mirror of
https://repo.or.cz/socat.git
synced 2024-12-22 23:42:34 +00:00
RECVFROM addresses with FORK option hung after processing the first packet
This commit is contained in:
parent
fe1337fe5f
commit
d086001911
4 changed files with 101 additions and 27 deletions
3
CHANGES
3
CHANGES
|
@ -4,6 +4,9 @@ corrections:
|
||||||
with the first received packet an error occurred:
|
with the first received packet an error occurred:
|
||||||
socket_init(): unknown address family 0
|
socket_init(): unknown address family 0
|
||||||
|
|
||||||
|
RECVFROM addresses with FORK option hung after processing the first
|
||||||
|
packet.
|
||||||
|
|
||||||
####################### V 1.6.0.1:
|
####################### V 1.6.0.1:
|
||||||
|
|
||||||
new features:
|
new features:
|
||||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
||||||
"1.6.0.1+ip4bind"
|
"1.6.0.1+ip4bind+recvfromfork"
|
||||||
|
|
48
test.sh
48
test.sh
|
@ -8195,6 +8195,54 @@ PORT=$((PORT+1))
|
||||||
N=$((N+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"
|
echo "summary: $((N-1)) tests; $numOK ok, $numFAIL failed, $numCANT could not be performed"
|
||||||
|
|
||||||
if [ "$numFAIL" -gt 0 ]; then
|
if [ "$numFAIL" -gt 0 ]; then
|
||||||
|
|
27
xio-socket.c
27
xio-socket.c
|
@ -509,8 +509,26 @@ int _xioopen_dgram_sendto(/* them is already in xfd->peersa */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static pid_t xio_waitingfor;
|
/* when the recvfrom address (with option fork) receives a packet it keeps this
|
||||||
static bool xio_hashappened;
|
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) {
|
void xiosigaction_hasread(int signum, siginfo_t *siginfo, void *ucontext) {
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
int _errno;
|
int _errno;
|
||||||
|
@ -519,6 +537,7 @@ void xiosigaction_hasread(int signum, siginfo_t *siginfo, void *ucontext) {
|
||||||
Debug5("xiosigaction_hasread(%d, {%d,%d,%d,"F_pid"}, )",
|
Debug5("xiosigaction_hasread(%d, {%d,%d,%d,"F_pid"}, )",
|
||||||
signum, siginfo->si_signo, siginfo->si_errno, siginfo->si_code,
|
signum, siginfo->si_signo, siginfo->si_errno, siginfo->si_code,
|
||||||
siginfo->si_pid);
|
siginfo->si_pid);
|
||||||
|
if (signum == SIGCHLD) {
|
||||||
_errno = errno;
|
_errno = errno;
|
||||||
do {
|
do {
|
||||||
pid = Waitpid(-1, &status, WNOHANG);
|
pid = Waitpid(-1, &status, WNOHANG);
|
||||||
|
@ -527,12 +546,14 @@ void xiosigaction_hasread(int signum, siginfo_t *siginfo, void *ucontext) {
|
||||||
"waitpid(-1, {}, WNOHANG): no child has exited");
|
"waitpid(-1, {}, WNOHANG): no child has exited");
|
||||||
Info("childdied() finished");
|
Info("childdied() finished");
|
||||||
errno = _errno;
|
errno = _errno;
|
||||||
|
Debug("xiosigaction_hasread() ->");
|
||||||
return;
|
return;
|
||||||
} else if (pid < 0 && errno == ECHILD) {
|
} else if (pid < 0 && errno == ECHILD) {
|
||||||
Msg1(wassig?E_INFO:E_WARN,
|
Msg1(wassig?E_INFO:E_WARN,
|
||||||
"waitpid(-1, {}, WNOHANG): %s", strerror(errno));
|
"waitpid(-1, {}, WNOHANG): %s", strerror(errno));
|
||||||
Info("childdied() finished");
|
Info("childdied() finished");
|
||||||
errno = _errno;
|
errno = _errno;
|
||||||
|
Debug("xiosigaction_hasread() ->");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
wassig = true;
|
wassig = true;
|
||||||
|
@ -540,9 +561,11 @@ void xiosigaction_hasread(int signum, siginfo_t *siginfo, void *ucontext) {
|
||||||
Warn2("waitpid(-1, {%d}, WNOHANG): %s", status, strerror(errno));
|
Warn2("waitpid(-1, {%d}, WNOHANG): %s", status, strerror(errno));
|
||||||
Info("childdied() finished");
|
Info("childdied() finished");
|
||||||
errno = _errno;
|
errno = _errno;
|
||||||
|
Debug("xiosigaction_hasread() ->");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} while (1);
|
} while (1);
|
||||||
|
}
|
||||||
if (xio_waitingfor == siginfo->si_pid) {
|
if (xio_waitingfor == siginfo->si_pid) {
|
||||||
xio_hashappened = true;
|
xio_hashappened = true;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue