mirror of
https://repo.or.cz/socat.git
synced 2024-12-22 15:32:35 +00:00
Output statistics per option and SIGUSR1
This commit is contained in:
parent
2af6089436
commit
c2196d6f15
17 changed files with 304 additions and 26 deletions
4
CHANGES
4
CHANGES
|
@ -41,6 +41,10 @@ Features:
|
||||||
references.
|
references.
|
||||||
Test: VARS_IN_SNIFFPATH
|
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:
|
Corrections:
|
||||||
When a sub process (EXEC, SYSTEM) terminated with exit code other than
|
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/
|
0, its last sent data might have been lost depending on timing of read/
|
||||||
|
|
|
@ -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
|
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 \
|
SHFILES = daemon.sh mail.sh ftp.sh readline.sh \
|
||||||
socat_buildscript_for_android.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
|
proxy.sh socks4a-echo.sh
|
||||||
|
|
||||||
all: progs doc
|
all: progs doc
|
||||||
|
|
|
@ -672,6 +672,7 @@
|
||||||
#undef HAVE_GETGROUPLIST
|
#undef HAVE_GETGROUPLIST
|
||||||
|
|
||||||
#undef WITH_HELP
|
#undef WITH_HELP
|
||||||
|
#undef WITH_STATS
|
||||||
#undef WITH_STDIO
|
#undef WITH_STDIO
|
||||||
#undef WITH_FDNUM
|
#undef WITH_FDNUM
|
||||||
#undef WITH_FILE
|
#undef WITH_FILE
|
||||||
|
|
|
@ -163,6 +163,14 @@ AC_ARG_ENABLE(help, [ --disable-help disable help],
|
||||||
esac],
|
esac],
|
||||||
[AC_DEFINE(WITH_HELP) AC_MSG_RESULT(yes)])
|
[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_MSG_CHECKING(whether to include STDIO support)
|
||||||
AC_ARG_ENABLE(stdio, [ --disable-stdio disable STDIO support],
|
AC_ARG_ENABLE(stdio, [ --disable-stdio disable STDIO support],
|
||||||
[case "$enableval" in
|
[case "$enableval" in
|
||||||
|
|
15
doc/socat.yo
15
doc/socat.yo
|
@ -223,6 +223,11 @@ label(option_4)dit(bf(tt(-4)))
|
||||||
label(option_6)dit(bf(tt(-6)))
|
label(option_6)dit(bf(tt(-6)))
|
||||||
Use IP version 6 in case that the addresses do not implicitly or explicitly
|
Use IP version 6 in case that the addresses do not implicitly or explicitly
|
||||||
specify a version.
|
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()
|
enddit()
|
||||||
|
|
||||||
|
|
||||||
|
@ -4057,6 +4062,16 @@ manpagefiles()
|
||||||
/usr/bin/procan
|
/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)
|
label(ENVIRONMENT_VARIABLES)
|
||||||
manpagesection(ENVIRONMENT VARIABLES)
|
manpagesection(ENVIRONMENT VARIABLES)
|
||||||
|
|
||||||
|
|
3
procan.c
3
procan.c
|
@ -157,6 +157,9 @@ int procan(FILE *outfile) {
|
||||||
#endif
|
#endif
|
||||||
#ifdef SIZE_MAX
|
#ifdef SIZE_MAX
|
||||||
fprintf(outfile, "SIZE_MAX = %-24lu\n", SIZE_MAX);
|
fprintf(outfile, "SIZE_MAX = %-24lu\n", SIZE_MAX);
|
||||||
|
#endif
|
||||||
|
#ifdef PIPE_BUF
|
||||||
|
fprintf(outfile, "PIPE_BUF = %-24d\n", PIPE_BUF);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
104
socat.c
104
socat.c
|
@ -38,6 +38,7 @@ struct {
|
||||||
bool righttoleft; /* first addr wo, second addr ro */
|
bool righttoleft; /* first addr wo, second addr ro */
|
||||||
xiolock_t lock; /* a lock file */
|
xiolock_t lock; /* a lock file */
|
||||||
unsigned long log_sigs; /* signals to be caught just for logging */
|
unsigned long log_sigs; /* signals to be caught just for logging */
|
||||||
|
bool statistics; /* log statistics on exit */
|
||||||
} socat_opts = {
|
} socat_opts = {
|
||||||
8192, /* bufsiz */
|
8192, /* bufsiz */
|
||||||
false, /* verbose */
|
false, /* verbose */
|
||||||
|
@ -52,6 +53,7 @@ struct {
|
||||||
false, /* righttoleft */
|
false, /* righttoleft */
|
||||||
{ NULL, 0 }, /* lock */
|
{ NULL, 0 }, /* lock */
|
||||||
1<<SIGHUP | 1<<SIGINT | 1<<SIGQUIT | 1<<SIGILL | 1<<SIGABRT | 1<<SIGBUS | 1<<SIGFPE | 1<<SIGSEGV | 1<<SIGTERM, /* log_sigs */
|
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);
|
void socat_usage(FILE *fd);
|
||||||
|
@ -61,6 +63,7 @@ int socat(const char *address1, const char *address2);
|
||||||
int _socat(void);
|
int _socat(void);
|
||||||
int cv_newline(unsigned char *buff, ssize_t *bytes, int lineterm1, int lineterm2);
|
int cv_newline(unsigned char *buff, ssize_t *bytes, int lineterm1, int lineterm2);
|
||||||
void socat_signal(int sig);
|
void socat_signal(int sig);
|
||||||
|
void socat_signal_logstats(int sig);
|
||||||
static int socat_sigchild(struct single *file);
|
static int socat_sigchild(struct single *file);
|
||||||
|
|
||||||
void lftocrlf(char **in, ssize_t *len, size_t bufsiz);
|
void lftocrlf(char **in, ssize_t *len, size_t bufsiz);
|
||||||
|
@ -69,6 +72,7 @@ void crlftolf(char **in, ssize_t *len, size_t bufsiz);
|
||||||
static int socat_lock(void);
|
static int socat_lock(void);
|
||||||
static void socat_unlock(void);
|
static void socat_unlock(void);
|
||||||
static int socat_newchild(void);
|
static int socat_newchild(void);
|
||||||
|
static void socat_print_stats(void);
|
||||||
|
|
||||||
static const char socatversion[] =
|
static const char socatversion[] =
|
||||||
#include "./VERSION"
|
#include "./VERSION"
|
||||||
|
@ -323,6 +327,8 @@ int main(int argc, const char *argv[]) {
|
||||||
case '-':
|
case '-':
|
||||||
if (!strcmp("experimental", &arg1[0][2])) {
|
if (!strcmp("experimental", &arg1[0][2])) {
|
||||||
xioparms.experimental = true;
|
xioparms.experimental = true;
|
||||||
|
} else if (!strcmp("statistics", &arg1[0][2])) {
|
||||||
|
socat_opts.statistics = true;
|
||||||
} else {
|
} else {
|
||||||
Error1("unknown option \"%s\"; use option \"-h\" for help", arg1[0]);
|
Error1("unknown option \"%s\"; use option \"-h\" for help", arg1[0]);
|
||||||
}
|
}
|
||||||
|
@ -374,7 +380,9 @@ int main(int argc, const char *argv[]) {
|
||||||
#endif /* WITH_MSGLEVEL <= E_DEBUG */
|
#endif /* WITH_MSGLEVEL <= E_DEBUG */
|
||||||
|
|
||||||
{
|
{
|
||||||
|
#if HAVE_SIGACTION
|
||||||
struct sigaction act;
|
struct sigaction act;
|
||||||
|
#endif
|
||||||
int i, m;
|
int i, m;
|
||||||
|
|
||||||
sigfillset(&act.sa_mask); /* while in sighandler block all signals */
|
sigfillset(&act.sa_mask); /* while in sighandler block all signals */
|
||||||
|
@ -383,9 +391,20 @@ int main(int argc, const char *argv[]) {
|
||||||
/* not sure which signals should be caught and print a message */
|
/* not sure which signals should be caught and print a message */
|
||||||
for (i = 0, m = 1; i < 8*sizeof(unsigned long); ++i, m <<= 1) {
|
for (i = 0, m = 1; i < 8*sizeof(unsigned long); ++i, m <<= 1) {
|
||||||
if (socat_opts.log_sigs & m) {
|
if (socat_opts.log_sigs & m) {
|
||||||
|
#if HAVE_SIGACTION
|
||||||
Sigaction(i, &act, NULL);
|
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);
|
Signal(SIGPIPE, SIG_IGN);
|
||||||
|
|
||||||
|
@ -400,6 +419,9 @@ int main(int argc, const char *argv[]) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Atexit(socat_unlock);
|
Atexit(socat_unlock);
|
||||||
|
if (socat_opts.statistics) {
|
||||||
|
Atexit(socat_print_stats);
|
||||||
|
}
|
||||||
|
|
||||||
result = socat(arg1[0], arg1[1]);
|
result = socat(arg1[0], arg1[1]);
|
||||||
if (result == EXIT_SUCCESS && engine_result != EXIT_SUCCESS) {
|
if (result == EXIT_SUCCESS && engine_result != EXIT_SUCCESS) {
|
||||||
|
@ -428,6 +450,7 @@ void socat_usage(FILE *fd) {
|
||||||
fputs(" -D analyze file descriptors before loop\n", fd);
|
fputs(" -D analyze file descriptors before loop\n", fd);
|
||||||
#endif
|
#endif
|
||||||
fputs(" --experimental enable experimental features\n", fd);
|
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(" -ly[facility] log to syslog, using facility (default is daemon)\n", fd);
|
||||||
fputs(" -lf<logfile> log to file\n", fd);
|
fputs(" -lf<logfile> log to file\n", fd);
|
||||||
fputs(" -ls log to stderr (default if no other log)\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",
|
fprintf(fd, " running on %s version %s, release %s, machine %s\n",
|
||||||
ubuf.sysname, ubuf.version, ubuf.release, ubuf.machine);
|
ubuf.sysname, ubuf.version, ubuf.release, ubuf.machine);
|
||||||
fputs("features:\n", fd);
|
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
|
#ifdef WITH_STDIO
|
||||||
fprintf(fd, " #define WITH_STDIO %d\n", WITH_STDIO);
|
fprintf(fd, " #define WITH_STDIO %d\n", WITH_STDIO);
|
||||||
#else
|
#else
|
||||||
|
@ -1339,6 +1372,10 @@ int xiotransfer(xiofile_t *inpipe, xiofile_t *outpipe,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bytes > 0) {
|
if (bytes > 0) {
|
||||||
|
#if WITH_STATS
|
||||||
|
++XIO_RDSTREAM(inpipe)->blocks_read;
|
||||||
|
XIO_RDSTREAM(inpipe)->bytes_read += bytes;
|
||||||
|
#endif
|
||||||
/* handle escape char */
|
/* handle escape char */
|
||||||
if (XIO_RDSTREAM(inpipe)->escape != -1) {
|
if (XIO_RDSTREAM(inpipe)->escape != -1) {
|
||||||
/* check input data for escape char */
|
/* check input data for escape char */
|
||||||
|
@ -1490,6 +1527,10 @@ int xiotransfer(xiofile_t *inpipe, xiofile_t *outpipe,
|
||||||
} else {
|
} else {
|
||||||
Info3("transferred "F_Zu" bytes from %d to %d",
|
Info3("transferred "F_Zu" bytes from %d to %d",
|
||||||
writt, XIO_GETRDFD(inpipe), XIO_GETWRFD(outpipe));
|
writt, XIO_GETRDFD(inpipe), XIO_GETWRFD(outpipe));
|
||||||
|
#if WITH_STATS
|
||||||
|
++XIO_WRSTREAM(outpipe)->blocks_written;
|
||||||
|
XIO_WRSTREAM(outpipe)->bytes_written += writt;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return writt;
|
return writt;
|
||||||
|
@ -1671,3 +1712,66 @@ static int socat_newchild(void) {
|
||||||
havelock = false;
|
havelock = false;
|
||||||
return 0;
|
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 */
|
||||||
|
|
143
test.sh
143
test.sh
|
@ -278,7 +278,7 @@ tolower () {
|
||||||
psleep () {
|
psleep () {
|
||||||
local T="$1"
|
local T="$1"
|
||||||
[ "$T" = 0 ] && T=0.000002
|
[ "$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
|
# time in microseconds to wait in some situations
|
||||||
if ! type usleep >/dev/null 2>&1 ||
|
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="${n%??????}"; uS="${n:${#n}-6}" ;;
|
||||||
*) S=0; uS="00000$n"; uS="${uS:${#uS}-6}" ;;
|
*) S=0; uS="00000$n"; uS="${uS:${#uS}-6}" ;;
|
||||||
esac
|
esac
|
||||||
$SOCAT -T "$S.$uS" pipe pipe
|
$SOCAT -T "$S.$uS" pipe pipe 2>/dev/null
|
||||||
}
|
}
|
||||||
fi
|
fi
|
||||||
#USLEEP=usleep
|
#USLEEP=usleep
|
||||||
|
@ -974,6 +974,12 @@ runssctp6 () {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# check if UNIX domain sockets work
|
||||||
|
runsunix () {
|
||||||
|
# for now...
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
# wait until an IP4 protocol is ready
|
# wait until an IP4 protocol is ready
|
||||||
waitip4proto () {
|
waitip4proto () {
|
||||||
local proto="$1"
|
local proto="$1"
|
||||||
|
@ -13399,7 +13405,7 @@ N=$((N+1))
|
||||||
# Test the OpenSSL SNI feature
|
# Test the OpenSSL SNI feature
|
||||||
NAME=OPENSSL_SNI
|
NAME=OPENSSL_SNI
|
||||||
case "$TESTS" in
|
case "$TESTS" in
|
||||||
*%$N%*|*%functions%*|*%socket%*|*%openssl%*|*%$NAME%*)
|
*%$N%*|*%functions%*|*%socket%*|*%openssl%*|*%foreign%*|*%$NAME%*)
|
||||||
TEST="$NAME: Test the OpenSSL SNI feature"
|
TEST="$NAME: Test the OpenSSL SNI feature"
|
||||||
# Connect to a server that is known to use SNI. Use an SNI name, not the
|
# 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
|
# certifications default name. When the TLS connection is established
|
||||||
|
@ -13446,7 +13452,7 @@ N=$((N+1))
|
||||||
# Test the openssl-no-sni option
|
# Test the openssl-no-sni option
|
||||||
NAME=OPENSSL_NO_SNI
|
NAME=OPENSSL_NO_SNI
|
||||||
case "$TESTS" in
|
case "$TESTS" in
|
||||||
*%$N%*|*%functions%*|*%socket%*|*%openssl%*|*%$NAME%*)
|
*%$N%*|*%functions%*|*%socket%*|*%openssl%*|*%foreign%*|*%$NAME%*)
|
||||||
TEST="$NAME: Test the openssl-no-sni option"
|
TEST="$NAME: Test the openssl-no-sni option"
|
||||||
# Connect to a server that is known to use SNI. Use an SNI name, not the
|
# 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.
|
# 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
|
printf "test $F_n $TEST... " $N
|
||||||
$CMD >/dev/null 2>"${te}"
|
$CMD >/dev/null 2>"${te}"
|
||||||
rc1=$?
|
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
|
#echo "LOWPORT=\"$LOWPORT\"" >&2
|
||||||
#type socat >&2
|
#type socat >&2
|
||||||
if [[ $LOWPORT =~ [0-9][0-9]* ]] && [ "$LOWPORT" -ge 640 -a "$LOWPORT" -le 1023 ]; then
|
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
|
while read KEYW FEAT RUNS ADDR IPPORT; do
|
||||||
if [ -z "$KEYW" ] || [[ "$KEYW" == \#* ]]; then continue; fi
|
if [ -z "$KEYW" ] || [[ "$KEYW" == \#* ]]; then continue; fi
|
||||||
RUNS=$(tolower $KEYW)
|
|
||||||
PROTO=$KEYW
|
PROTO=$KEYW
|
||||||
proto="$(echo "$PROTO" |tr A-Z a-z)"
|
proto="$(echo "$PROTO" |tr A-Z a-z)"
|
||||||
feat="$(tolower "$FEAT")"
|
feat="$(tolower "$FEAT")"
|
||||||
|
@ -15054,8 +15059,8 @@ fi # NUMCOND
|
||||||
esac
|
esac
|
||||||
N=$((N+1))
|
N=$((N+1))
|
||||||
done <<<"
|
done <<<"
|
||||||
UDP4 UDP udp4 127.0.0.1 PORT
|
UDP4 UDP ip4 127.0.0.1 PORT
|
||||||
UDP6 UDP udp4 [::1] PORT
|
UDP6 UDP ip6 [::1] PORT
|
||||||
UNIX unix unix $td/test\$N.server -
|
UNIX unix unix $td/test\$N.server -
|
||||||
"
|
"
|
||||||
|
|
||||||
|
@ -15395,6 +15400,127 @@ esac
|
||||||
N=$((N+1))
|
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
|
# end of common tests
|
||||||
|
|
||||||
##################################################################################
|
##################################################################################
|
||||||
|
@ -15528,6 +15654,7 @@ exit
|
||||||
NAME=SHORT_UNIQUE_TESTNAME
|
NAME=SHORT_UNIQUE_TESTNAME
|
||||||
case "$TESTS" in
|
case "$TESTS" in
|
||||||
*%$N%*|*%functions%*|*%bugs%*|*%socket%*|*%$NAME%*)
|
*%$N%*|*%functions%*|*%bugs%*|*%socket%*|*%$NAME%*)
|
||||||
|
#*%foreign%*|*%root%*|*%listen%*|*%fork%*|*%ip4%*|*%tcp4%*|*%bug%*|...
|
||||||
TEST="$NAME: give a one line description of test"
|
TEST="$NAME: give a one line description of test"
|
||||||
# Describe how the test is performed, and what's the success criteria
|
# Describe how the test is performed, and what's the success criteria
|
||||||
if ! eval $NUMCOND; then :;
|
if ! eval $NUMCOND; then :;
|
||||||
|
|
|
@ -197,7 +197,10 @@ int _xioopen_open(const char *path, int rw, struct opt *opts) {
|
||||||
|
|
||||||
retropt_modet(opts, OPT_PERM, &mode);
|
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",
|
Error4("open(\"%s\", 0%lo, 0%03o): %s",
|
||||||
path, flags, mode, strerror(errno));
|
path, flags, mode, strerror(errno));
|
||||||
return STAT_RETRYLATER;
|
return STAT_RETRYLATER;
|
||||||
|
|
|
@ -153,7 +153,7 @@ static int xioopen_fifo(int argc, const char *argv[], struct opt *opts, int xiof
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* exists */
|
/* 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",
|
Notice3("opening %s \"%s\" for %s",
|
||||||
filetypenames[(pipstat.st_mode&S_IFMT)>>12],
|
filetypenames[(pipstat.st_mode&S_IFMT)>>12],
|
||||||
pipename, ddirection[rw]);
|
pipename, ddirection[rw]);
|
||||||
|
|
9
xio.h
9
xio.h
|
@ -85,8 +85,9 @@ enum xiotag {
|
||||||
XIO_TAG_RDONLY, /* this is a single read-only stream */
|
XIO_TAG_RDONLY, /* this is a single read-only stream */
|
||||||
XIO_TAG_WRONLY, /* this is a single write-only stream */
|
XIO_TAG_WRONLY, /* this is a single write-only stream */
|
||||||
XIO_TAG_RDWR, /* this is a single read-write 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 */
|
streams */
|
||||||
|
XIO_TAG_CLOSED=8, /* close, additional bit */
|
||||||
} ;
|
} ;
|
||||||
|
|
||||||
/* Keep condition consistent with xioopts.h:GROUP_*! */
|
/* Keep condition consistent with xioopts.h:GROUP_*! */
|
||||||
|
@ -278,6 +279,12 @@ typedef struct single {
|
||||||
} tun;
|
} tun;
|
||||||
#endif /* WITH_TUN */
|
#endif /* WITH_TUN */
|
||||||
} para;
|
} 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;
|
} xiosingle_t;
|
||||||
|
|
||||||
/* rw: 0..read, 1..write, 2..r/w */
|
/* rw: 0..read, 1..write, 2..r/w */
|
||||||
|
|
|
@ -93,7 +93,7 @@ int xioclose1(struct single *pipe) {
|
||||||
free(pipe->unlink_close);
|
free(pipe->unlink_close);
|
||||||
}
|
}
|
||||||
|
|
||||||
pipe->tag = XIO_TAG_INVALID;
|
pipe->tag |= XIO_TAG_CLOSED;
|
||||||
return 0; /*! */
|
return 0; /*! */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,7 +111,7 @@ int xioclose(xiofile_t *file) {
|
||||||
if (file->tag == XIO_TAG_DUAL) {
|
if (file->tag == XIO_TAG_DUAL) {
|
||||||
result = xioclose1(file->dual.stream[0]);
|
result = xioclose1(file->dual.stream[0]);
|
||||||
result |= xioclose1(file->dual.stream[1]);
|
result |= xioclose1(file->dual.stream[1]);
|
||||||
file->tag = XIO_TAG_INVALID;
|
file->tag |= XIO_TAG_CLOSED;
|
||||||
} else {
|
} else {
|
||||||
result = xioclose1(&file->stream);
|
result = xioclose1(&file->stream);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,8 @@ void xioexit(void) {
|
||||||
diag_in_handler = 0;
|
diag_in_handler = 0;
|
||||||
Debug("starting xioexit()");
|
Debug("starting xioexit()");
|
||||||
for (i = 0; i < XIO_MAXSOCK; ++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)) {
|
||||||
xioclose(sock[i]);
|
xioclose(sock[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -121,22 +121,27 @@ void xiodroplocks(void) {
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < XIO_MAXSOCK; ++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]);
|
xiofiledroplock(sock[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* consider an invokation like this:
|
/* Consider an invocation like this:
|
||||||
socat -u exec:'some program that accepts data' tcp-l:...,fork
|
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
|
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
|
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
|
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
|
should not be shut down (affects the connection), but closed (affects only
|
||||||
sub processes copy of file descriptor) */
|
sub processes copy of file descriptor) */
|
||||||
static int xio_nokill(xiofile_t *sock) {
|
static int xio_nokill(xiofile_t *sock) {
|
||||||
int result = 0;
|
int result = 0;
|
||||||
|
|
||||||
|
if (sock->tag & XIO_TAG_CLOSED) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
switch (sock->tag) {
|
switch (sock->tag) {
|
||||||
case XIO_TAG_INVALID:
|
case XIO_TAG_INVALID:
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -23,7 +23,7 @@ ssize_t xioread(xiofile_t *file, void *buff, size_t bufsiz) {
|
||||||
struct single *pipe;
|
struct single *pipe;
|
||||||
int _errno;
|
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);
|
Error1("xioread(): invalid xiofile descriptor %p", file);
|
||||||
errno = EINVAL;
|
errno = EINVAL;
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -31,7 +31,7 @@ ssize_t xioread(xiofile_t *file, void *buff, size_t bufsiz) {
|
||||||
|
|
||||||
if (file->tag == XIO_TAG_DUAL) {
|
if (file->tag == XIO_TAG_DUAL) {
|
||||||
pipe = file->dual.stream[0];
|
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);
|
Error1("xioread(): invalid xiofile sub descriptor %p[0]", file);
|
||||||
errno = EINVAL;
|
errno = EINVAL;
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -433,7 +433,7 @@ ssize_t xioread(xiofile_t *file, void *buff, size_t bufsiz) {
|
||||||
ssize_t xiopending(xiofile_t *file) {
|
ssize_t xiopending(xiofile_t *file) {
|
||||||
struct single *pipe;
|
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);
|
Error1("xiopending(): invalid xiofile descriptor %p", file);
|
||||||
errno = EINVAL;
|
errno = EINVAL;
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -441,7 +441,7 @@ ssize_t xiopending(xiofile_t *file) {
|
||||||
|
|
||||||
if (file->tag == XIO_TAG_DUAL) {
|
if (file->tag == XIO_TAG_DUAL) {
|
||||||
pipe = file->dual.stream[0];
|
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);
|
Error1("xiopending(): invalid xiofile sub descriptor %p[0]", file);
|
||||||
errno = EINVAL;
|
errno = EINVAL;
|
||||||
return -1;
|
return -1;
|
||||||
|
|
|
@ -25,7 +25,7 @@ static void signal_kill_pid(int dummy) {
|
||||||
int xioshutdown(xiofile_t *sock, int how) {
|
int xioshutdown(xiofile_t *sock, int how) {
|
||||||
int result = 0;
|
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");
|
Error("xioshutdown(): invalid file descriptor");
|
||||||
errno = EINVAL;
|
errno = EINVAL;
|
||||||
return -1;
|
return -1;
|
||||||
|
|
|
@ -22,7 +22,7 @@ ssize_t xiowrite(xiofile_t *file, const void *buff, size_t bytes) {
|
||||||
struct single *pipe;
|
struct single *pipe;
|
||||||
int _errno;
|
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);
|
Error1("xiowrite(): invalid xiofile descriptor %p", file);
|
||||||
errno = EINVAL;
|
errno = EINVAL;
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -30,7 +30,7 @@ ssize_t xiowrite(xiofile_t *file, const void *buff, size_t bytes) {
|
||||||
|
|
||||||
if (file->tag == XIO_TAG_DUAL) {
|
if (file->tag == XIO_TAG_DUAL) {
|
||||||
pipe = file->dual.stream[1];
|
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);
|
Error1("xiowrite(): invalid xiofile sub descriptor %p[1]", file);
|
||||||
errno = EINVAL;
|
errno = EINVAL;
|
||||||
return -1;
|
return -1;
|
||||||
|
|
Loading…
Reference in a new issue