Added socat-chain.sh for layering protocols
diff --git a/CHANGES b/CHANGES
index 4aa4980..b1e2861 100644
--- a/CHANGES
+++ b/CHANGES
@@ -197,6 +197,10 @@
reads info from /proc/self/stat and searches for a device with matching
major and minor numbers.
+ Added socat-chain.sh that makes it possible to stack protocols, e.g. to
+ drive socks through TLS, or to use TLS over a serial line.
+ Tests: SOCAT_CHAIN_SOCKS4 SOCAT_CHAIN_SSL_PTY
+
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 3ecd5d2..cf9f263 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -76,7 +76,8 @@
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 = socat-chain.sh \
+ daemon.sh mail.sh ftp.sh readline.sh \
socat_buildscript_for_android.sh
TESTFILES = test.sh socks4echo.sh proxyecho.sh readline-test.sh \
proxy.sh socks4a-echo.sh
@@ -131,6 +132,7 @@
mkdir -p $(DESTDIR)$(BINDEST)
$(INSTALL) -m 755 socat $(DESTDIR)$(BINDEST)/socat1
ln -s socat1 $(DESTDIR)$(BINDEST)/socat
+ $(INSTALL) -m 755 socat-chain.sh $(DESTDIR)$(BINDEST)
$(INSTALL) -m 755 procan $(DESTDIR)$(BINDEST)
$(INSTALL) -m 755 filan $(DESTDIR)$(BINDEST)
mkdir -p $(DESTDIR)$(MANDEST)/man1
@@ -140,6 +142,7 @@
uninstall:
rm -f $(DESTDIR)$(BINDEST)/socat
rm -f $(DESTDIR)$(BINDEST)/socat1
+ rm -f $(DESTDIR)$(BINDEST)/socat-chain.sh
rm -f $(DESTDIR)$(BINDEST)/procan
rm -f $(DESTDIR)$(BINDEST)/filan
rm -f $(DESTDIR)$(MANDEST)/man1/socat.1
diff --git a/socat-chain.sh b/socat-chain.sh
new file mode 100755
index 0000000..865446d
--- /dev/null
+++ b/socat-chain.sh
@@ -0,0 +1,264 @@
+#! /usr/bin/env bash
+# Copyright Gerhard Rieger and contributors (see file CHANGES)
+# Published under the GNU General Public License V.2, see file COPYING
+
+# Shell script to build a chain of Socat instances connected via TCP sockets.
+# This allows to drive, e.g., PROXY-CONNECT over SSL, or SSL over serial.
+# Currently only a chain made from 3 addresses, resulting in two instances, is
+# implemented.
+# The 2nd address must be one of OPENSSL (SSL), PROXY-CONNECT (PROXY),
+# SOCKS4, SOCKS4A, SOCKS5
+
+# This is beta!
+
+# Examples:
+
+# Drive HTTP CONNECT (PROXY) over SSL
+# (establish an SSL tunnel to a proxy server, request being forwarded to a
+# telnet server):
+# socat-chain.sh \
+# STDIO \
+# PROXY::<telnet-server>:23 \
+# OPENSSL:<proxy-server>:8443
+
+# Accept connections that arrive on port 7777, encrypt the data, and send it
+# via socks server to final target:
+# socat-chain.sh \
+# TCP-L:7777,reuseaddr,fork \
+# OPENSSL,verify=0 \
+# SOCKS4:<socks-server>:<ssl-server>:8443
+
+# Receive SSL coming from a serial lie
+# socat-chain.sh \
+# /dev/ttyS0,cfmakeraw \
+# SSL-L,cafile=server.pem,verify=0 \
+# TCP4:localhost:80
+
+# Formally, this is what happens:
+# socat-chain.sh addr1 addr2 addr3
+# results in something like:
+# socat TCP-L:RANDOM addr3 &
+# socat addr1 addr2:localhost:RANDOM
+# or on passive/listening addr2:
+# socat addr2:RANDOM addr3 &
+# socat addr1 TCP:localhost:RANDOM
+
+ECHO="echo -e"
+
+usage () {
+ $ECHO "Usage: $0 <options> <address1> <address2> <address3>"
+ $ECHO " <address1> is typically a passive (listening) address like"
+ $ECHO " TCP-L:1234"
+ $ECHO " <address2> must be one of OPENSSL, PROXY, SOCK4, SOCKS4A, or SOCKS5,"
+ $ECHO " or SSL-L (passive/listening)"
+ $ECHO " Given server hostname and port are ignored and replaced by internal"
+ $ECHO " communication point"
+ $ECHO " <address3> is typically a client address with protocol like OPENSSL"
+ $ECHO " <options>:"
+ $ECHO " -d* -S <sigmask> -t <timeout> -T <timeout> are passed to socat"
+ $ECHO " -V prints the socat commands before starting them"
+ $ECHO "Example to drive SOCKS over TLS:"
+ $ECHO " $0 \\"
+ $ECHO " TCP4-L:1234,reuseaddr,fork \\"
+ $ECHO " SOCKS::<server>:<port> \\"
+ $ECHO " OPENSSL:10.2.3.4:12345,cafile=..."
+ $ECHO " Clients that connect to port 1234 will be forwarded to <server>:<port> using socks"
+ $ECHO " over TLS"
+}
+
+
+LOCALHOST=127.0.0.1
+
+VERBOSE= QUIET= OPTS=
+while [ "$1" ]; do
+ case "X$1" in
+ X-h) usage; exit ;;
+ X-V) VERBOSE=1 ;;
+ X-q) QUIET=1; OPTS="-d0" ;;
+ X-d*|X-l?*) OPTS="$OPTS $1" ;;
+ X-b|X-S|X-t|X-T|X-l) OPT=$1; shift; OPTS="$OPTS $OPT $1" ;;
+ X-) break ;;
+ X-*) echo "$0: Unknown option \"$1\"" >&2
+ usage >&2
+ exit 1 ;;
+ *) break ;;
+ esac
+ shift
+done
+
+ARG0="$1"
+ARG1="$2"
+ARG2="$3"
+
+if [ -z "$ARG0" -o -z "$ARG1" -o -z "$ARG2" ]; then
+ echo "$0: Three addresses required" >&2
+ usage >&2
+ exit 1
+fi
+
+
+mkprogname () {
+ ARG="$1"
+ if [[ "$ARG" =~ .*[:].* ]]; then
+ NAME="${ARG%%:*}"
+ elif [[ "$ARG" =~ .*[,].* ]]; then
+ NAME="${ARG%%,*}"
+ elif [ "X$ARG" = X- ]; then
+ NAME=stdio
+ else
+ NAME="$ARG"
+ fi
+ NAME="${NAME,,*}"
+ echo $NAME
+}
+
+
+# You may place a fork option in the first address
+# in which case the following internal listeners do fork too
+FORK=
+case "$ARG0" in
+ *,fork,*|*,fork) FORK=fork ;;
+esac
+
+# Split middle address for insertion of additional parts
+if [[ "$ARG1" =~ .*,.* ]]; then
+ ARG1A="${ARG1%%,*}"
+ ARG1B="${ARG1#*,}"
+else
+ ARG1A="$ARG1"
+ ARG1B=
+fi
+
+case "$0" in
+ */*) SOCAT=${0%/*}/socat ;;
+ *) SOCAT=socat ;;
+esac
+
+PORT=$($SOCAT -d -d TCP4-L:0,accept-timeout=0.000001 /dev/null 2>&1 |grep listening |sed 's/.*:\([1-9][0-9]*\)$/\1/')
+if [ -z "$PORT" ]; then
+ echo "$0: Failed to determine free TCP port" >&2
+ exit 1
+fi
+
+PASSIVE= # is the second address passive/listening/server?
+case "${ARG1A^^*}" in
+ OPENSSL|OPENSSL:*|SSL|SSL:.*)
+ OPTS1A=
+ #if [[ $ARG1A =~ ^\([^:]*\):\([^:]*\):\([^,]*\)\(.*\) ]]; then # bash 3
+ if [[ $ARG1A =~ ^([^:]*):([^:]*):([^,]*)(.*) ]]; then
+ OPTS1A="${BASH_REMATCH[4]}"
+ #elif [[ $ARG1A =~ ^\([^,]*\)\(.*\) ]]; then # bash 3
+ elif [[ $ARG1A =~ ^([^,]*)(.*) ]]; then
+ OPTS1A="${BASH_REMATCH[2]}"
+ else
+ echo "$0: \"$ARG1A\": invalid arguments" >&2
+ exit 1
+ fi
+ PROG1="${BASH_REMATCH[1]}"
+ NAME1=$(mkprogname "${BASH_REMATCH[1]}")
+ NAME2=$(mkprogname "$ARG2")
+ ARG1A=$PROG1:$LOCALHOST:$PORT$OPTS1A ;;
+ PROXY-CONNECT:*|PROXY:*)
+ #if ! [[ $ARG1A =~ ^\([^:]*\):\([^:]*\):\([^:]*\):\([^,]*\)\(.*\) ]]; then # bash 3
+ if ! [[ $ARG1A =~ ^([^:]*):([^:]*):([^:]*):([^,]*)(.*) ]]; then
+ echo "$0: \"$ARG1A\": invalid arguments" >&2
+ exit 1
+ fi
+ #echo "0:\"${BASH_REMATCH[0]}\" 1:\"${BASH_REMATCH[1]}\" 2:\"${BASH_REMATCH[2]}\" 3:\"${BASH_REMATCH[3]}\" 4:\"${BASH_REMATCH[4]}\""
+ PROG1="${BASH_REMATCH[1]}"
+ NAME1=$(mkprogname "${PROG1,,*}")
+ NAME2=$(mkprogname "$ARG2")
+ OPTS1A="${BASH_REMATCH[5]}"
+ ARG1A="$PROG1:$LOCALHOST:${BASH_REMATCH[3]}:${BASH_REMATCH[4]},proxyport=$PORT,$OPTS1A" ;;
+ SOCKS:*|SOCKS4:*|SOCKS4A*)
+ #if ! [[ $ARG1A =~ ^\([^:]*\):\([^:]*\):\([^:]*\):\([^:,]*\),* ]]; then # bash 3
+ if ! [[ $ARG1A =~ ^([^:]*):([^:]*):([^:]*):([^:,]*),* ]]; then
+ echo "$0: \"$ARG1A\": invalid arguments" >&2
+ exit 1
+ fi
+ PROG1="${BASH_REMATCH[1]}"
+ NAME1=$(mkprogname "${PROG1,,*}")
+ NAME2=$(mkprogname "$ARG2")
+ OPTS1A="${BASH_REMATCH[5]}"
+ ARG1A="$PROG1:$LOCALHOST:${BASH_REMATCH[3]}:${BASH_REMATCH[4]},socksport=$PORT,$OPTS1A" ;;
+ SOCKS5:*|SOCKS5-CONNECT*)
+ #if ! [[ $ARG1A =~ ^\([^:]*\):\([^:]*\):\([^:]*\):\([^:,]*\):\([^:,]*\),* ]]; then # bash 3
+ if ! [[ $ARG1A =~ ^([^:]*):([^:]*):([^:]*):([^:,]*):([^:,]*),* ]]; then
+ echo "$0: \"$ARG1A\": invalid arguments" >&2
+ exit 1
+ fi
+ PROG1="${BASH_REMATCH[1]}"
+ NAME1=$(mkprogname "${PROG1,,*}")
+ NAME2=$(mkprogname "$ARG2")
+ OPTS1A="${BASH_REMATCH[6]}"
+ ARG1A="$PROG1:$LOCALHOST:$PORT:${BASH_REMATCH[4]}:${BASH_REMATCH[5]},$OPTS1A" ;;
+ # Passive (server) addresses
+ OPENSSL-LISTEN|OPENSSL-LISTEN:*|SSL-L|SSL-L:.*)
+ PASSIVE=1
+ OPTS1A=
+ #if [[ $ARG1A =~ ^\([^:]*\):\([^,]*\)\(.*\) ]]; then # bash 3
+ if [[ $ARG1A =~ ^([^:]*):([^,]*)(.*) ]]; then
+ OPTS1A="${BASH_REMATCH[3]}"
+ #elif [[ $ARG1A =~ ^\([^,]*\)\(.*\) ]]; then # bash 3
+ elif [[ $ARG1A =~ ^([^,]*)(.*) ]]; then
+ OPTS1A="${BASH_REMATCH[2]}"
+ else
+ echo "$0: \"$ARG1A\": invalid arguments" >&2
+ exit 1
+ fi
+ PROG1="${BASH_REMATCH[1]}"
+ NAME1=$(mkprogname "$ARG0")
+ NAME2=$(mkprogname "${BASH_REMATCH[1]}")
+ ARG1A=$PROG1:$PORT$OPTS1A ;;
+ *) echo "$0: Unsupported address \"$ARG1A\"" >&2
+ usage >&2
+ exit 1 ;;
+esac
+
+ADDR1A="$ARG0"
+if [ -z "$PASSIVE" ]; then
+ ADDR1B="$ARG1A,bind=$LOCALHOST,$ARG1B"
+ ADDR2A="TCP4-L:$PORT,reuseaddr,$FORK,bind=$LOCALHOST,range=$LOCALHOST/32"
+else
+ ADDR1B="TCP4:$LOCALHOST:$PORT,bind=$LOCALHOST"
+ ADDR2A="$ARG1A,reuseaddr,$FORK,bind=$LOCALHOST,range=$LOCALHOST/32,$ARG1B"
+fi
+ADDR2B="$ARG2"
+
+
+pid1= pid2=
+trap '[ "$pid1" ] && kill $pid1 2>/dev/null; [ "$pid2" ] && kill $pid2 2>/dev/null' EXIT
+
+set -bm
+trap 'rc=$?; if ! kill -n 0 $pid2 2>/dev/null; then [ -z "$QUIET" -a $rc -ne 0 ] && echo "$0: socat-$NAME2 exited with rc=$rc" >&2; exit $rc; fi' SIGCHLD
+
+# Start instance 2 first, because instance 1 ("left") connects to 2
+if [ "$VERBOSE" ]; then
+ $ECHO "$SOCAT $OPTS -lp socat-$NAME2 \\
+ \"$ADDR2A\" \\
+ \"$ADDR2B\" &"
+fi
+$SOCAT $OPTS -lp socat-$NAME2 \
+ "$ADDR2A" \
+ "$ADDR2B" &
+pid2=$!
+sleep 0.1
+
+#trap 'if ! kill -n 0 $pid1 2>/dev/null; then [ -z "$QUIET" ] && echo "$0: socat-$NAME1 exited with rc=$?" >&2; kill $pid2 2>/dev/null; exit 1; elif ! kill -n 0 $pid2 2>/dev/null; then [ -z "$QUIET" ] && echo "$0: socat-$NAME2 exited with rc=$?" >&2; kill $pid1 2>/dev/null; exit 1; fi' SIGCHLD
+
+if [ "$VERBOSE" ]; then
+ $ECHO "$SOCAT $OPTS -lp socat-$NAME1 \\
+ \"$ADDR1A\" \\
+ \"$ADDR1B\""
+fi
+$SOCAT $OPTS -lp socat-$NAME1 \
+ "$ADDR1A" \
+ "$ADDR1B"
+#pid1=$!
+rc1=$?
+
+kill $pid2 2>/dev/null
+wait 2>/dev/null
+#wait -f
+
+exit $rc1
diff --git a/socks4echo.sh b/socks4echo.sh
index 103719a..48ea536 100755
--- a/socks4echo.sh
+++ b/socks4echo.sh
@@ -91,7 +91,7 @@
fi
if [ "$u" != "nobody" ]; then
$ECHO "$SOCKSREPLY_FAILED"
- echo "wrong socks user requested" >&2
+ echo "wrong socks user requested (expected \"nobody\")" >&2
exit
fi
diff --git a/test.sh b/test.sh
index c9bc3aa..f91973e 100755
--- a/test.sh
+++ b/test.sh
@@ -1044,7 +1044,7 @@
local uname="$(echo $UNAME |tr 'A-Z' 'a-z')"
for i in $unames; do
if [ "$uname" = "$(echo "$i" |tr 'A-Z,' 'a-z ')" ]; then
- # good, mark as passed
+ # good, mark as passed
i=
break;
fi
@@ -18838,6 +18838,154 @@
N=$((N+1))
+# Test the socat-chain.sh script with SOCKS4 over UNIX-socket
+NAME=SOCAT_CHAIN_SOCKS4
+case "$TESTS" in
+*%$N%*|*%functions%*|*%scripts%*|*%socat-chain%*|*%listen%*|*%fork%*|*%ip4%*|*%tcp4%*|*%unix%*|*%socks4%*|*%socket%*|*%$NAME%*)
+TEST="$NAME: test socat-chain.sh with SOCKS4 over UNIX-socket"
+# Run a socks4 server on UNIX-listen
+# Connect with socat-chein.sh; check if data transfer is correct
+if ! eval $NUMCOND; then :
+# Remove unneeded checks, adapt lists of the remaining ones
+elif ! cond=$(checkconds \
+ "" \
+ "" \
+ "" \
+ "IP4 TCP LISTEN STDIO UNIX" \
+ "TCP4-LISTEN PIPE STDIN STDOUT TCP4 UNIX UNIX-LISTEN" \
+ "so-reuseaddr" \
+ "tcp4 unix" ); then
+ $PRINTF "test $F_n $TEST... ${YELLOW}$cond${NORMAL}\n" $N
+ numCANT=$((numCANT+1))
+ listCANT="$listCANT $N"
+ namesCANT="$namesCANT $NAME"
+else
+ 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 UNIX-LISTEN:$ts,reuseaddr EXEC:./socks4echo.sh"
+ CMD1="$TRACE ./socat-chain.sh $opts - SOCKS4::32.98.76.54:32109,socksuser=nobody UNIX:$ts"
+ printf "test $F_n $TEST... " $N
+ $CMD0 >/dev/null 2>"${te}0" &
+ pid0=$!
+ waitunixport $ts 1
+ #relsleep 1 # if no matching wait*port function
+ echo "$da" |$CMD1 >"${tf}1" 2>"${te}1"
+ rc1=$?
+ kill $pid0 2>/dev/null; wait
+ if [ "$rc1" -ne 0 ]; then
+ $PRINTF "$FAILED (rc1=$rc1)\n"
+ echo "$CMD0 &"
+ cat "${te}0" >&2
+ echo "$CMD1"
+ cat "${te}1" >&2
+ numFAIL=$((numFAIL+1))
+ listFAIL="$listFAIL $N"
+ namesFAIL="$namesFAIL $NAME"
+ elif ! echo "$da" |diff - "${tf}1" >$tdiff; then
+ $PRINTF "$FAILED (diff)\n"
+ echo "$CMD0 &"
+ cat "${te}0" >&2
+ echo "$CMD1"
+ cat "${te}1" >&2
+ echo "// diff:" >&2
+ cat "$tdiff" >&2
+ numFAIL=$((numFAIL+1))
+ listFAIL="$listFAIL $N"
+ namesFAIL="$namesFAIL $NAME"
+ else
+ $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
+ numOK=$((numOK+1))
+ fi
+fi # NUMCOND
+ ;;
+esac
+N=$((N+1))
+
+# Test the socat-chain.sh script by driving SSL over serial
+NAME=SOCAT_CHAIN_SSL_PTY
+case "$TESTS" in
+*%$N%*|*%functions%*|*%scripts%*|*%socat-chain%*|*%listen%*|*%fork%*|*%ip4%*|*%tcp4%*|*%unix%*|*%socket%*|*%socket%*|*%$NAME%*)
+TEST="$NAME: test socat-chain.sh with SSL over PTY"
+# Run a socat-chain.sh instance with SSL listening behind a PTY;
+# open the PTY with socat-chein.sh using SSL;
+# check if data transfer is correct
+if ! eval $NUMCOND; then :
+# Remove unneeded checks, adapt lists of the remaining ones
+elif ! cond=$(checkconds \
+ "" \
+ "" \
+ "" \
+ "IP4 TCP LISTEN OPENSSL STDIO PTY" \
+ "TCP4-LISTEN SOCKETPAIR STDIN STDOUT TCP4 SSL SSL-L" \
+ "so-reuseaddr" \
+ "tcp4" ); then
+ $PRINTF "test $F_n $TEST... ${YELLOW}$cond${NORMAL}\n" $N
+ numCANT=$((numCANT+1))
+ listCANT="$listCANT $N"
+ namesCANT="$namesCANT $NAME"
+elif [[ $BASH_VERSION =~ ^[1-3]\. ]]; then
+ $PRINTF "test $F_n $TEST... ${YELLOW}requires bash 4 or higher${NORMAL}\n" $N
+ numCANT=$((numCANT+1))
+ listCANT="$listCANT $N"
+ namesCANT="$namesCANT $NAME"
+else
+ gentestcert testsrv
+ tp="$td/test$N.pty"
+ tf="$td/test$N.stdout"
+ te="$td/test$N.stderr"
+ tdiff="$td/test$N.diff"
+ da="test$N $(date) $RANDOM"
+ CMD0="$TRACE ./socat-chain.sh $opts PTY,link=$tp SSL-L,cert=testsrv.pem,verify=0 SOCKETPAIR"
+ CMD1="$TRACE ./socat-chain.sh $opts - SSL,cafile=testsrv.crt,commonname=localhost $tp,cfmakeraw"
+ printf "test $F_n $TEST... " $N
+ $CMD0 >/dev/null 2>"${te}0" &
+ pid0=$!
+ waitfile $tp 1
+ echo "$da" |$CMD1 >"${tf}1" 2>"${te}1"
+ rc1=$?
+ kill $pid0 2>/dev/null
+ wait 2>/dev/null
+ if [ "$rc1" -ne 0 ]; then
+ $PRINTF "$FAILED (rc1=$rc1)\n"
+ echo "$CMD0 &"
+ cat "${te}0" >&2
+ echo "$CMD1"
+ cat "${te}1" >&2
+ numFAIL=$((numFAIL+1))
+ listFAIL="$listFAIL $N"
+ namesFAIL="$namesFAIL $NAME"
+ elif ! echo "$da" |diff - "${tf}1" >$tdiff; then
+ $PRINTF "$FAILED (diff)\n"
+ echo "$CMD0 &"
+ cat "${te}0" >&2
+ echo "$CMD1"
+ cat "${te}1" >&2
+ echo "// diff:" >&2
+ cat "$tdiff" >&2
+ numFAIL=$((numFAIL+1))
+ listFAIL="$listFAIL $N"
+ namesFAIL="$namesFAIL $NAME"
+ else
+ $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
+ numOK=$((numOK+1))
+ fi
+fi # NUMCOND
+ ;;
+esac
+N=$((N+1))
+
+
# end of common tests
##################################################################################
@@ -19015,7 +19163,7 @@
numFAIL=$((numFAIL+1))
listFAIL="$listFAIL $N"
namesFAIL="$namesFAIL $NAME"
- elif ! echo "$da" |diff "${tf}1" - >$tdiff; then
+ elif ! echo "$da" |diff - "${tf}1" >$tdiff; then
$PRINTF "$FAILED (diff)\n"
echo "$CMD0 &"
cat "${te}0" >&2
diff --git a/xio-pty.c b/xio-pty.c
index 2e6f60c..585fb1f 100644
--- a/xio-pty.c
+++ b/xio-pty.c
@@ -38,7 +38,8 @@
{
/* we expect the form: filename */
struct single *sfd = &xfd->stream;
- int ptyfd = -1, ttyfd = -1;
+ int ptyfd = -1; /* master */
+ int ttyfd = -1; /* slave */
#if defined(HAVE_DEV_PTMX) || defined(HAVE_DEV_PTC)
bool useptmx = false; /* use /dev/ptmx or equivalent */
#endif
@@ -101,6 +102,18 @@
if (applyopts_single(sfd, opts, PH_INIT) < 0) return -1;
applyopts2(sfd, -1, opts, PH_INIT, PH_EARLY);
+#if HAVE_OPENPTY
+ if (ptyfd < 0) {
+ int result;
+ if ((result = Openpty(&ptyfd, &ttyfd, ptyname, NULL, NULL)) < 0) {
+ Error4("openpty(%p, %p, %p, NULL, NULL): %s",
+ &ptyfd, &ttyfd, ptyname, strerror(errno));
+ return -1;
+ }
+ Notice1("PTY is %s", ptyname);
+ }
+#endif /* HAVE_OPENPTY */
+
applyopts(sfd, -1, opts, PH_PREBIGEN);
#if defined(HAVE_DEV_PTMX)
@@ -109,7 +122,7 @@
# define PTMX "/dev/ptc" /* AIX 4.3.3 */
#endif
#if HAVE_DEV_PTMX || HAVE_DEV_PTC
- if (useptmx) {
+ if (useptmx || ptyfd < 0) {
if ((ptyfd = Open(PTMX, O_RDWR|O_NOCTTY, 0620)) < 0) {
Warn1("open(\""PTMX"\", O_RDWR|O_NOCTTY, 0620): %s",
strerror(errno));
@@ -117,7 +130,7 @@
} else {
;/*0 Info1("open(\""PTMX"\", O_RDWR|O_NOCTTY, 0620) -> %d", ptyfd);*/
}
- if (ptyfd >= 0 && ttyfd < 0) {
+ if (ptyfd >= 0) {
/* we used PTMX before forking */
/*0 extern char *ptsname(int);*/
#if HAVE_GRANTPT /* AIX, not Linux */
@@ -146,17 +159,6 @@
}
}
#endif /* HAVE_DEV_PTMX || HAVE_DEV_PTC */
-#if HAVE_OPENPTY
- if (ptyfd < 0) {
- int result;
- if ((result = Openpty(&ptyfd, &ttyfd, ptyname, NULL, NULL)) < 0) {
- Error4("openpty(%p, %p, %p, NULL, NULL): %s",
- &ptyfd, &ttyfd, ptyname, strerror(errno));
- return -1;
- }
- Notice1("PTY is %s", ptyname);
- }
-#endif /* HAVE_OPENPTY */
if (!retropt_string(opts, OPT_SYMBOLIC_LINK, &linkname)) {
xio_unlink(linkname, E_ERROR);
@@ -178,7 +180,7 @@
applyopts_cloexec(ptyfd, opts);/*!*/
sfd->dtype = XIODATA_PTY;
- applyopts(sfd, ptyfd, opts, PH_FD);
+ applyopts(sfd, ttyfd, opts, PH_FD);
{
/* special handling of user-late etc.; with standard behaviour (up to
diff --git a/xiohelp.c b/xiohelp.c
index 4641dbd..9db54c4 100644
--- a/xiohelp.c
+++ b/xiohelp.c
@@ -225,8 +225,8 @@
int isnum,
const char *syntax)
{
- Error4("%s: wrong number of parameters (%d instead of %d): usage: %s",
- addr, isnum, expectnum, syntax);
+ Error5("%s: wrong number of parameters (%d instead of %d): usage: %s%s",
+ addr, isnum, expectnum, addr, syntax);
return -1;
}