diff --git a/CHANGES b/CHANGES index aa5a538..b6e7c88 100644 --- a/CHANGES +++ b/CHANGES @@ -22,6 +22,11 @@ corrections: ABSTRACT-CONNECT, ABSTRACT-CLIENT, SOCKSA, SOCKS4A (thanks to Fernanda G Weiden for reporting this problem) + fixed a bug where sub processes would become zombies because the master + process caught SIGCHLD but did not wait(). this affected addresses + UDP-RECVFROM, IP-RECVFROM, UNIX-RECVFROM, ABSTRACT-RECVFROM + (thanks to Evan Borgstrom for reporting this problem) + corrected some print statements and variable names make uninstall did not uninstall procan diff --git a/VERSION b/VERSION index 7420a71..92d153c 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -"1.6.0.0+execpty+servres+fd_setsize+udp_sigchld1" +"1.6.0.0+execpty+servres+fd_setsize+udp_sigchld" diff --git a/test.sh b/test.sh index 57f7074..441ab5f 100755 --- a/test.sh +++ b/test.sh @@ -8029,6 +8029,56 @@ PORT=$((PORT+1)) N=$((N+1)) +# there was a bug with udp-recvfrom and fork: terminating sub processes became +# zombies because the master process did caught SIGCHLD but did not wait() +NAME=UDP4RECVFROM_SIGCHLD +case "$TESTS" in +*%functions%*|*%ip4%*|*%udp%*|*%dgram%*|*%$NAME%*) +TEST="$NAME: test if UDP4-RECVFROM child becomes zombie" +# idea: run a udp-recvfrom process with fork and -T. Sent it one packet, so a +# sub process is forked off. Make some transfer and wait until the -T timeout +# is over. Now check for the child process: if it is zombie the test failed. +# Correct is that child process terminated +tf="$td/test$N.stdout" +te="$td/test$N.stderr" +tdiff="$td/test$N.diff" +tsl=$PORT +ts="$LOCALHOST:$tsl" +da=$(date) +CMD1="$SOCAT $opts -T 0.5 UDP4-RECVFROM:$tsl,reuseaddr,fork PIPE" +CMD2="$SOCAT $opts - UDP4-SENDTO:$ts" +printf "test $F_n $TEST... " $N +$CMD1 >"$tf" 2>"${te}1" & +pid1=$! +waitudp4port $tsl 1 +echo "$da" |$CMD2 >>"$tf" 2>>"${te}2" +rc2=$? +sleep 1 +#read -p ">" +l="$(childprocess $pid1)" +kill $pid1 2>/dev/null; wait +if [ $rc2 -ne 0 ]; then + $PRINTF "$NO_RESULT\n" # already handled in test UDP4DGRAM + numCANT=$((numCANT+1)) +elif ! echo "$da" |diff - "$tf" >"$tdiff"; then + $PRINTF "$NO_RESULT\n" # already handled in test UDP4DGRAMM + numCANT=$((numCANT+1)) +elif $(isdefunct "$l"); then + $PRINTF "$FAILED: $SOCAT:\n" + echo "$CMD1 &" + echo "$CMD2" + cat "${te}1" "${te}2" + numFAIL=$((numFAIL+1)) +else + $PRINTF "$OK\n" + if [ -n "$debug" ]; then cat "${te}1" "${te}2"; 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 diff --git a/xio-socket.c b/xio-socket.c index c0c7037..01fda15 100644 --- a/xio-socket.c +++ b/xio-socket.c @@ -512,9 +512,37 @@ int _xioopen_dgram_sendto(/* them is already in xfd->peersa */ static pid_t xio_waitingfor; static bool xio_hashappened; void xiosigaction_hasread(int signum, siginfo_t *siginfo, void *ucontext) { + pid_t pid; + int _errno; + int status = 0; + bool wassig = false; 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 (xio_waitingfor == siginfo->si_pid) { xio_hashappened = true; }