Output statistics per option and SIGUSR1
diff --git a/CHANGES b/CHANGES
index a1c1a60..0cf1165 100644
--- a/CHANGES
+++ b/CHANGES
@@ -41,6 +41,10 @@
 	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 @@
 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 @@
 	       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_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 @@
 /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
@@ -158,6 +158,9 @@
 #ifdef SIZE_MAX
       fprintf(outfile, "SIZE_MAX                  = %-24lu\n", SIZE_MAX);
 #endif
+#ifdef PIPE_BUF
+      fprintf(outfile, "PIPE_BUF                  = %-24d\n", PIPE_BUF);
+#endif
    }
 
    /* file descriptors */
diff --git a/socat.c b/socat.c
index fa39d3b..4e0e618 100644
--- a/socat.c
+++ b/socat.c
@@ -38,6 +38,7 @@
    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 @@
    false,	/* righttoleft */
    { NULL, 0 },	/* lock */
    1<<SIGHUP | 1<<SIGINT | 1<<SIGQUIT | 1<<SIGILL | 1<<SIGABRT | 1<<SIGBUS | 1<<SIGFPE | 1<<SIGSEGV | 1<<SIGTERM, 	/* log_sigs */
+   false	/* statistics */
 };
 
 void socat_usage(FILE *fd);
@@ -61,6 +63,7 @@
 int _socat(void);
 int cv_newline(unsigned char *buff, ssize_t *bytes, int lineterm1, int lineterm2);
 void socat_signal(int sig);
+void socat_signal_logstats(int sig);
 static int socat_sigchild(struct single *file);
 
 void lftocrlf(char **in, ssize_t *len, size_t bufsiz);
@@ -69,6 +72,7 @@
 static int socat_lock(void);
 static void socat_unlock(void);
 static int socat_newchild(void);
+static void socat_print_stats(void);
 
 static const char socatversion[] =
 #include "./VERSION"
@@ -323,6 +327,8 @@
       case '-':
 	 if (!strcmp("experimental", &arg1[0][2])) {
 	    xioparms.experimental = true;
+	 } else if (!strcmp("statistics", &arg1[0][2])) {
+	    socat_opts.statistics = true;
 	 } else {
 	    Error1("unknown option \"%s\"; use option \"-h\" for help", arg1[0]);
 	 }
@@ -374,7 +380,9 @@
 #endif /* WITH_MSGLEVEL <= E_DEBUG */
 
    {
+#if HAVE_SIGACTION
       struct sigaction act;
+#endif
       int i, m;
 
       sigfillset(&act.sa_mask); 	/* while in sighandler block all signals */
@@ -383,9 +391,20 @@
       /* not sure which signals should be caught and print a message */
       for (i = 0, m = 1; i < 8*sizeof(unsigned long); ++i, m <<= 1) {
 	 if (socat_opts.log_sigs & m) {
+#if HAVE_SIGACTION
 	    Sigaction(i,  &act, NULL);
+#else
+	    Signal(i, socat_signal);
+#endif
 	 }
       }
+
+#if HAVE_SIGACTION
+      act.sa_handler = socat_signal_logstats;
+      Sigaction(SIGUSR1, &act, NULL);
+#else
+      Signal(SIGUSR1, socat_signal_logstats);
+#endif
    }
    Signal(SIGPIPE, SIG_IGN);
 
@@ -400,6 +419,9 @@
    }
 
    Atexit(socat_unlock);
+   if (socat_opts.statistics) {
+      Atexit(socat_print_stats);
+   }
 
    result = socat(arg1[0], arg1[1]);
    if (result == EXIT_SUCCESS && engine_result != EXIT_SUCCESS) {
@@ -428,6 +450,7 @@
    fputs("      -D     analyze file descriptors before loop\n", fd);
 #endif
    fputs("      --experimental enable experimental features\n", fd);
+   fputs("      --statistics   output transfer statistics on exit\n", fd);
    fputs("      -ly[facility]  log to syslog, using facility (default is daemon)\n", fd);
    fputs("      -lf<logfile>   log to file\n", fd);
    fputs("      -ls            log to stderr (default if no other log)\n", fd);
@@ -472,6 +495,16 @@
    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 @@
 	 }
 
 	 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 @@
 	    } 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 @@
    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 @@
 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 @@
 	*???????) 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 @@
     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 @@
 # 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 @@
 # 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 @@
 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 @@
 
 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 @@
 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 @@
 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 @@
 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 @@
 
    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 @@
       }
    } 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 @@
    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 @@
       } 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 @@
       free(pipe->unlink_close);
    }
 
-   pipe->tag = XIO_TAG_INVALID;
+   pipe->tag |= XIO_TAG_CLOSED;
    return 0;	/*! */
 }
 
@@ -111,7 +111,7 @@
    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 @@
    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 @@
    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 @@
    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 @@
 
    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 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 @@
 
    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 @@
 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 @@
    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 @@
 
    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;