From c2196d6f159b2425c8fc619d0cd3cafc287c0e27 Mon Sep 17 00:00:00 2001 From: Gerhard Rieger Date: Thu, 26 Oct 2023 18:42:41 +0200 Subject: [PATCH] Output statistics per option and SIGUSR1 --- CHANGES | 4 ++ Makefile.in | 2 +- config.h.in | 1 + configure.ac | 8 +++ doc/socat.yo | 15 +++++ procan.c | 3 + socat.c | 104 +++++++++++++++++++++++++++++++++++ test.sh | 143 +++++++++++++++++++++++++++++++++++++++++++++--- xio-named.c | 5 +- xio-pipe.c | 2 +- xio.h | 9 ++- xioclose.c | 4 +- xioexit.c | 3 +- xioinitialize.c | 13 +++-- xioread.c | 8 +-- xioshutdown.c | 2 +- xiowrite.c | 4 +- 17 files changed, 304 insertions(+), 26 deletions(-) diff --git a/CHANGES b/CHANGES index a1c1a60..0cf1165 100644 --- a/CHANGES +++ b/CHANGES @@ -41,6 +41,10 @@ Features: references. Test: VARS_IN_SNIFFPATH + Socat option --statistics logs final byte and packet counter values + before exit. Signal USR1 logs actual values. + Tests: OPTION_STATISTICS SIGUSR1_STATISTICS + Corrections: 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/ diff --git a/Makefile.in b/Makefile.in index 3ee3b44..55dd355 100644 --- a/Makefile.in +++ b/Makefile.in @@ -76,7 +76,7 @@ HFILES = sycls.h sslcls.h error.h dalan.h procan.h filan.h hostan.h sysincludes. DOCFILES = README README.FIPS CHANGES FILES EXAMPLES PORTING SECURITY DEVELOPMENT doc/socat.yo doc/socat.1 doc/socat.html FAQ BUGREPORTS COPYING COPYING.OpenSSL doc/dest-unreach.css doc/socat-openssltunnel.html doc/socat-multicast.html doc/socat-tun.html doc/socat-genericsocket.html SHFILES = daemon.sh mail.sh ftp.sh readline.sh \ socat_buildscript_for_android.sh -TESTFILES = test.sh socks4echo.sh proxyecho.sh gatherinfo.sh readline-test.sh \ +TESTFILES = test.sh socks4echo.sh proxyecho.sh readline-test.sh \ proxy.sh socks4a-echo.sh all: progs doc diff --git a/config.h.in b/config.h.in index a80a5ba..6ca2507 100644 --- a/config.h.in +++ b/config.h.in @@ -672,6 +672,7 @@ #undef HAVE_GETGROUPLIST #undef WITH_HELP +#undef WITH_STATS #undef WITH_STDIO #undef WITH_FDNUM #undef WITH_FILE diff --git a/configure.ac b/configure.ac index 7fdbe2c..a48b127 100644 --- a/configure.ac +++ b/configure.ac @@ -163,6 +163,14 @@ AC_ARG_ENABLE(help, [ --disable-help disable help], esac], [AC_DEFINE(WITH_HELP) AC_MSG_RESULT(yes)]) +AC_MSG_CHECKING(whether to include transfer statistics) +AC_ARG_ENABLE(stats, [ --disable-stats disable stats], + [case "$enableval" in + no) AC_MSG_RESULT(no);; + *) AC_DEFINE(WITH_STATS) AC_MSG_RESULT(yes);; + esac], + [AC_DEFINE(WITH_STATS) AC_MSG_RESULT(yes)]) + AC_MSG_CHECKING(whether to include STDIO support) AC_ARG_ENABLE(stdio, [ --disable-stdio disable STDIO support], [case "$enableval" in diff --git a/doc/socat.yo b/doc/socat.yo index 2fdca50..b94b8b4 100644 --- a/doc/socat.yo +++ b/doc/socat.yo @@ -223,6 +223,11 @@ label(option_4)dit(bf(tt(-4))) label(option_6)dit(bf(tt(-6))) Use IP version 6 in case that the addresses do not implicitly or explicitly specify a version. +label(option_statistics)dit(bf(tt(--statistics))) + Logs transfer statistics (bytes and blocks counters for both directions) + before terminating Socat.nl() + See also link(signal USR1)(signal_usr1).nl() + This feature is experimental and might change in future versions. enddit() @@ -4057,6 +4062,16 @@ manpagefiles() /usr/bin/procan +label(SIGNALS) +manpagesection(SIGNALS) + +description( +label(signal_usr1)dit(SIGUSR1:) Causes logging of current transfer statistics. +nl() +See also link(option --statistics)(option_statistics) +) + + label(ENVIRONMENT_VARIABLES) manpagesection(ENVIRONMENT VARIABLES) diff --git a/procan.c b/procan.c index d0d92f3..0265c2b 100644 --- a/procan.c +++ b/procan.c @@ -157,6 +157,9 @@ int procan(FILE *outfile) { #endif #ifdef SIZE_MAX fprintf(outfile, "SIZE_MAX = %-24lu\n", SIZE_MAX); +#endif +#ifdef PIPE_BUF + fprintf(outfile, "PIPE_BUF = %-24d\n", PIPE_BUF); #endif } diff --git a/socat.c b/socat.c index fa39d3b..4e0e618 100644 --- a/socat.c +++ b/socat.c @@ -38,6 +38,7 @@ struct { bool righttoleft; /* first addr wo, second addr ro */ xiolock_t lock; /* a lock file */ unsigned long log_sigs; /* signals to be caught just for logging */ + bool statistics; /* log statistics on exit */ } socat_opts = { 8192, /* bufsiz */ false, /* verbose */ @@ -52,6 +53,7 @@ struct { false, /* righttoleft */ { NULL, 0 }, /* lock */ 1< log to file\n", fd); fputs(" -ls log to stderr (default if no other log)\n", fd); @@ -472,6 +495,16 @@ void socat_version(FILE *fd) { fprintf(fd, " running on %s version %s, release %s, machine %s\n", ubuf.sysname, ubuf.version, ubuf.release, ubuf.machine); fputs("features:\n", fd); +#ifdef WITH_HELP + fprintf(fd, " #define WITH_HELP %d\n", WITH_HELP); +#else + fputs(" #undef WITH_HELP\n", fd); +#endif +#ifdef WITH_STATS + fprintf(fd, " #define WITH_STATS %d\n", WITH_STATS); +#else + fputs(" #undef WITH_STATS\n", fd); +#endif #ifdef WITH_STDIO fprintf(fd, " #define WITH_STDIO %d\n", WITH_STDIO); #else @@ -1339,6 +1372,10 @@ int xiotransfer(xiofile_t *inpipe, xiofile_t *outpipe, } if (bytes > 0) { +#if WITH_STATS + ++XIO_RDSTREAM(inpipe)->blocks_read; + XIO_RDSTREAM(inpipe)->bytes_read += bytes; +#endif /* handle escape char */ if (XIO_RDSTREAM(inpipe)->escape != -1) { /* check input data for escape char */ @@ -1490,6 +1527,10 @@ int xiotransfer(xiofile_t *inpipe, xiofile_t *outpipe, } else { Info3("transferred "F_Zu" bytes from %d to %d", writt, XIO_GETRDFD(inpipe), XIO_GETWRFD(outpipe)); +#if WITH_STATS + ++XIO_WRSTREAM(outpipe)->blocks_written; + XIO_WRSTREAM(outpipe)->bytes_written += writt; +#endif } } return writt; @@ -1671,3 +1712,66 @@ static int socat_newchild(void) { havelock = false; return 0; } + + +#if WITH_STATS +void socat_signal_logstats(int signum) { + diag_in_handler = 1; + Notice1("socat_signal_logstats(): handling signal %d", signum); + socat_print_stats(); + Notice1("socat_signal_logstats(): finishing signal %d", signum); + diag_in_handler = 0; +} +#endif /* WITH_STATS */ + +#if WITH_STATS +static void socat_print_stats(void) +{ + const char ltorf0[] = "STATISTICS: left to right: %%%ullu packets(s), %%%ullu byte(s)"; + const char rtolf0[] = "STATISTICS: right to left: %%%ullu packets(s), %%%ullu byte(s)"; + char ltorf1[sizeof(ltorf0)]; /* final printf format with lengths of number */ + char rtolf1[sizeof(rtolf0)]; /* final printf format with lengths of number */ + unsigned int blocksd = 1, bytesd = 1; /* number of digits in output */ + struct single *sock1w, *sock2w; + int savelevel; + + if (sock1 == NULL || sock2 == NULL) { + Warn("transfer engine not yet started, statistics not available"); + return; + } + if ((sock1->tag & ~XIO_TAG_CLOSED) == XIO_TAG_DUAL) { + sock1w = sock1->dual.stream[1]; + } else { + sock1w = &sock1->stream; + } + if ((sock2->tag & ~XIO_TAG_CLOSED) == XIO_TAG_DUAL) { + sock2w = sock2->dual.stream[1]; + } else { + sock2w = &sock2->stream; + } + if (!socat_opts.righttoleft && !socat_opts.righttoleft) { + /* Both directions - format output */ + unsigned long long int maxblocks = + Max(sock1w->blocks_written, sock2w->blocks_written); + unsigned long long int maxbytes = + Max(sock1w->bytes_written, sock2w->bytes_written); + /* Calculate number of digits */ + while (maxblocks >= 10) { ++blocksd; maxblocks /= 10; } + while (maxbytes >= 10) { ++bytesd; maxbytes /= 10; } + } + snprintf(ltorf1, sizeof(ltorf1), ltorf0, blocksd, bytesd); + snprintf(rtolf1, sizeof(rtolf1), rtolf0, blocksd, bytesd); + /* Statistics are E_INFO level; make sure they are printed anyway */ + savelevel = diag_get_int('d'); + diag_set_int('d', E_INFO); + Warn("statistics are experimental"); + if (!socat_opts.righttoleft) { + Info2(ltorf1, sock2w->blocks_written, sock2w->bytes_written); + } + if (!socat_opts.lefttoright) { + Info2(rtolf1, sock1w->blocks_written, sock1w->bytes_written); + } + diag_set_int('d', savelevel); + return; +} +#endif /* WITH_STATs */ diff --git a/test.sh b/test.sh index edcfcfa..1ab7ef2 100755 --- a/test.sh +++ b/test.sh @@ -278,7 +278,7 @@ tolower () { psleep () { local T="$1" [ "$T" = 0 ] && T=0.000002 - $SOCAT -T "$T" pipe pipe + $SOCAT -T "$T" pipe pipe 2>/dev/null } # time in microseconds to wait in some situations if ! type usleep >/dev/null 2>&1 || @@ -289,7 +289,7 @@ if ! type usleep >/dev/null 2>&1 || *???????) S="${n%??????}"; uS="${n:${#n}-6}" ;; *) S=0; uS="00000$n"; uS="${uS:${#uS}-6}" ;; esac - $SOCAT -T "$S.$uS" pipe pipe + $SOCAT -T "$S.$uS" pipe pipe 2>/dev/null } fi #USLEEP=usleep @@ -974,6 +974,12 @@ runssctp6 () { return 0; } +# check if UNIX domain sockets work +runsunix () { + # for now... + return 0; +} + # wait until an IP4 protocol is ready waitip4proto () { local proto="$1" @@ -13399,7 +13405,7 @@ N=$((N+1)) # Test the OpenSSL SNI feature NAME=OPENSSL_SNI case "$TESTS" in -*%$N%*|*%functions%*|*%socket%*|*%openssl%*|*%$NAME%*) +*%$N%*|*%functions%*|*%socket%*|*%openssl%*|*%foreign%*|*%$NAME%*) TEST="$NAME: Test the OpenSSL SNI feature" # Connect to a server that is known to use SNI. Use an SNI name, not the # certifications default name. When the TLS connection is established @@ -13446,7 +13452,7 @@ N=$((N+1)) # Test the openssl-no-sni option NAME=OPENSSL_NO_SNI case "$TESTS" in -*%$N%*|*%functions%*|*%socket%*|*%openssl%*|*%$NAME%*) +*%$N%*|*%functions%*|*%socket%*|*%openssl%*|*%foreign%*|*%$NAME%*) TEST="$NAME: Test the openssl-no-sni option" # Connect to a server that is known to use SNI. Use an SNI name, not the # certifications default name, and use option openssl-no-sni. @@ -14498,7 +14504,7 @@ CMD="$TRACE $SOCAT $opts -d -d -d -d /dev/null UDP4-SENDTO:$LOCALHOST:$PORT,lowp printf "test $F_n $TEST... " $N $CMD >/dev/null 2>"${te}" rc1=$? -LOWPORT=$(grep 'D bind(.*:' $te |sed 's/.*:\([0-9][0-9]*\),.*/\1/') +LOWPORT=$(grep '[DE] bind(.*:' $te |sed 's/.*:\([0-9][0-9]*\),.*/\1/') #echo "LOWPORT=\"$LOWPORT\"" >&2 #type socat >&2 if [[ $LOWPORT =~ [0-9][0-9]* ]] && [ "$LOWPORT" -ge 640 -a "$LOWPORT" -le 1023 ]; then @@ -14966,7 +14972,6 @@ 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")" @@ -15054,8 +15059,8 @@ fi # NUMCOND esac N=$((N+1)) done <<<" -UDP4 UDP udp4 127.0.0.1 PORT -UDP6 UDP udp4 [::1] PORT +UDP4 UDP ip4 127.0.0.1 PORT +UDP6 UDP ip6 [::1] PORT UNIX unix unix $td/test\$N.server - " @@ -15395,6 +15400,127 @@ esac N=$((N+1)) +# Test logging of statistics on Socat option --statistics +NAME=OPTION_STATISTICS +case "$TESTS" in +*%$N%*|*%functions%*|*%stats%*|*%system%*|*%stdio%*|*%$NAME%*) +TEST="$NAME: Socat option --statistics" +# Invoke Socat with option --statistics, transfer some date, and check the log +# file for the values +if ! eval $NUMCOND; then :; +elif ! $(type >/dev/null 2>&1); then + $PRINTF "test $F_n $TEST... ${YELLOW}tee not available${NORMAL}\n" $N + numCANT=$((numCANT+1)) + listCANT="$listCANT $N" +elif ! F=$(testfeats STATS STDIO SYSTEM); then + $PRINTF "test $F_n $TEST... ${YELLOW}Feature $F not available in $SOCAT${NORMAL}\n" $N + numCANT=$((numCANT+1)) + listCANT="$listCANT $N" +elif ! A=$(testaddrs STDIO SYSTEM); then + $PRINTF "test $F_n $TEST... ${YELLOW}Address $A not available${NORMAL}\n" $N + numCANT=$((numCANT+1)) + listCANT="$listCANT $N" +elif ! o=$(testoptions pty cfmakeraw) >/dev/null; then + $PRINTF "test $F_n $TEST... ${YELLOW}Option $o not available${NORMAL}\n" $N + numCANT=$((numCANT+1)) + listCANT="$listCANT $N" +else +tf="$td/test$N.stdout" +te="$td/test$N.stderr" +tdiff="$td/test$N.diff" +da="test$N $(date) $RANDOM" +CMD0="$TRACE $SOCAT $opts --statistics STDIO SYSTEM:'tee /dev/stdout',pty,cfmakeraw" +printf "test $F_n $TEST... " $N +echo "$da" |eval "$CMD0" >"${tf}0" 2>"${te}0" +rc0=$? +if [ $rc0 -ne 0 ]; then + # The test could not run meaningfully + $PRINTF "$CANT\n" + if [ "$VERBOSE" ]; then echo "$CMD0"; fi + if [ "$DEBUG" ]; then cat "${te}0" >&2; fi + numCANT=$((numCANT+1)) + listCANT="$listCANT $N" +elif [ $(grep STATISTICS "${te}0" |wc -l) -eq 2 ]; then + $PRINTF "$OK\n" + if [ "$VERBOSE" ]; then echo "$CMD0"; fi + if [ "$DEBUG" ]; then cat "${te}0" >&2; fi + numOK=$((numOK+1)) +else + $PRINTF "$FAILED\n" + echo "$CMD0 &" + cat "${te}0" >&2 + numFAIL=$((numFAIL+1)) + listFAIL="$listFAIL $N" + namesFAIL="$namesFAIL $NAME" +fi +fi # NUMCOND + ;; +esac +N=$((N+1)) + +# Test logging of statistics on SIGUSR1 +NAME=SIGUSR1_STATISTICS +case "$TESTS" in +*%$N%*|*%functions%*|*%signal%*|*%stats%*|*%system%*|*%stdio%*|*%$NAME%*) +TEST="$NAME: statistics on SIGUSR1" +# Invoke Socat without option --statistics, transfer some date, send signal +# USR1,and check the log file for the values +if ! eval $NUMCOND; then :; +elif ! $(type tee >/dev/null 2>&1); then + $PRINTF "test $F_n $TEST... ${YELLOW}tee not available${NORMAL}\n" $N + numCANT=$((numCANT+1)) + listCANT="$listCANT $N" +elif ! $(type pkill >/dev/null 2>&1); then + $PRINTF "test $F_n $TEST... ${YELLOW}pkill not available${NORMAL}\n" $N + numCANT=$((numCANT+1)) + listCANT="$listCANT $N" +elif ! F=$(testfeats STATS STDIO SYSTEM); then + $PRINTF "test $F_n $TEST... ${YELLOW}Feature $F not available in $SOCAT${NORMAL}\n" $N + numCANT=$((numCANT+1)) + listCANT="$listCANT $N" +elif ! A=$(testaddrs STDIO SYSTEM); then + $PRINTF "test $F_n $TEST... ${YELLOW}Address $A not available${NORMAL}\n" $N + numCANT=$((numCANT+1)) + listCANT="$listCANT $N" +elif ! o=$(testoptions pty cfmakeraw) >/dev/null; then + $PRINTF "test $F_n $TEST... ${YELLOW}Option $o not available${NORMAL}\n" $N + numCANT=$((numCANT+1)) + listCANT="$listCANT $N" +else +tf="$td/test$N.stdout" +te="$td/test$N.stderr" +tdiff="$td/test$N.diff" +da="test$N $(date) $RANDOM" +CMD0="$TRACE $SOCAT $opts STDIO SYSTEM:'tee /dev/stdout 2>/dev/null',pty,cfmakeraw" +#set -vx +printf "test $F_n $TEST... " $N +{ echo "$da"; relsleep 3; } |eval "$CMD0" >"${tf}0" 2>"${te}0" & +pid0=$! +relsleep 2 +TTY=$(tty |sed 's|/dev/||') +pkill -USR1 -t $TTY socat || { echo "pkill -t $TTY -USR1 socat"; } +relsleep 1 +pkill -t $TTY socat +wait +if [ "$(grep STATISTICS "${te}0" |wc -l)" -eq 2 ]; then + $PRINTF "$OK\n" + if [ "$VERBOSE" ]; then echo "$CMD0"; fi + if [ "$DEBUG" ]; then cat "${te}0" >&2; fi + numOK=$((numOK+1)) +else + $PRINTF "$FAILED\n" + echo "$CMD0 &" + cat "${te}0" >&2 + numFAIL=$((numFAIL+1)) + listFAIL="$listFAIL $N" + namesFAIL="$namesFAIL $NAME" +fi +fi # NUMCOND + ;; +esac +N=$((N+1)) + + # end of common tests ################################################################################## @@ -15528,6 +15654,7 @@ exit NAME=SHORT_UNIQUE_TESTNAME case "$TESTS" in *%$N%*|*%functions%*|*%bugs%*|*%socket%*|*%$NAME%*) +#*%foreign%*|*%root%*|*%listen%*|*%fork%*|*%ip4%*|*%tcp4%*|*%bug%*|... TEST="$NAME: give a one line description of test" # Describe how the test is performed, and what's the success criteria if ! eval $NUMCOND; then :; diff --git a/xio-named.c b/xio-named.c index da4f10e..ceae370 100644 --- a/xio-named.c +++ b/xio-named.c @@ -197,7 +197,10 @@ int _xioopen_open(const char *path, int rw, struct opt *opts) { retropt_modet(opts, OPT_PERM, &mode); - if ((fd = Open(path, flags, mode)) < 0) { + do { + fd = Open(path, flags, mode); + } while (fd < 0 && errno == EINTR); + if (fd < 0) { Error4("open(\"%s\", 0%lo, 0%03o): %s", path, flags, mode, strerror(errno)); return STAT_RETRYLATER; diff --git a/xio-pipe.c b/xio-pipe.c index 4a18f32..c860fb8 100644 --- a/xio-pipe.c +++ b/xio-pipe.c @@ -153,7 +153,7 @@ static int xioopen_fifo(int argc, const char *argv[], struct opt *opts, int xiof } } else { /* exists */ - Debug1("xioopen_fifo(\"%s\"): already exist, opening it", pipename); + Info1("xioopen_fifo(\"%s\"): already exist, opening it", pipename); Notice3("opening %s \"%s\" for %s", filetypenames[(pipstat.st_mode&S_IFMT)>>12], pipename, ddirection[rw]); diff --git a/xio.h b/xio.h index 33c2eb4..cb23f65 100644 --- a/xio.h +++ b/xio.h @@ -85,8 +85,9 @@ enum xiotag { XIO_TAG_RDONLY, /* this is a single read-only stream */ XIO_TAG_WRONLY, /* this is a single write-only stream */ XIO_TAG_RDWR, /* this is a single read-write stream */ - XIO_TAG_DUAL /* this is a dual stream, consisting of two single + XIO_TAG_DUAL, /* this is a dual stream, consisting of two single streams */ + XIO_TAG_CLOSED=8, /* close, additional bit */ } ; /* Keep condition consistent with xioopts.h:GROUP_*! */ @@ -278,6 +279,12 @@ typedef struct single { } tun; #endif /* WITH_TUN */ } para; +#if WITH_STATS + unsigned long long blocks_read; + unsigned long long bytes_read; + unsigned long long blocks_written; + unsigned long long bytes_written; +#endif /* WITH_STATS */ } xiosingle_t; /* rw: 0..read, 1..write, 2..r/w */ diff --git a/xioclose.c b/xioclose.c index fe3dd1f..3f87119 100644 --- a/xioclose.c +++ b/xioclose.c @@ -93,7 +93,7 @@ int xioclose1(struct single *pipe) { free(pipe->unlink_close); } - pipe->tag = XIO_TAG_INVALID; + pipe->tag |= XIO_TAG_CLOSED; return 0; /*! */ } @@ -111,7 +111,7 @@ int xioclose(xiofile_t *file) { if (file->tag == XIO_TAG_DUAL) { result = xioclose1(file->dual.stream[0]); result |= xioclose1(file->dual.stream[1]); - file->tag = XIO_TAG_INVALID; + file->tag |= XIO_TAG_CLOSED; } else { result = xioclose1(&file->stream); } diff --git a/xioexit.c b/xioexit.c index 294fbe2..c3582f5 100644 --- a/xioexit.c +++ b/xioexit.c @@ -18,7 +18,8 @@ void xioexit(void) { diag_in_handler = 0; Debug("starting xioexit()"); for (i = 0; i < XIO_MAXSOCK; ++i) { - if (sock[i] != NULL && sock[i]->tag != XIO_TAG_INVALID) { + if (sock[i] != NULL && sock[i]->tag != XIO_TAG_INVALID && + !(sock[i]->tag & XIO_TAG_CLOSED)) { xioclose(sock[i]); } } diff --git a/xioinitialize.c b/xioinitialize.c index 43c2a4d..4e1412f 100644 --- a/xioinitialize.c +++ b/xioinitialize.c @@ -121,22 +121,27 @@ void xiodroplocks(void) { int i; for (i = 0; i < XIO_MAXSOCK; ++i) { - if (sock[i] != NULL && sock[i]->tag != XIO_TAG_INVALID) { + if (sock[i] != NULL && sock[i]->tag != XIO_TAG_INVALID && + !(sock[i]->tag & XIO_TAG_CLOSED)) { xiofiledroplock(sock[i]); } } } -/* consider an invokation like this: - socat -u exec:'some program that accepts data' tcp-l:...,fork - we do not want the program to be killed by the first tcp-l sub process, it's +/* Consider an invocation like this: + socat -u EXEC:'some program that accepts data' TCP-L:...,fork + we do not want the program to be killed by the first TCP-L sub process, it's better if it survives all sub processes. Thus, it must not be killed when the sub process delivers EOF. Also, a socket that is reused in sub processes should not be shut down (affects the connection), but closed (affects only sub processes copy of file descriptor) */ static int xio_nokill(xiofile_t *sock) { int result = 0; + + if (sock->tag & XIO_TAG_CLOSED) { + return -1; + } switch (sock->tag) { case XIO_TAG_INVALID: default: diff --git a/xioread.c b/xioread.c index 9df0ee2..de8c931 100644 --- a/xioread.c +++ b/xioread.c @@ -23,7 +23,7 @@ ssize_t xioread(xiofile_t *file, void *buff, size_t bufsiz) { struct single *pipe; int _errno; - if (file->tag == XIO_TAG_INVALID) { + if (file->tag == XIO_TAG_INVALID || file->tag & XIO_TAG_CLOSED) { Error1("xioread(): invalid xiofile descriptor %p", file); errno = EINVAL; return -1; @@ -31,7 +31,7 @@ ssize_t xioread(xiofile_t *file, void *buff, size_t bufsiz) { if (file->tag == XIO_TAG_DUAL) { pipe = file->dual.stream[0]; - if (pipe->tag == XIO_TAG_INVALID) { + if (pipe->tag == XIO_TAG_INVALID || file->tag & XIO_TAG_CLOSED) { Error1("xioread(): invalid xiofile sub descriptor %p[0]", file); errno = EINVAL; return -1; @@ -433,7 +433,7 @@ ssize_t xioread(xiofile_t *file, void *buff, size_t bufsiz) { ssize_t xiopending(xiofile_t *file) { struct single *pipe; - if (file->tag == XIO_TAG_INVALID) { + if (file->tag == XIO_TAG_INVALID || file->tag & XIO_TAG_CLOSED) { Error1("xiopending(): invalid xiofile descriptor %p", file); errno = EINVAL; return -1; @@ -441,7 +441,7 @@ ssize_t xiopending(xiofile_t *file) { if (file->tag == XIO_TAG_DUAL) { pipe = file->dual.stream[0]; - if (pipe->tag == XIO_TAG_INVALID) { + if (pipe->tag == XIO_TAG_INVALID || file->tag & XIO_TAG_CLOSED) { Error1("xiopending(): invalid xiofile sub descriptor %p[0]", file); errno = EINVAL; return -1; diff --git a/xioshutdown.c b/xioshutdown.c index ae004a6..4fe51a0 100644 --- a/xioshutdown.c +++ b/xioshutdown.c @@ -25,7 +25,7 @@ static void signal_kill_pid(int dummy) { int xioshutdown(xiofile_t *sock, int how) { int result = 0; - if (sock->tag == XIO_TAG_INVALID) { + if (sock->tag == XIO_TAG_INVALID || sock->tag & XIO_TAG_CLOSED) { Error("xioshutdown(): invalid file descriptor"); errno = EINVAL; return -1; diff --git a/xiowrite.c b/xiowrite.c index 3ce86d0..143e1ac 100644 --- a/xiowrite.c +++ b/xiowrite.c @@ -22,7 +22,7 @@ ssize_t xiowrite(xiofile_t *file, const void *buff, size_t bytes) { struct single *pipe; int _errno; - if (file->tag == XIO_TAG_INVALID) { + if (file->tag == XIO_TAG_INVALID || file->tag & XIO_TAG_CLOSED) { Error1("xiowrite(): invalid xiofile descriptor %p", file); errno = EINVAL; return -1; @@ -30,7 +30,7 @@ ssize_t xiowrite(xiofile_t *file, const void *buff, size_t bytes) { if (file->tag == XIO_TAG_DUAL) { pipe = file->dual.stream[1]; - if (pipe->tag == XIO_TAG_INVALID) { + if (pipe->tag == XIO_TAG_INVALID || file->tag & XIO_TAG_CLOSED) { Error1("xiowrite(): invalid xiofile sub descriptor %p[1]", file); errno = EINVAL; return -1;