diff --git a/CHANGES b/CHANGES index 60deebf..1da4617 100644 --- a/CHANGES +++ b/CHANGES @@ -28,6 +28,13 @@ Coding: Make gcc happy, replace strncat with "manual" copying + On addresses like UDP-RECVFROM with fork option every packet causes a + new child process which then reads the packet. The parent process must + wait until the packet has been read before checking again. The former + synchronization mechanism using SIGUSR1 is now replaced by a + socketpair. SIGUSR1 is no longer used for internal synchronization. + Tests: UDP4_FORK UDP6_FORK UNIX_FORK + ####################### V 1.7.4.5 (not released): Corrections: diff --git a/socat.c b/socat.c index 873920c..6488103 100644 --- a/socat.c +++ b/socat.c @@ -720,6 +720,7 @@ int socat(const char *address1, const char *address2) { the communication channel, so continue */ Info2("child "F_pid" has already died with status %d", XIO_RDSTREAM(sock1)->para.exec.pid, statunknown[i]); + ++num_child; /* it was counted as anonymous child, undo */ if (statunknown[i] != 0) { return 1; } diff --git a/test.sh b/test.sh index 93ea5c8..0cb66b5 100755 --- a/test.sh +++ b/test.sh @@ -1935,7 +1935,7 @@ testaddrs () { local a A; for a in $@; do A=$(echo "$a" |tr 'a-z' 'A-Z') - if ! $SOCAT $A /dev/null 2>&1 |grep -q "E unknown device/address"; then + if ! $SOCAT $A /dev/null 2>&1 /dev/null; then @@ -6055,7 +6056,7 @@ N=$((N+1)) NAME=TCP4WRAPPERS_NAME case "$TESTS" in -*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%tcpwrap%*|*%$NAME%*) +*%$N%*|*%functions%*|*%fork%*|*%security%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%tcpwrap%*|*%$NAME%*) TEST="$NAME: security of TCP4-L with TCPWRAP option" if ! eval $NUMCOND; then :; elif ! feat=$(testfeats tcp ip4 libwrap) || ! runsip4 >/dev/null; then @@ -6076,7 +6077,7 @@ N=$((N+1)) NAME=TCP6RANGE case "$TESTS" in -*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp6%*|*%ip6%*|*%range%*|*%$NAME%*) +*%$N%*|*%functions%*|*%fork%*|*%security%*|*%tcp%*|*%tcp6%*|*%ip6%*|*%range%*|*%$NAME%*) TEST="$NAME: security of TCP6-L with RANGE option" if ! eval $NUMCOND; then :; elif ! feat=$(testfeats tcp ip6) || ! runsip6 >/dev/null; then @@ -6092,7 +6093,7 @@ N=$((N+1)) NAME=TCP6SOURCEPORT case "$TESTS" in -*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp6%*|*%ip6%*|*%sourceport%*|*%$NAME%*) +*%$N%*|*%functions%*|*%fork%*|*%security%*|*%tcp%*|*%tcp6%*|*%ip6%*|*%sourceport%*|*%$NAME%*) TEST="$NAME: security of TCP6-L with SOURCEPORT option" if ! eval $NUMCOND; then :; elif ! feat=$(testfeats tcp ip6) || ! runsip6 >/dev/null; then @@ -6108,7 +6109,7 @@ N=$((N+1)) NAME=TCP6LOWPORT case "$TESTS" in -*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp6%*|*%ip6%*|*%lowport%*|*%$NAME%*) +*%$N%*|*%functions%*|*%fork%*|*%security%*|*%tcp%*|*%tcp6%*|*%ip6%*|*%lowport%*|*%$NAME%*) TEST="$NAME: security of TCP6-L with LOWPORT option" if ! eval $NUMCOND; then :; elif ! feat=$(testfeats tcp ip6) || ! runsip6 >/dev/null; then @@ -6124,7 +6125,7 @@ N=$((N+1)) NAME=TCP6TCPWRAP case "$TESTS" in -*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp6%*|*%ip6%*|*%tcpwrap%*|*%$NAME%*) +*%$N%*|*%functions%*|*%fork%*|*%security%*|*%tcp%*|*%tcp6%*|*%ip6%*|*%tcpwrap%*|*%$NAME%*) TEST="$NAME: security of TCP6-L with TCPWRAP option" if ! eval $NUMCOND; then :; elif ! feat=$(testfeats tcp ip6 libwrap && runstcp6); then @@ -6145,7 +6146,7 @@ N=$((N+1)) NAME=UDP4RANGE case "$TESTS" in -*%$N%*|*%functions%*|*%security%*|*%udp%*|*%udp4%*|*%ip4%*|*%range%*|*%$NAME%*) +*%$N%*|*%functions%*|*%security%*|*%fork%*|*%udp%*|*%udp4%*|*%ip4%*|*%range%*|*%$NAME%*) TEST="$NAME: security of UDP4-L with RANGE option" if ! eval $NUMCOND; then :; else newport udp4 # provide free port number in $PORT @@ -6200,7 +6201,7 @@ N=$((N+1)) NAME=UDP6RANGE case "$TESTS" in -*%$N%*|*%functions%*|*%security%*|*%udp%*|*%udp6%*|*%ip6%*|*%range%*|*%$NAME%*) +*%$N%*|*%functions%*|*%security%*|*%fork%*|*%udp%*|*%udp6%*|*%ip6%*|*%range%*|*%$NAME%*) TEST="$NAME: security of UDP6-L with RANGE option" if ! eval $NUMCOND; then :; elif ! feat=$(testfeats tcp ip6) || ! runsip6 >/dev/null; then @@ -6269,7 +6270,7 @@ N=$((N+1)) NAME=OPENSSLTCP4_RANGE case "$TESTS" in -*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%openssl%*|*%range%*|*%$NAME%*) +*%$N%*|*%functions%*|*%security%*|*%fork%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%openssl%*|*%range%*|*%$NAME%*) TEST="$NAME: security of SSL-L over TCP/IPv4 with RANGE option" if ! eval $NUMCOND; then :; elif ! testfeats openssl >/dev/null; then @@ -6286,7 +6287,7 @@ N=$((N+1)) NAME=OPENSSLTCP4_SOURCEPORT case "$TESTS" in -*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%openssl%*|*%sourceport%*|*%$NAME%*) +*%$N%*|*%functions%*|*%security%*|*%fork%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%openssl%*|*%sourceport%*|*%$NAME%*) TEST="$NAME: security of SSL-L with SOURCEPORT option" if ! eval $NUMCOND; then :; elif ! testfeats openssl >/dev/null; then @@ -6303,7 +6304,7 @@ N=$((N+1)) NAME=OPENSSLTCP4_LOWPORT case "$TESTS" in -*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%openssl%*|*%lowport%*|*%$NAME%*) +*%$N%*|*%functions%*|*%security%*|*%fork%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%openssl%*|*%lowport%*|*%$NAME%*) TEST="$NAME: security of SSL-L with LOWPORT option" if ! eval $NUMCOND; then :; elif ! testfeats openssl >/dev/null; then @@ -6320,7 +6321,7 @@ N=$((N+1)) NAME=OPENSSLTCP4_TCPWRAP case "$TESTS" in -*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%openssl%*|*%tcpwrap%*|*%$NAME%*) +*%$N%*|*%functions%*|*%security%*|*%fork%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%openssl%*|*%tcpwrap%*|*%$NAME%*) TEST="$NAME: security of SSL-L with TCPWRAP option" if ! eval $NUMCOND; then :; elif ! feat=$(testfeats ip4 tcp libwrap openssl); then @@ -6341,7 +6342,7 @@ N=$((N+1)) NAME=OPENSSLCERTSERVER case "$TESTS" in -*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%openssl%*|*%$NAME%*) +*%$N%*|*%functions%*|*%security%*|*%fork%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%openssl%*|*%$NAME%*) TEST="$NAME: security of SSL-L with client certificate" if ! eval $NUMCOND; then :; elif ! testfeats openssl >/dev/null; then @@ -6359,7 +6360,7 @@ N=$((N+1)) NAME=OPENSSLCERTCLIENT case "$TESTS" in -*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%openssl%*|*%$NAME%*) +*%$N%*|*%functions%*|*%security%*|*%fork%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%openssl%*|*%$NAME%*) TEST="$NAME: security of SSL with server certificate" if ! eval $NUMCOND; then :; elif ! testfeats openssl >/dev/null; then @@ -6378,7 +6379,7 @@ N=$((N+1)) NAME=OPENSSLTCP6_RANGE case "$TESTS" in -*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp6%*|*%ip6%*|*%openssl%*|*%range%*|*%$NAME%*) +*%$N%*|*%functions%*|*%security%*|*%fork%*|*%tcp%*|*%tcp6%*|*%ip6%*|*%openssl%*|*%range%*|*%$NAME%*) TEST="$NAME: security of SSL-L over TCP/IPv6 with RANGE option" if ! eval $NUMCOND; then :; elif ! testfeats openssl >/dev/null; then @@ -6399,7 +6400,7 @@ N=$((N+1)) NAME=OPENSSLTCP6_SOURCEPORT case "$TESTS" in -*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp6%*|*%ip6%*|*%openssl%*|*%sourceport%*|*%$NAME%*) +*%$N%*|*%functions%*|*%security%*|*%fork%*|*%tcp%*|*%tcp6%*|*%ip6%*|*%openssl%*|*%sourceport%*|*%$NAME%*) TEST="$NAME: security of SSL-L over TCP/IPv6 with SOURCEPORT option" if ! eval $NUMCOND; then :; elif ! testfeats openssl >/dev/null; then @@ -6420,7 +6421,7 @@ N=$((N+1)) NAME=OPENSSLTCP6_LOWPORT case "$TESTS" in -*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp6%*|*%ip6%*|*%openssl%*|*%lowport%*|*%$NAME%*) +*%$N%*|*%functions%*|*%security%*|*%fork%*|*%tcp%*|*%tcp6%*|*%ip6%*|*%openssl%*|*%lowport%*|*%$NAME%*) TEST="$NAME: security of SSL-L over TCP/IPv6 with LOWPORT option" if ! eval $NUMCOND; then :; elif ! testfeats openssl >/dev/null; then @@ -6441,7 +6442,7 @@ N=$((N+1)) NAME=OPENSSLTCP6_TCPWRAP case "$TESTS" in -*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp6%*|*%ip6%*|*%openssl%*|*%tcpwrap%*|*%$NAME%*) +*%$N%*|*%functions%*|*%security%*|*%fork%*|*%tcp%*|*%tcp6%*|*%ip6%*|*%openssl%*|*%tcpwrap%*|*%$NAME%*) TEST="$NAME: security of SSL-L over TCP/IPv6 with TCPWRAP option" if ! eval $NUMCOND; then :; elif ! feat=$(testfeats ip6 tcp libwrap openssl && runsip6); then @@ -6464,7 +6465,7 @@ N=$((N+1)) # test security with the openssl-commonname option on client side NAME=OPENSSL_CN_CLIENT_SECURITY case "$TESTS" in -*%$N%*|*%functions%*|*%security%*|*%openssl%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%$NAME%*) +*%$N%*|*%functions%*|*%security%*|*%fork%*|*%openssl%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%$NAME%*) TEST="$NAME: security of client openssl-commonname option" # connect using non matching server name/address with commonname # options, this should succeed. Then without this option, should fail @@ -6515,7 +6516,7 @@ N=$((N+1)) NAME=OPENSSL_FIPS_SECURITY case "$TESTS" in -*%$N%*|*%functions%*|*%security%*|*%openssl%*|*%fips%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%$NAME%*) +*%$N%*|*%functions%*|*%security%*|*%fork%*|*%openssl%*|*%fips%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%$NAME%*) TEST="$NAME: OpenSSL restrictions by FIPS" if ! eval $NUMCOND; then :; elif ! testfeats openssl >/dev/null; then @@ -7108,7 +7109,12 @@ kill $pid0 2>/dev/null wait if ! diff "$tref" "$tf" >"$tdiff"; then $PRINTF "$FAILED\n" - cat "${te}0" "${te}1" "${te}2" + echo "$CMD0 &" + cat "${te}0" >&2 + echo "$CMD1" + cat "${te}1" >&2 + echo "$CMD2" + cat "${te}2" >&2 cat "$tdiff" numFAIL=$((numFAIL+1)) listFAIL="$listFAIL $N" @@ -7908,7 +7914,7 @@ N=$((N+1)) NAME=UDP4RECVFROM_RANGE case "$TESTS" in -*%$N%*|*%functions%*|*%security%*|*%udp%*|*%udp4%*|*%ip4%*|*%range%*|*%$NAME%*) +*%$N%*|*%functions%*|*%security%*|*%fork%*|*%udp%*|*%udp4%*|*%ip4%*|*%range%*|*%$NAME%*) TEST="$NAME: security of UDP4-RECVFROM with RANGE option" newport udp4 # provide free port number in $PORT #testserversec "$N" "$TEST" "$opts" "UDP4-RECVFROM:$PORT,reuseaddr,fork" "" "range=$SECONDADDR/32" "UDP4-SENDTO:127.0.0.1:$PORT" 4 udp $PORT 0 @@ -7920,7 +7926,7 @@ N=$((N+1)) NAME=UDP4RECVFROM_TCPWRAP case "$TESTS" in -*%$N%*|*%functions%*|*%security%*|*%udp%*|*%udp4%*|*%ip4%*|*%tcpwrap%*|*%$NAME%*) +*%$N%*|*%functions%*|*%security%*|*%fork%*|*%udp%*|*%udp4%*|*%ip4%*|*%tcpwrap%*|*%$NAME%*) TEST="$NAME: security of UDP4-RECVFROM with TCPWRAP option" if ! eval $NUMCOND; then :; elif ! feat=$(testfeats ip4 udp libwrap) || ! runsip4 >/dev/null; then @@ -8056,7 +8062,7 @@ N=$((N+1)) NAME=UDP6RECVFROM_RANGE case "$TESTS" in -*%$N%*|*%functions%*|*%security%*|*%udp%*|*%udp6%*|*%ip6%*|*%range%*|*%$NAME%*) +*%$N%*|*%functions%*|*%security%*|*%fork%*|*%udp%*|*%udp6%*|*%ip6%*|*%range%*|*%$NAME%*) TEST="$NAME: security of UDP6-RECVFROM with RANGE option" if ! eval $NUMCOND; then :; elif ! feat=$(testfeats tcp ip6) || ! runsip6 >/dev/null; then @@ -8176,7 +8182,7 @@ N=$((N+1)) NAME=IP4RECVFROM_RANGE case "$TESTS" in -*%$N%*|*%functions%*|*%security%*|*%ip%*|*%ip4%*|*%range%*|*%root%*|*%$NAME%*) +*%$N%*|*%functions%*|*%security%*|*%fork%*|*%ip%*|*%ip4%*|*%range%*|*%root%*|*%$NAME%*) TEST="$NAME: security of IP4-RECVFROM with RANGE option" if ! eval $NUMCOND; then :; elif ! feat=$(testfeats ip4 rawip) || ! runsip4 >/dev/null; then @@ -8198,7 +8204,7 @@ N=$((N+1)) NAME=IP4RECVFROM_TCPWRAP case "$TESTS" in -*%$N%*|*%functions%*|*%security%*|*%ip%*|*%ip4%*|*%tcpwrap%*|*%root%*|*%$NAME%*) +*%$N%*|*%functions%*|*%security%*|*%fork%*|*%ip%*|*%ip4%*|*%tcpwrap%*|*%root%*|*%$NAME%*) TEST="$NAME: security of IP4-RECVFROM with TCPWRAP option" if ! eval $NUMCOND; then :; elif ! feat=$(testfeats ip4 rawip libwrap) || ! runsip4 >/dev/null; then @@ -8281,7 +8287,7 @@ N=$((N+1)) NAME=IP6RECVFROM_RANGE case "$TESTS" in -*%$N%*|*%functions%*|*%security%*|*%ip%*|*%ip6%*|*%range%*|*%root%*|*%$NAME%*) +*%$N%*|*%functions%*|*%security%*|*%fork%*|*%ip%*|*%ip6%*|*%range%*|*%root%*|*%$NAME%*) TEST="$NAME: security of IP6-RECVFROM with RANGE option" if ! eval $NUMCOND; then :; elif ! feat=$(testfeats ip6 rawip && runsip6); then @@ -8303,7 +8309,7 @@ N=$((N+1)) NAME=IP6RECVFROM_TCPWRAP case "$TESTS" in -*%$N%*|*%functions%*|*%security%*|*%ip%*|*%ip6%*|*%tcpwrap%*|*%root%*|*%$NAME%*) +*%$N%*|*%functions%*|*%security%*|*%fork%*|*%ip%*|*%ip6%*|*%tcpwrap%*|*%root%*|*%$NAME%*) TEST="$NAME: security of IP6-RECVFROM with TCPWRAP option" if ! eval $NUMCOND; then :; elif ! feat=$(testfeats ip6 rawip libwrap && runsip6); then @@ -8650,49 +8656,70 @@ N=$((N+1)) NAME=TCP4ENDCLOSE case "$TESTS" in -*%$N%*|*%functions%*|*%ip4%*|*%ipapp%*|*%tcp%*|*%$NAME%*) +*%$N%*|*%functions%*|*%fork%*|*%ip4%*|*%ipapp%*|*%tcp%*|*%$NAME%*) TEST="$NAME: end-close keeps TCP V4 socket open" if ! eval $NUMCOND; then :; else tf="$td/test$N.stdout" te="$td/test$N.stderr" tdiff="$td/test$N.diff" +newport tcp4; p0=$PORT newport tcp4; p1=$PORT -newport tcp4; p2=$PORT -da1a="$(date) $RANDOM" -da1b="$(date) $RANDOM" -CMD1="$TRACE $SOCAT $opts -u - TCP4-CONNECT:$LOCALHOST:$p1" -CMD="$TRACE $SOCAT $opts -U TCP4:$LOCALHOST:$p2,end-close TCP4-LISTEN:$p1,bind=$LOCALHOST,$REUSEADDR,fork" -CMD3="$TRACE $SOCAT $opts -u TCP4-LISTEN:$p2,$REUSEADDR,bind=$LOCALHOST -" +da2a="$(date) $RANDOM" +da2b="$(date) $RANDOM" +CMD0="$TRACE $SOCAT -lp collector $opts -u TCP4-LISTEN:$p0,$REUSEADDR,bind=$LOCALHOST -" +CMD1="$TRACE $SOCAT -lp forker $opts -U TCP4:$LOCALHOST:$p0,end-close TCP4-LISTEN:$p1,bind=$LOCALHOST,$REUSEADDR,fork" +CMD2="$TRACE $SOCAT -lp client $opts -u - TCP4-CONNECT:$LOCALHOST:$p1" printf "test $F_n $TEST... " $N -$CMD3 >"$tf" 2>"${te}3" & -pid3=$! -waittcp4port $p2 1 -$CMD 2>"${te}2" & -pid2=$! +$CMD0 >"${tf}0" 2>"${te}0" & +pid0=$! +waittcp4port $p0 1 +$CMD1 2>"${te}1" & +pid1=$! usleep $MICROS waittcp4port $p1 1 -echo "$da1a" |$CMD1 2>>"${te}1a" -echo "$da1b" |$CMD1 2>>"${te}1b" +echo "$da2a" |$CMD2 2>>"${te}2a" +rc2a=$? +echo "$da2b" |$CMD2 2>>"${te}2b" +rc2b=$? sleep 1 -kill "$pid3" "$pid2" 2>/dev/null +kill "$pid0" "$pid1" 2>/dev/null wait -if [ $? -ne 0 ]; then - $PRINTF "$FAILED: $TRACE $SOCAT:\n" - echo "$CMD1 &" - echo "$CMD2" - cat "${te}1a" "${te}1b" "${te}2" "${te}3" +if [ $rc2a -ne 0 -o $rc2b -ne 0 ]; then + $PRINTF "$FAILED: $TRACE $SOCAT:\n" + echo "$CMD0 &" + cat "${te}0" >&2 + echo "$CMD1" + cat "${te}1" >&2 + echo "$CMD2" + cat "${te}2a" >&2 + echo "$CMD2" + cat "${te}2b" >&2 numFAIL=$((numFAIL+1)) listFAIL="$listFAIL $N" -elif ! $ECHO "$da1a\n$da1b" |diff - "$tf" >"$tdiff"; then - $PRINTF "$FAILED\n" - cat "$tdiff" - cat "${te}1a" "${te}1b" "${te}2" "${te}3" +elif ! $ECHO "$da2a\n$da2b" |diff - "${tf}0" >"$tdiff"; then + $PRINTF "$FAILED (diff)\n" + echo "$CMD0 &" + cat "${te}0" >&2 + echo "$CMD1" + cat "${te}1" >&2 + echo "$CMD2" + cat "${te}2a" >&2 + echo "$CMD2" + cat "${te}2b" >&2 + cat "$tdiff" numFAIL=$((numFAIL+1)) listFAIL="$listFAIL $N" else - $PRINTF "$OK\n" - if [ -n "$debug" ]; then cat "${te}1a" "${te}1b" "${te}2" "${te}3"; fi - numOK=$((numOK+1)) + $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 + if [ "$VERBOSE" ]; then echo "$CMD2"; fi + if [ "$DEBUG" ]; then cat "${te}2a" >&2; fi + if [ "$VERBOSE" ]; then echo "$CMD2"; fi + if [ "$DEBUG" ]; then cat "${te}2b" >&2; fi + numOK=$((numOK+1)) fi fi ;; # NUMCOND esac @@ -8701,7 +8728,7 @@ N=$((N+1)) NAME=EXECENDCLOSE case "$TESTS" in -*%$N%*|*%functions%*|*%exec%*|*%$NAME%*) +*%$N%*|*%functions%*|*%fork%*|*%exec%*|*%$NAME%*) TEST="$NAME: end-close keeps EXEC child running" if ! eval $NUMCOND; then :; else tf="$td/test$N.stdout" @@ -9875,7 +9902,7 @@ N=$((N+1)) # process under some circumstances. NAME=EXECPTYKILL case "$TESTS" in -*%$N%*|*%functions%*|*%bugs%*|*%exec%*|*%pty%*|*%$NAME%*) +*%$N%*|*%functions%*|*%bugs%*|*%fork%*|*%exec%*|*%pty%*|*%$NAME%*) TEST="$NAME: exec:...,pty explicitely kills sub process" # we want to check if the exec'd sub process is killed in time # for this we have a shell script that generates a file after two seconds; @@ -10037,7 +10064,7 @@ N=$((N+1)) # zombies because the master process did not catch SIGCHLD NAME=UDP4LISTEN_SIGCHLD case "$TESTS" in -*%$N%*|*%functions%*|*%ip4%*|*%ipapp%*|*%udp%*|*%zombie%*|*%signal%*|*%$NAME%*) +*%$N%*|*%functions%*|*%fork%*|*%ip4%*|*%ipapp%*|*%udp%*|*%zombie%*|*%signal%*|*%$NAME%*) TEST="$NAME: test if UDP4-LISTEN child becomes zombie" # idea: run a udp-listen process with fork and -T. Connect once, so a sub # process is forked off. Make some transfer and wait until the -T timeout is @@ -10091,7 +10118,7 @@ N=$((N+1)) # zombies because the master process caught SIGCHLD but did not wait() NAME=UDP4RECVFROM_SIGCHLD case "$TESTS" in -*%$N%*|*%functions%*|*%ip4%*|*%udp%*|*%dgram%*|*%zombie%*|*%signal%*|*%$NAME%*) +*%$N%*|*%functions%*|*%bugs%*|*%fork%*|*%ip4%*|*%udp%*|*%dgram%*|*%zombie%*|*%signal%*|*%$NAME%*) TEST="$NAME: test if UDP4-RECVFROM child becomes zombie" # idea: run a udp-recvfrom process with fork and -T. Send it one packet, so a # sub process is forked off. Make some transfer and wait until the -T timeout @@ -10115,25 +10142,39 @@ rc2=$? sleep 1 #read -p ">" l="$(childprocess $pid1)" +#echo "l=\"$l\"" kill $pid1 2>/dev/null; wait if [ $rc2 -ne 0 ]; then $PRINTF "$NO_RESULT\n" # already handled in test UDP4DGRAM + if [ "$VERBOSE" ]; then echo "$CMD1 &"; fi + if [ "$DEBUG" ]; then cat "${te}1" >&2; fi + if [ "$VERBOSE" ]; then echo "$CMD2"; fi + if [ "$DEBUG" ]; then cat "${te}2" >&2; fi numCANT=$((numCANT+1)) listCANT="$listCANT $N" elif ! echo "$da" |diff - "$tf" >"$tdiff"; then $PRINTF "$NO_RESULT\n" # already handled in test UDP4DGRAM + if [ "$VERBOSE" ]; then echo "$CMD1 &"; fi + if [ "$DEBUG" ]; then cat "${te}1" >&2; fi + if [ "$VERBOSE" ]; then echo "$CMD2"; fi + if [ "$DEBUG" ]; then cat "${te}2" >&2; fi numCANT=$((numCANT+1)) listCANT="$listCANT $N" elif $(isdefunct "$l"); then $PRINTF "$FAILED: $TRACE $SOCAT:\n" echo "$CMD1 &" + cat "${te}1" >&2 echo "$CMD2" + cat "${te}2" >&2 cat "${te}1" "${te}2" numFAIL=$((numFAIL+1)) listFAIL="$listFAIL $N" else $PRINTF "$OK\n" - if [ -n "$debug" ]; then cat "${te}1" "${te}2"; fi + if [ "$VERBOSE" ]; then echo "$CMD1 &"; fi + if [ "$DEBUG" ]; then cat "${te}1" >&2; fi + if [ "$VERBOSE" ]; then echo "$CMD2"; fi + if [ "$DEBUG" ]; then cat "${te}2" >&2; fi numOK=$((numOK+1)) fi fi ;; # NUMCOND @@ -10204,7 +10245,7 @@ N=$((N+1)) # child process. NAME=UDP4RECVFROM_FORK case "$TESTS" in -*%$N%*|*%functions%*|*%ip4%*|*%udp%*|*%dgram%*|*%$NAME%*) +*%$N%*|*%functions%*|*%fork%*|*%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 @@ -11293,7 +11334,7 @@ N=$((N+1)) NAME=SOCKETRANGEMASK case "$TESTS" in -*%$N%*|*%functions%*|*%security%*|*%generic%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%socket%*|*%range%*|*%$NAME%*) +*%$N%*|*%functions%*|*%security%*|*%fork%*|*%generic%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%socket%*|*%range%*|*%$NAME%*) TEST="$NAME: security of generic socket-listen with RANGE option" if ! eval $NUMCOND; then :; elif [ -z "$SECONDADDR" ]; then @@ -11374,7 +11415,7 @@ N=$((N+1)) # Test the generic setsockopt option NAME=SETSOCKOPT case "$TESTS" in -*%$N%*|*%functions%*|*%ip4%*|*%tcp%*|*%generic%*|*%$NAME%*) +*%$N%*|*%functions%*|*%fork%*|*%ip4%*|*%tcp%*|*%generic%*|*%$NAME%*) TEST="$NAME: test the setsockopt option" # Set the TCP_MAXSEG (MSS) option with a reasonable value, this should succeed. # The try again with TCP_MAXSEG=1, this fails at least on Linux. @@ -12096,7 +12137,7 @@ N=$((N+1)) while read KEYW FEAT ADDR IPPORT; do -if [ -z "$KEYW" ]|| [[ "$KEYW" == \#* ]]; then continue; fi +if [ -z "$KEYW" ] || [[ "$KEYW" == \#* ]]; then continue; fi RUNS=$(tolower $KEYW) PROTO=$KEYW proto="$(echo "$PROTO" |tr A-Z a-z)" @@ -12104,7 +12145,7 @@ feat="$(tolower "$FEAT")" # test the max-children option on really connection oriented sockets NAME=${KEYW}MAXCHILDREN case "$TESTS" in -*%$N%*|*%functions%*|*%maxchildren%*|*%$feat%*|*%$proto%*|*%socket%*|*%$NAME%*) +*%$N%*|*%functions%*|*%fork%*|*%maxchildren%*|*%$feat%*|*%$proto%*|*%socket%*|*%$NAME%*) TEST="$NAME: max-children option" # start a listen process with max-children=1; connect with a client, let it # sleep some time before sending data; connect with second client that sends @@ -12182,14 +12223,14 @@ UNIX unix $td/test\$N.server - while read KEYW FEAT ADDR IPPORT SHUT; do -if [ -z "$KEYW" ]|| [[ "$KEYW" == \#* ]]; then continue; fi +if [ -z "$KEYW" ] || [[ "$KEYW" == \#* ]]; then continue; fi RUNS=$(tolower $KEYW) PROTO=$KEYW proto="$(echo "$PROTO" |tr A-Z a-z)" # test the max-children option on pseudo connected sockets NAME=${KEYW}MAXCHILDREN case "$TESTS" in -*%$N%*|*%functions%*|*%maxchildren%*|*%socket%*|*%$NAME%*) +*%$N%*|*%functions%*|*%fork%*|*%maxchildren%*|*%socket%*|*%$NAME%*) TEST="$NAME: max-children option" # start a listen process with max-children=1; connect with a client, let it # send data and then sleep; connect with second client that wants to send @@ -12550,7 +12591,7 @@ N=$((N+1)) # had a bug that converted a bit mask of 0 internally to 0xffffffff NAME=TCP4RANGE_0BITS case "$TESTS" in -*%$N%*|*%functions%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%range%*|*%$NAME%*) +*%$N%*|*%functions%*|*%fork%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%range%*|*%$NAME%*) TEST="$NAME: correct evaluation of range mask 0" if ! eval $NUMCOND; then :; elif [ -z "$SECONDADDR" ]; then @@ -12999,7 +13040,7 @@ if [ "$addropts" = "." ]; then addropts=; fi # $ADDR with fork removes the file system entry when the process is terminated NAME=${ADDR_}_REMOVE_FORK case "$TESTS" in -*%$N%*|*%functions%*|*%bugs%*|*%unix%*|*%socket%*|*%$NAME%*) +*%$N%*|*%functions%*|*%bugs%*|*%fork%*|*%unix%*|*%socket%*|*%$NAME%*) TEST="$NAME: $ADDR with fork removes socket entry when terminated during accept" # start a socat process with listening unix domain socket etc and option fork. # Terminate the process and check if the file system socket entry still exists. @@ -16069,6 +16110,101 @@ PORT=$((PORT+1)) N=$((N+1)) +while read KEYW FEAT RUNS ADDR IPPORT; do +if [ -z "$KEYW" ] || [[ "$KEYW" == \#* ]]; then continue; fi +RUNS=$(tolower $KEYW) +PROTO=$KEYW +proto="$(echo "$PROTO" |tr A-Z a-z)" +feat="$(tolower "$FEAT")" +# test the fork option on really RECVFROM oriented sockets +NAME=${KEYW}_FORK +case "$TESTS" in +*%$N%*|*%functions%*|*%fork%*|*%$feat%*|*%$proto%*|*%socket%*|*%$NAME%*) +TEST="$NAME: ${KEYW}-RECVFROM with fork option" +# Start a RECVFROM process with fork option and SYSTEM address where clients +# data determines the sleep time; send a record with sleep before storing the +# data, then send a record with 0 sleep before storing data. +# When the second record is stored before the first one the test succeeded. +if ! eval $NUMCOND; then :; +elif ! F=$(testfeats $FEAT STDIO SYSTEM); then + $PRINTF "test $F_n $TEST... ${YELLOW}Feature $F not available${NORMAL}\n" $N + numCANT=$((numCANT+1)) + listCANT="$listCANT $N" +elif ! A=$(testaddrs - STDIO SYSTEM $PROTO-RECVFROM $PROTO-SENDTO); then + $PRINTF "test $F_n $TEST... ${YELLOW}Address $A not available${NORMAL}\n" $N + numCANT=$((numCANT+1)) + listCANT="$listCANT $N" +elif ! o=$(testoptions fork ) >/dev/null; then + $PRINTF "test $F_n $TEST... ${YELLOW}Option $o not available${NORMAL}\n" $N + numCANT=$((numCANT+1)) + listCANT="$listCANT $N" +elif ! runs$RUNS >/dev/null; then + $PRINTF "test $F_n $TEST... ${YELLOW}$(toupper $RUNS) not available${NORMAL}\n" $N + numCANT=$((numCANT+1)) + listCANT="$listCANT $N" +else +case "X$IPPORT" in + "XPORT") + newport $proto + tsl=$PORT # test socket listen address + tsc="$ADDR:$PORT" # test socket connect address + ;; + *) + tsl="$(eval echo "$ADDR")" # resolve $N + tsc=$tsl +esac +#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 -t 3 $PROTO-RECVFROM:$tsl,fork SYSTEM:'read t x; sleep \$t; echo \$x >>'\"$tf\"" +CMD1="$TRACE $SOCAT $opts -t 3 - $PROTO-SENDTO:$tsc" +printf "test $F_n $TEST... " $N +eval $CMD0 "${te}0" & +pid0=$! +wait${proto}port $tsl 1 +echo "2 $da 1" |$CMD1 >"${tf}1" 2>"${te}1" & +pid1=$! +sleep 1 +echo "0 $da 2" |$CMD1 >"${tf}2" 2>"${te}2" & +pid2=$! +sleep 2 +cpids="$(childpids $pid0 /dev/null; wait +if echo -e "$da 2\n$da 1" |diff - $tf >$tdiff; 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 + if [ "$VERBOSE" ]; then echo "$CMD2"; fi + if [ "$DEBUG" ]; then cat "${te}2" >&2; fi + numOK=$((numOK+1)) +else + $PRINTF "$FAILED\n" + echo "$CMD0 &" + cat "${te}0" >&2 + echo "$CMD1" + cat "${te}1" >&2 + echo "$CMD2" + cat "${te}2" >&2 + echo "diff:" >&2 + cat "$tdiff" >&2 + numFAIL=$((numFAIL+1)) + listFAIL="$listFAIL $N" + namesFAIL="$namesFAIL $NAME" +fi +fi # NUMCOND + ;; +esac +N=$((N+1)) +done <<<" +UDP4 UDP udp4 127.0.0.1 PORT +UDP6 UDP udp4 [::1] PORT +UNIX unix unix $td/test\$N.server - +" + # end of common tests ################################################################################## diff --git a/xio-listen.c b/xio-listen.c index 83a5f0b..54d69dc 100644 --- a/xio-listen.c +++ b/xio-listen.c @@ -338,11 +338,7 @@ int _xioopen_listen(struct single *xfd, int xioflags, struct sockaddr *us, sockl pid_t pid; /* mostly int; only used with fork */ sigset_t mask_sigchld; - /* we must prevent that the current packet triggers another fork; - therefore we wait for a signal from the recent child: USR1 - indicates that is has consumed the last packet; CHLD means it has - terminated */ - /* block SIGCHLD and SIGUSR1 until parent is ready to react */ + /* Block SIGCHLD until parent is ready to react */ sigemptyset(&mask_sigchld); sigaddset(&mask_sigchld, SIGCHLD); Sigprocmask(SIG_BLOCK, &mask_sigchld, NULL); @@ -383,6 +379,7 @@ int _xioopen_listen(struct single *xfd, int xioflags, struct sockaddr *us, sockl /* now we are ready to handle signals */ Sigprocmask(SIG_UNBLOCK, &mask_sigchld, NULL); + while (maxchildren) { if (num_child < maxchildren) break; Notice("maxchildren are active, waiting"); diff --git a/xio-progcall.c b/xio-progcall.c index c530983..50e314d 100644 --- a/xio-progcall.c +++ b/xio-progcall.c @@ -545,7 +545,7 @@ int _xioopen_foxec(int xioflags, /* XIO_RDONLY etc. */ applyopts(fdi, *copts, PH_LATE2); } if (withfork) { - Info("Signalling parent ready"); + Info("notifying parent that child process is ready"); Close(trigger[1]); /* in child, notify parent that ready */ } } /* withfork */ @@ -603,7 +603,7 @@ int _xioopen_foxec(int xioflags, /* XIO_RDONLY etc. */ fds[0].fd = trigger[0]; fds[0].events = POLLIN|POLLHUP; Poll(fds, 1, -1); - Info("Child process signalled ready"); + Info("child process notified parent that it is ready"); } return pid; /* indicate parent (main) process */ diff --git a/xio-socket.c b/xio-socket.c index 78ff176..f1d8410 100644 --- a/xio-socket.c +++ b/xio-socket.c @@ -1084,101 +1084,13 @@ int _xioopen_dgram_sendto(/* them is already in xfd->peersa */ 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 child process reads the + again if it calls select()/poll() before the child process has read 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. + Before forking an unnamed pipe (fifo) is created. The sub process closes the + write side when it has read the packet. The parent process waits until the + read side of the pipe gives EOF and only then continues to listen. */ -static pid_t xio_waitingfor; /* info from recv loop to signal handler: - indicates the pid 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 */ -static int xio_childstatus; - -/* this is the signal handler for USR1 and CHLD */ -void xiosigaction_hasread(int signum -#if HAVE_STRUCT_SIGACTION_SA_SIGACTION && defined(SA_SIGINFO) - , siginfo_t *siginfo, void *ucontext -#endif - ) { - pid_t pid; - int _errno; - int status = 0; - bool wassig = false; - - _errno = errno; - diag_in_handler = 1; -#if HAVE_STRUCT_SIGACTION_SA_SIGACTION && defined(SA_SIGINFO) - Debug5("xiosigaction_hasread(%d, {%d,%d,%d,"F_pid"}, )", - signum, siginfo->si_signo, siginfo->si_errno, siginfo->si_code, - siginfo->si_pid); -#else - Debug1("xiosigaction_hasread(%d)", signum); -#endif - if (signum == SIGCHLD) { - do { - pid = Waitpid(-1, &status, WNOHANG); - if (pid == 0) { - Msg(wassig?E_INFO:E_WARN, - "waitpid(-1, {}, WNOHANG): no child has exited"); - Info("xiosigaction_hasread() finished"); - Debug("xiosigaction_hasread() ->"); - diag_in_handler = 0; - errno = _errno; - return; - } else if (pid < 0 && errno == EINTR) { - Info1("xiosigaction_hasread(): %s", strerror(errno)); - } else if (pid < 0 && errno == ECHILD) { - Msg(wassig?E_INFO:E_NOTICE, - "waitpid(-1, {}, WNOHANG): "F_strerror); - Info("xiosigaction_hasread() finished"); - Debug("xiosigaction_hasread() ->"); - diag_in_handler = 0; - errno = _errno; - return; - } - wassig = true; - if (pid < 0) { - Warn1("waitpid(-1, {%d}, WNOHANG): "F_strerror, status); - Info("xiosigaction_hasread() finished"); - Debug("xiosigaction_hasread() ->"); - diag_in_handler = 0; - errno = _errno; - return; - } - if (pid == xio_waitingfor) { - xio_waitingfor = 0; /* so this child will not set hashappened again */ - xio_hashappened = true; - xio_childstatus = WEXITSTATUS(status); - Debug("xiosigaction_hasread() ->"); - diag_in_handler = 0; - errno = _errno; - return; - } - } while (1); - } -#if HAVE_STRUCT_SIGACTION_SA_SIGACTION && defined(SA_SIGINFO) - if (xio_waitingfor == siginfo->si_pid) { - xio_hashappened = true; - } -#else - xio_hashappened = true; -#endif -#if !HAVE_SIGACTION - Signal(sig, xiosigaction_hasread); -#endif /* !HAVE_SIGACTION */ - Debug("xiosigaction_hasread() ->"); - diag_in_handler = 0; - errno = _errno; - return; -} - /* waits for incoming packet, checks its source address and port. Depending on fork option, it may fork a subprocess. @@ -1267,42 +1179,7 @@ int _xioopen_dgram_recvfrom(struct single *xfd, int xioflags, } if (dofork) { -#if HAVE_SIGACTION - { - struct sigaction act; - memset(&act, 0, sizeof(struct sigaction)); - act.sa_flags = SA_NOCLDSTOP/*|SA_RESTART*/ -#ifdef SA_SIGINFO /* not on Linux 2.0(.33) */ - |SA_SIGINFO -#endif -#ifdef SA_NOMASK - |SA_NOMASK -#endif - ; -#if HAVE_STRUCT_SIGACTION_SA_SIGACTION && defined(SA_SIGINFO) - act.sa_sigaction = xiosigaction_hasread; -#else /* Linux 2.0(.33) does not have sigaction.sa_sigaction */ - act.sa_handler = xiosigaction_hasread; -#endif - sigfillset(&act.sa_mask); - if (Sigaction(SIGUSR1, &act, NULL) < 0) { - /*! Linux man does not explicitely say that errno is defined */ - Warn1("sigaction(SIGUSR1, {&xiosigaction_subaddr_ok}, NULL): %s", strerror(errno)); - } - if (Sigaction(SIGCHLD, &act, NULL) < 0) { - /*! Linux man does not explicitely say that errno is defined */ - Warn1("sigaction(SIGCHLD, {&xiosigaction_subaddr_ok}, NULL): %s", strerror(errno)); - } - } -#else /* !HAVE_SIGACTION */ - /*!!!*/ - if (Signal(SIGUSR1, xiosigaction_hasread) == SIG_ERR) { - Warn1("signal(SIGUSR1, xiosigaction_hasread): %s", strerror(errno)); - } - if (Signal(SIGCHLD, xiosigaction_hasread) == SIG_ERR) { - Warn1("signal(SIGCHLD, xiosigaction_hasread): %s", strerror(errno)); - } -#endif /* !HAVE_SIGACTION */ + xiosetchilddied(); } while (true) { /* but we only loop if fork option is set */ @@ -1314,6 +1191,7 @@ int _xioopen_dgram_recvfrom(struct single *xfd, int xioflags, socklen_t palen = sizeof(_peername); /* peer address size */ char ctrlbuff[1024]; /* ancillary messages */ struct msghdr msgh = {0}; + int trigger[2]; /* for socketpair that indicates consumption of packet */ int rc; socket_init(pf, pa); @@ -1397,29 +1275,22 @@ int _xioopen_dgram_recvfrom(struct single *xfd, int xioflags, xfd->salen = palen; if (dofork) { - sigset_t oldset, mask_sigchldusr1; - - /* we must prevent that the current packet triggers another fork; - therefore we wait for a signal from the recent child: USR1 - indicates that is has consumed the last packet; CHLD means it has - terminated */ - /* block SIGCHLD and SIGUSR1 until parent is ready to react */ - Sigprocmask(SIG_BLOCK, NULL, &mask_sigchldusr1); - sigaddset(&mask_sigchldusr1, SIGCHLD); - sigaddset(&mask_sigchldusr1, SIGUSR1); - Sigprocmask(SIG_SETMASK, &mask_sigchldusr1, &oldset); + Info("Generating socketpair that triggers parent when packet has been consumed"); + if (Socketpair(PF_UNIX, SOCK_STREAM, 0, trigger) < 0) { + Error1("socketpair(PF_UNIX, SOCK_STREAM, 0, ...): %s", strerror(errno)); + } if ((pid = xio_fork(false, level)) < 0) { + Close(trigger[0]); + Close(trigger[1]); Close(xfd->fd); - Sigprocmask(SIG_SETMASK, &oldset, NULL); return STAT_RETRYLATER; } if (pid == 0) { /* child */ - /* no reason to block SIGCHLD in child process */ - Sigprocmask(SIG_SETMASK, &oldset, NULL); - xfd->ppid = Getppid(); /* send parent a signal when packet has - been consumed */ + Close(trigger[0]); + xfd->triggerfd = trigger[1]; + Fcntl_l(xfd->triggerfd, F_SETFD, FD_CLOEXEC); #if WITH_RETRY /* !? */ @@ -1437,29 +1308,14 @@ int _xioopen_dgram_recvfrom(struct single *xfd, int xioflags, break; } - xio_waitingfor = pid; + /* Parent */ + Close(trigger[1]); - do { -#if HAVE_PSELECT - { - struct timespec timeout = { LONG_MAX, 0 }; - Pselect(0, NULL, NULL, NULL, &timeout, &oldset); - Sigprocmask(SIG_SETMASK, &oldset, NULL); - } -#else /* ! HAVE_PSELECT */ - /* now we are ready to handle signals */ - Sigprocmask(SIG_SETMASK, &oldset, NULL); - Sleep(1); /* any signal speeds up return */ -#endif /* ! HAVE_PSELECT */ - } while (!xio_hashappened) ; - xio_hashappened = false; + { + char buf[1]; + while (Read(trigger[0], buf, 1) < 0 && errno == EINTR) ; + } - if (xio_childstatus != 0) { - char buff[512]; - Recv(xfd->fd, buff, sizeof(buff), 0); - xio_childstatus = 0; - Info("drop data because of child exit failed"); - } Info("continue listening"); } else { break; diff --git a/xio-udp.c b/xio-udp.c index 4359510..074f4f9 100644 --- a/xio-udp.c +++ b/xio-udp.c @@ -226,7 +226,9 @@ int _xioopen_ipdgram_listen(struct single *sfd, } /* server: continue loop with socket()+recvfrom() */ - /* when we dont close this we get awkward behaviour on Linux 2.4: + /* This avoids the requirement of a sync (trigger) mechanism as with + RECVFROM addresses */ + /* And when we dont close this we got awkward behaviour on Linux 2.4: recvfrom gives 0 bytes with invalid socket address */ if (Close(sfd->fd) < 0) { Info2("close(%d): %s", sfd->fd, strerror(errno)); diff --git a/xio.h b/xio.h index 92035e6..286cee4 100644 --- a/xio.h +++ b/xio.h @@ -164,6 +164,7 @@ typedef struct single { size_t actbytes; /* so many bytes still to be read (when readbytes!=0)*/ xiolock_t lock; /* parameters of lockfile */ bool havelock; /* we are happy owner of the above lock */ + int triggerfd; /* close this FD in child process to signal parent */ bool cool_write; /* downlevel EPIPE, ECONNRESET to notice */ /* until here, keep consistent with bipipe.dual ! */ int argc; /* number of fields in argv */ @@ -199,7 +200,6 @@ typedef struct single { struct termios savetty; /* save orig tty settings for later restore */ #endif /* WITH_TERMIOS */ int (*sigchild)(struct single *); /* callback after sigchild */ - pid_t ppid; /* parent pid, only if we send it signals */ int escape; /* escape character; -1 for no escape */ bool actescape; /* escape character found in input data */ union { @@ -312,7 +312,9 @@ typedef union bipipe { size_t actbytes; /* so many bytes still to be read */ xiolock_t lock; /* parameters of lockfile */ bool havelock; /* we are happy owner of the above lock */ - xiosingle_t *stream[2]; /* input stream, output stream */ + int triggerfd; /* close this FD in child process to notify parent */ + bool cool_write; /* downlevel EPIPE, ECONNRESET to notice */ + struct single *stream[2]; /* input stream, output stream */ } dual; } xiofile_t; diff --git a/xioinitialize.c b/xioinitialize.c index 81925e9..4118ab6 100644 --- a/xioinitialize.c +++ b/xioinitialize.c @@ -217,6 +217,15 @@ pid_t xio_fork(bool subchild, int level) { if (!subchild) { /* set SOCAT_PID to new value */ xiosetenvulong("PID", pid, 1); + } else { + /* Make sure the sub process does not hold the trigger pipe open */ + if (sock1 != NULL) { + struct single *sfd; + sfd = XIO_RDSTREAM(sock1); + if (sfd->triggerfd >= 0) Close(sfd->triggerfd); + sfd = XIO_WRSTREAM(sock1); + if (sfd->triggerfd >= 0) Close(sfd->triggerfd); + } } /* gdb recommends to have env controlled sleep after fork */ if (forkwaitstring = getenv("SOCAT_FORK_WAIT")) { diff --git a/xioopen.c b/xioopen.c index f879365..b93999b 100644 --- a/xioopen.c +++ b/xioopen.c @@ -363,7 +363,7 @@ static xiofile_t *xioallocfd(void) { #endif /* WITH_RETRY */ /* fd->common.ignoreeof = false; */ /* fd->common.eof = 0; */ - + fd->stream.triggerfd = -1; fd->stream.fd = -1; fd->stream.dtype = XIODATA_STREAM; #if _WITH_SOCKET diff --git a/xioread.c b/xioread.c index b05db3f..9df0ee2 100644 --- a/xioread.c +++ b/xioread.c @@ -271,8 +271,10 @@ ssize_t xioread(xiofile_t *file, void *buff, size_t bufsiz) { #else Shutdown(pipe->fd, SHUT_RD); #endif - if (pipe->ppid > 0) { - Kill(pipe->ppid, SIGUSR1); + if (pipe->triggerfd >= 0) { + Info("notifying parent that socket is ready again"); + Close(pipe->triggerfd); + pipe->triggerfd = -1; } } diff --git a/xiosigchld.c b/xiosigchld.c index 98abb70..81f5dbc 100644 --- a/xiosigchld.c +++ b/xiosigchld.c @@ -112,7 +112,6 @@ void childdied(int signum) { return; } /*! indent */ - if (num_child) num_child--; /* check if it was a registered child process */ i = 0; while (i < XIO_MAXSOCK) { @@ -121,6 +120,7 @@ void childdied(int signum) { } if (i == XIO_MAXSOCK) { Info2("childdied(%d): cannot identify child %d", signum, pid); + if (num_child) num_child--; if (nextunknown == NUMUNKNOWN) { nextunknown = 0; }