New feature SHELL address

This commit is contained in:
Gerhard Rieger 2023-10-01 21:24:46 +02:00
parent 50b199dcd9
commit 03f028a985
15 changed files with 399 additions and 45 deletions

View file

@ -133,6 +133,8 @@ Features:
Tests: LINUX_POSIXMQ_READ_PRIO LINUX_POSIXMQ_RECV_FORK Tests: LINUX_POSIXMQ_READ_PRIO LINUX_POSIXMQ_RECV_FORK
LINUX_POSIXMQ_RECV_MAXCHILDREN LINUX_POSIXMQ_SEND_MAXCHILDREN LINUX_POSIXMQ_RECV_MAXCHILDREN LINUX_POSIXMQ_SEND_MAXCHILDREN
New address SHELL invokes a shell but without the overhead of SYSTEM
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/

View file

@ -50,7 +50,8 @@ XIOSRCS = xioinitialize.c xiohelp.c xioparam.c xiodiag.c xioopen.c xioopts.c \
xio-ip.c xio-ip4.c xio-ip6.c xio-ipapp.c xio-tcp.c \ xio-ip.c xio-ip4.c xio-ip6.c xio-ipapp.c xio-tcp.c \
xio-sctp.c xio-rawip.c xio-posixmq.c \ xio-sctp.c xio-rawip.c xio-posixmq.c \
xio-socks.c xio-socks5.c xio-proxy.c xio-udp.c \ xio-socks.c xio-socks5.c xio-proxy.c xio-udp.c \
xio-progcall.c xio-exec.c xio-system.c xio-termios.c xio-readline.c \ xio-progcall.c xio-exec.c xio-system.c xio-shell.c \
xio-termios.c xio-readline.c \
xio-pty.c xio-openssl.c xio-streams.c xio-namespaces.c \ xio-pty.c xio-openssl.c xio-streams.c xio-namespaces.c \
xio-ascii.c xiolockfile.c xio-tcpwrap.c xio-fs.c xio-tun.c xio-ascii.c xiolockfile.c xio-tcpwrap.c xio-fs.c xio-tun.c
XIOOBJS = $(XIOSRCS:.c=.o) XIOOBJS = $(XIOSRCS:.c=.o)
@ -68,7 +69,7 @@ HFILES = sycls.h sslcls.h error.h dalan.h procan.h filan.h hostan.h sysincludes.
xio-ip.h xio-ip4.h xio-ip6.h xio-rawip.h xio-posixmq.h \ xio-ip.h xio-ip4.h xio-ip6.h xio-rawip.h xio-posixmq.h \
xio-ipapp.h xio-tcp.h xio-udp.h xio-sctp.h \ xio-ipapp.h xio-tcp.h xio-udp.h xio-sctp.h \
xio-socks.h xio-socks5.h xio-proxy.h xio-progcall.h xio-exec.h \ xio-socks.h xio-socks5.h xio-proxy.h xio-progcall.h xio-exec.h \
xio-system.h xio-termios.h xio-readline.h \ xio-system.h xio-shell.h xio-termios.h xio-readline.h \
xio-pty.h xio-openssl.h xio-streams.h xio-namespaces.h \ xio-pty.h xio-openssl.h xio-streams.h xio-namespaces.h \
xio-ascii.h xiolockfile.h xio-tcpwrap.h xio-fs.h xio-tun.h xio-ascii.h xiolockfile.h xio-tcpwrap.h xio-fs.h xio-tun.h

View file

@ -713,6 +713,7 @@
#undef WITH_PROXY #undef WITH_PROXY
#undef WITH_EXEC #undef WITH_EXEC
#undef WITH_SYSTEM #undef WITH_SYSTEM
#undef WITH_SHELL
#undef WITH_READLINE #undef WITH_READLINE
#undef WITH_TUN #undef WITH_TUN
#undef WITH_PTY #undef WITH_PTY

View file

@ -489,6 +489,14 @@ AC_ARG_ENABLE(system, [ --disable-system disable system (shell) support]
esac], esac],
[AC_DEFINE(WITH_SYSTEM) AC_MSG_RESULT(yes)]) [AC_DEFINE(WITH_SYSTEM) AC_MSG_RESULT(yes)])
AC_MSG_CHECKING([whether to include shell invocation support])
AC_ARG_ENABLE(system, [ --disable-shell disable shell invocation support],
[case "$enableval" in
no) AC_MSG_RESULT(no);;
*) AC_DEFINE(WITH_SHELL) AC_MSG_RESULT(yes);;
esac],
[AC_DEFINE(WITH_SHELL) AC_MSG_RESULT(yes)])
AC_MSG_CHECKING(whether to include pty address support) AC_MSG_CHECKING(whether to include pty address support)
AC_ARG_ENABLE(pty, [ --disable-pty disable pty support], AC_ARG_ENABLE(pty, [ --disable-pty disable pty support],
[case "$enableval" in [case "$enableval" in

View file

@ -335,6 +335,7 @@ label(ADDRESS_EXEC)dit(bf(tt(EXEC:<command-line>)))
link(su)(OPTION_SUBSTUSER), link(su)(OPTION_SUBSTUSER),
link(su-d)(OPTION_SUBSTUSER_DELAYED), link(su-d)(OPTION_SUBSTUSER_DELAYED),
link(nofork)(OPTION_NOFORK), link(nofork)(OPTION_NOFORK),
link(socktype)(OPTION_SO_TYPE),
link(pty)(OPTION_PTY), link(pty)(OPTION_PTY),
link(stderr)(OPTION_STDERR), link(stderr)(OPTION_STDERR),
link(ctty)(OPTION_CTTY), link(ctty)(OPTION_CTTY),
@ -344,7 +345,7 @@ label(ADDRESS_EXEC)dit(bf(tt(EXEC:<command-line>)))
link(sigint)(OPTION_SIGINT), link(sigint)(OPTION_SIGINT),
link(sigquit)(OPTION_SIGQUIT), link(sigquit)(OPTION_SIGQUIT),
link(netns)(OPTION_NETNS)nl() link(netns)(OPTION_NETNS)nl()
See also: link(SYSTEM)(ADDRESS_SYSTEM) See also: link(SYSTEM)(ADDRESS_SYSTEM),link(SHELL)(ADDRESS_SHELL)
label(ADDRESS_FD)dit(bf(tt(FD:<fdnum>))) label(ADDRESS_FD)dit(bf(tt(FD:<fdnum>)))
Uses the file descriptor link(<fdnum>)(TYPE_FDNUM). It must already exist as Uses the file descriptor link(<fdnum>)(TYPE_FDNUM). It must already exist as
valid unix() file descriptor.nl() valid unix() file descriptor.nl()
@ -785,7 +786,7 @@ label(ADDRESS_PTY)dit(bf(tt(PTY)))
See also: See also:
link(UNIX-LISTEN)(ADDRESS_UNIX_LISTEN), link(UNIX-LISTEN)(ADDRESS_UNIX_LISTEN),
link(PIPE)(ADDRESS_NAMED_PIPE), link(PIPE)(ADDRESS_NAMED_PIPE),
link(EXEC)(ADDRESS_EXEC), link(SYSTEM)(ADDRESS_SYSTEM) link(EXEC)(ADDRESS_EXEC), link(SYSTEM)(ADDRESS_SYSTEM), link(SHELL)(ADDRESS_SHELL)
label(ADDRESS_READLINE)dit(bf(tt(READLINE))) label(ADDRESS_READLINE)dit(bf(tt(READLINE)))
Uses GNU readline and history on stdio to allow editing and reusing input Uses GNU readline and history on stdio to allow editing and reusing input
lines (link(example)(EXAMPLE_ADDRESS_READLINE)). This requires the GNU readline and lines (link(example)(EXAMPLE_ADDRESS_READLINE)). This requires the GNU readline and
@ -1087,6 +1088,32 @@ label(ADDRESS_STDOUT)dit(bf(tt(STDOUT)))
link(-U)(option_U), and link(dual addresses)(ADDRESS_DUAL).nl() link(-U)(option_U), and link(dual addresses)(ADDRESS_DUAL).nl()
Option groups: link(FD)(GROUP_FD) (link(TERMIOS)(GROUP_TERMIOS),link(REG)(GROUP_REG),link(SOCKET)(GROUP_SOCKET)) nl() Option groups: link(FD)(GROUP_FD) (link(TERMIOS)(GROUP_TERMIOS),link(REG)(GROUP_REG),link(SOCKET)(GROUP_SOCKET)) nl()
See also: link(FD)(ADDRESS_FD) See also: link(FD)(ADDRESS_FD)
label(ADDRESS_SHELL)dit(bf(tt(SHELL:<shell-command>)))
Forks a sub process that establishes communication with its parent process
and invokes the specified program with the configured shell ($SHELL).
Note that <shell-command> [link(string)(TYPE_STRING)] must
not contain ',' or "!!", and that shell meta characters may have to be
protected.
After successful program start, socat() writes data to stdin of the
process and reads from its stdout.nl()
Option groups: link(FD)(GROUP_FD),link(SOCKET)(GROUP_SOCKET),link(EXEC)(GROUP_EXEC),link(FORK)(GROUP_FORK),link(TERMIOS)(GROUP_TERMIOS) nl()
Useful options:
link(path)(OPTION_PATH),
link(fdin)(OPTION_FDIN),
link(fdout)(OPTION_FDOUT),
link(chroot)(OPTION_CHROOT),
link(su)(OPTION_SUBSTUSER),
link(su-d)(OPTION_SUBSTUSER_DELAYED),
link(nofork)(OPTION_NOFORK),
link(socktype)(OPTION_SO_TYPE),
link(pty)(OPTION_PTY),
link(stderr)(OPTION_STDERR),
link(ctty)(OPTION_CTTY),
link(setsid)(OPTION_SETSID),
link(pipes)(OPTION_PIPES),
link(sigint)(OPTION_SIGINT),
link(sigquit)(OPTION_SIGQUIT)nl()
See also: link(EXEC)(ADDRESS_EXEC), link(SYSTEM)(ADDRESS_SYSTEM)
label(ADDRESS_SYSTEM)dit(bf(tt(SYSTEM:<shell-command>))) label(ADDRESS_SYSTEM)dit(bf(tt(SYSTEM:<shell-command>)))
Forks a sub process that establishes communication with its parent process Forks a sub process that establishes communication with its parent process
and invokes the specified program with code(system()). Please note that and invokes the specified program with code(system()). Please note that
@ -1104,6 +1131,7 @@ label(ADDRESS_SYSTEM)dit(bf(tt(SYSTEM:<shell-command>)))
link(su)(OPTION_SUBSTUSER), link(su)(OPTION_SUBSTUSER),
link(su-d)(OPTION_SUBSTUSER_DELAYED), link(su-d)(OPTION_SUBSTUSER_DELAYED),
link(nofork)(OPTION_NOFORK), link(nofork)(OPTION_NOFORK),
link(socktype)(OPTION_SO_TYPE),
link(pty)(OPTION_PTY), link(pty)(OPTION_PTY),
link(stderr)(OPTION_STDERR), link(stderr)(OPTION_STDERR),
link(ctty)(OPTION_CTTY), link(ctty)(OPTION_CTTY),
@ -1112,7 +1140,7 @@ label(ADDRESS_SYSTEM)dit(bf(tt(SYSTEM:<shell-command>)))
link(sigint)(OPTION_SIGINT), link(sigint)(OPTION_SIGINT),
link(sigquit)(OPTION_SIGQUIT), link(sigquit)(OPTION_SIGQUIT),
link(netns)(OPTION_NETNS)nl() link(netns)(OPTION_NETNS)nl()
See also: link(EXEC)(ADDRESS_EXEC) See also: link(EXEC)(ADDRESS_EXEC), link(SHELL)(ADDRESS_SHELL)
label(ADDRESS_TCP_CONNECT)dit(bf(tt(TCP:<host>:<port>))) label(ADDRESS_TCP_CONNECT)dit(bf(tt(TCP:<host>:<port>)))
Connects to <port> [link(TCP service)(TYPE_TCP_SERVICE)] on Connects to <port> [link(TCP service)(TYPE_TCP_SERVICE)] on
<host> [link(IP address)(TYPE_IP_ADDRESS)] using TCP/IP version 4 or 6 <host> [link(IP address)(TYPE_IP_ADDRESS)] using TCP/IP version 4 or 6
@ -2163,6 +2191,7 @@ label(OPTION_SO_TYPE)dit(bf(tt(socktype=<type>)))
[link(int)(TYPE_INT)]. Address resolution is not affected by this option. [link(int)(TYPE_INT)]. Address resolution is not affected by this option.
Under Linux, 1 means stream oriented socket, 2 means datagram socket, 3 Under Linux, 1 means stream oriented socket, 2 means datagram socket, 3
means raw socket, and 5 seqpacket (stream keeping packet boundaries). means raw socket, and 5 seqpacket (stream keeping packet boundaries).
Datagrams are useful when you want to keep packet boundaries.
label(OPTION_SO_PROTOCOL)dit(bf(tt(protocol))) label(OPTION_SO_PROTOCOL)dit(bf(tt(protocol)))
Sets the protocol of the socket, specified as third argument to the Sets the protocol of the socket, specified as third argument to the
code(socket()) or code(socketpair()) calls, to <protocol> code(socket()) or code(socketpair()) calls, to <protocol>
@ -2786,6 +2815,19 @@ enddit()
startdit()enddit()nl() startdit()enddit()nl()
label(GROUP_SHELL)
Options for link(address SHELL)(ADDRESS_SHELL)
label(OPTION_SHELL)dit(bf(tt(shell=<filename>)))
Overwrites use the default shell with the named executable, e.g.
tt(/bin/dash). Also sets the tt(SHELL) environment variable.
enddit()
startdit()enddit()nl()
label(GROUP_TERMIOS)em(bf(TERMIOS option group)) label(GROUP_TERMIOS)em(bf(TERMIOS option group))
For addresses that work on a tty (e.g., stdio, file:/dev/tty, exec:...,pty), the terminal parameters defined in the unix() termios mechanism are made available as address option parameters. For addresses that work on a tty (e.g., stdio, file:/dev/tty, exec:...,pty), the terminal parameters defined in the unix() termios mechanism are made available as address option parameters.
@ -4409,8 +4451,8 @@ temporary versions; can be used in scripts invoked by socat.
dit(bf(SOCAT_PID) (output)) Socat sets this variable to its process id. In case dit(bf(SOCAT_PID) (output)) Socat sets this variable to its process id. In case
of link(fork)(OPTION_FORK) address option, SOCAT_PID gets the child processes of link(fork)(OPTION_FORK) address option, SOCAT_PID gets the child processes
id. Forking for link(exec)(ADDRESS_EXEC) and link(system)(ADDRESS_SYSTEM) does id. Forking for link(exec)(ADDRESS_EXEC), link(system)(ADDRESS_SYSTEM), and
not change SOCAT_PID. link(SHELL)(ADDRESS_SHELL) does not change SOCAT_PID.
dit(bf(SOCAT_PPID) (output)) Socat sets this variable to its process id. In dit(bf(SOCAT_PPID) (output)) Socat sets this variable to its process id. In
case of link(fork)(OPTION_FORK), SOCAT_PPID keeps the pid of the master process. case of link(fork)(OPTION_FORK), SOCAT_PPID keeps the pid of the master process.
@ -4506,8 +4548,8 @@ link(su-d)(OPTION_SUBSTUSER_DELAYED), SHELL is set to the login shell of the
given user. given user.
dit(bf(PATH) (output)) dit(bf(PATH) (output))
Can be set with option link(path)(OPTION_PATH) for link(exec)(ADDRESS_EXEC) and Can be set with option link(path)(OPTION_PATH) for link(exec)(ADDRESS_EXEC),
link(system)(ADDRESS_SYSTEM) addresses. link(system)(ADDRESS_SYSTEM), and link(SHELL)(ADDRESS_SHELL) addresses.
dit(bf(HOME) (output)) dit(bf(HOME) (output))
With options link(su)(OPTION_SUBSTUSER) and With options link(su)(OPTION_SUBSTUSER) and

View file

@ -644,6 +644,11 @@ void socat_version(FILE *fd) {
#else #else
fputs(" #undef WITH_SYSTEM\n", fd); fputs(" #undef WITH_SYSTEM\n", fd);
#endif #endif
#ifdef WITH_SHELL
fprintf(fd, " #define WITH_SHELL %d\n", WITH_SHELL);
#else
fputs(" #undef WITH_SHELL\n", fd);
#endif
#ifdef WITH_EXEC #ifdef WITH_EXEC
fprintf(fd, " #define WITH_EXEC %d\n", WITH_EXEC); fprintf(fd, " #define WITH_EXEC %d\n", WITH_EXEC);
#else #else

240
test.sh
View file

@ -814,11 +814,11 @@ childprocess () {
return 0 return 0
} }
# return a list of child process pids # return a list of child process pids [killchild]
childpids () { childpids () {
case "$UNAME" in case "$UNAME" in
AIX) l="$(ps -fade |grep "^........ ...... $(printf %6u $1)" |awk '{print($2);}')" ;; AIX) l="$(ps -fade |grep "^........ ...... $(printf %6u $1)" |awk '{print($2);}')" ;;
FreeBSD) l="$(ps -fl |grep "^[^ ][^ ]*[ ][ ]*[0-9][0-9]*[ ][ ]*$(printf %5u $1)" |awk '{print($2);}')" ;; FreeBSD) l="$(ps -fl |grep "^[^ ][^ ]*[ ][ ]*[0-9][0-9]*[ ][ ]*$1[ ]" |awk '{print($2);}')" ;;
HP-UX) l="$(ps -fade |grep "^........ ..... $(printf %5u $1)" |awk '{print($2);}')" ;; HP-UX) l="$(ps -fade |grep "^........ ..... $(printf %5u $1)" |awk '{print($2);}')" ;;
# Linux) l="$(ps -fade |grep "^........ ..... $(printf %5u $1)" |awk '{print($2);}')" ;; # Linux) l="$(ps -fade |grep "^........ ..... $(printf %5u $1)" |awk '{print($2);}')" ;;
Linux) l="$(ps -fade |grep "^[^[:space:]][^[:space:]]*[[:space:]][[:space:]]*[^[:space:]][^[:space:]]*[[:space:]][[:space:]]*$1 " |awk '{print($2);}')" ;; Linux) l="$(ps -fade |grep "^[^[:space:]][^[:space:]]*[[:space:]][[:space:]]*[^[:space:]][^[:space:]]*[[:space:]][[:space:]]*$1 " |awk '{print($2);}')" ;;
@ -4466,7 +4466,7 @@ elif ! feat=$(testfeats readline pty); then
numCANT=$((numCANT+1)) numCANT=$((numCANT+1))
listCANT="$listCANT $N" listCANT="$listCANT $N"
else else
SAVETERM="$TERM"; TERM= # 'cause konsole might print controls even in raw SAVETERM="$TERM"; TERM= # 'cause console might print controls even in raw
SAVEMICS=$MICROS SAVEMICS=$MICROS
#MICROS=2000000 #MICROS=2000000
ts="$td/test$N.sh" ts="$td/test$N.sh"
@ -4478,7 +4478,7 @@ tr="$td/test$N.ref"
tdiff="$td/test$N.diff" tdiff="$td/test$N.diff"
da="test$N $(date) $RANDOM"; da="$da$($ECHO '\r')" da="test$N $(date) $RANDOM"; da="$da$($ECHO '\r')"
# the feature that we really want to test is in the readline.sh script: # the feature that we really want to test is in the readline.sh script:
CMD="$TRACE $SOCAT $opts -t1 open:$tpi,nonblock!!open:$tpo exec:\"./readline.sh -nh ./readline-test.sh\",pty,ctty,setsid,raw,echo=0,isig" CMD="$TRACE $SOCAT -lpwrapper $opts -t1 open:$tpi,nonblock!!open:$tpo exec:\"./readline.sh -nh ./readline-test.sh\",pty,ctty,setsid,raw,echo=0,isig"
#echo "$CMD" >"$ts" #echo "$CMD" >"$ts"
#chmod a+x "$ts" #chmod a+x "$ts"
printf "test $F_n $TEST... " $N printf "test $F_n $TEST... " $N
@ -4487,6 +4487,7 @@ mkfifo "$tpi"
touch "$tpo" touch "$tpo"
# #
# during development of this test, the following command line succeeded: # during development of this test, the following command line succeeded:
# ECHO="echo -e" SOCAT=./socat
# (sleep 1; $ECHO "user\n\c"; sleep 1; $ECHO "password\c"; sleep 1; $ECHO "\n\c"; sleep 1; $ECHO "test 1\n\c"; sleep 1; $ECHO "\003\c"; sleep 1; $ECHO "test 2\n\c"; sleep 1; $ECHO "exit\n\c"; sleep 1) |$TRACE $SOCAT -d -d -d -d -lf/tmp/$USER/debug1 -v -x - exec:'./readline.sh ./readline-test.sh',pty,ctty,setsid,raw,echo=0,isig # (sleep 1; $ECHO "user\n\c"; sleep 1; $ECHO "password\c"; sleep 1; $ECHO "\n\c"; sleep 1; $ECHO "test 1\n\c"; sleep 1; $ECHO "\003\c"; sleep 1; $ECHO "test 2\n\c"; sleep 1; $ECHO "exit\n\c"; sleep 1) |$TRACE $SOCAT -d -d -d -d -lf/tmp/$USER/debug1 -v -x - exec:'./readline.sh ./readline-test.sh',pty,ctty,setsid,raw,echo=0,isig
# #
# the following cat, in case of socat failure, reads the pipe to prevent below writer from hanging # the following cat, in case of socat failure, reads the pipe to prevent below writer from hanging
@ -4530,14 +4531,16 @@ EOF
wait wait
if ! tr "$($ECHO '\r \c')" "% " <$tpo |sed 's/%$//g' |sed 's/.*%//g' |diff "$tr" - >"$tdiff" 2>&1; then if ! tr "$($ECHO '\r \c')" "% " <$tpo |sed 's/%$//g' |sed 's/.*%//g' |diff "$tr" - >"$tdiff" 2>&1; then
$PRINTF "$FAILED: $TRACE $SOCAT:\n" $PRINTF "$FAILED: $TRACE $SOCAT:\n"
echo "$CMD" echo "$CMD" 2>&1
cat "$te" cat "$te" 2>&1
cat "$tdiff" echo diff: 2>&1
cat "$tdiff" 2>&1
numFAIL=$((numFAIL+1)) numFAIL=$((numFAIL+1))
listFAIL="$listFAIL $N" listFAIL="$listFAIL $N"
else else
$PRINTF "$OK\n" $PRINTF "$OK\n"
if [ -n "$debug" ]; then cat $te; fi if [ "$VERBOSE" ]; then echo "$CMD0 &"; fi
if [ "$DEBUG" ]; then cat "${te}0" >&2; fi
numOK=$((numOK+1)) numOK=$((numOK+1))
fi fi
kill $pid 2>/dev/null # necc on OpenBSD kill $pid 2>/dev/null # necc on OpenBSD
@ -5978,7 +5981,7 @@ to="$td/test$N.out"
te="$td/test$N.err" te="$td/test$N.err"
tdiff="$td/test$N.diff" tdiff="$td/test$N.diff"
da="test$N $(date) $RANDOM"; da="$da$($ECHO '\r')" da="test$N $(date) $RANDOM"; da="$da$($ECHO '\r')"
# the feature that we really want to test is in the readline.sh script: #
CMD="$TRACE $SOCAT $opts -u open:$ti,readbytes=100 -" CMD="$TRACE $SOCAT $opts -u open:$ti,readbytes=100 -"
printf "test $F_n $TEST... " $N printf "test $F_n $TEST... " $N
rm -f "$tf" "$ti" "$to" rm -f "$tf" "$ti" "$to"
@ -17066,21 +17069,21 @@ esac
N=$((N+1)) N=$((N+1))
# Test the sigint option # Test the sigint option with SHELL address
NAME=EXEC_SIGINT NAME=SHELL_SIGINT
case "$TESTS" in case "$TESTS" in
*%$N%*|*%functions%*|*%socket%*|*%exec%*|*%sigint%*|*%$NAME%*) *%$N%*|*%functions%*|*%socket%*|*%shell%*|*%progcall%*|*%sigint%*|*%$NAME%*)
TEST="$NAME: sigint option with EXEC" TEST="$NAME: sigint option with SHELL"
# Run Socat with an EXEC address invoking Socat, with option sigint # Run Socat with an EXEC address invoking Socat, with option sigint
# Send the parent a SIGINT; when the child gets SIGINT too (vs.SIGTERM) # Send the parent a SIGINT; when the child gets SIGINT too (vs.SIGTERM)
# the test succeeded # the test succeeded
if ! eval $NUMCOND; then :; if ! eval $NUMCOND; then :;
# Remove unneeded checks, adapt lists of the remaining ones # Remove unneeded checks, adapt lists of the remaining ones
elif ! F=$(testfeats STDIO EXEC PIPE); then elif ! F=$(testfeats STDIO SHELL PIPE); then
$PRINTF "test $F_n $TEST... ${YELLOW}Feature $F not configured in $SOCAT${NORMAL}\n" $N $PRINTF "test $F_n $TEST... ${YELLOW}Feature $F not configured in $SOCAT${NORMAL}\n" $N
numCANT=$((numCANT+1)) numCANT=$((numCANT+1))
listCANT="$listCANT $N" listCANT="$listCANT $N"
elif ! A=$(testaddrs STDIO EXEC PIPE); then elif ! A=$(testaddrs STDIO SHELL PIPE); then
$PRINTF "test $F_n $TEST... ${YELLOW}Address $A not available in $SOCAT${NORMAL}\n" $N $PRINTF "test $F_n $TEST... ${YELLOW}Address $A not available in $SOCAT${NORMAL}\n" $N
numCANT=$((numCANT+1)) numCANT=$((numCANT+1))
listCANT="$listCANT $N" listCANT="$listCANT $N"
@ -17093,8 +17096,11 @@ tf="$td/test$N.stdout"
te="$td/test$N.stderr" te="$td/test$N.stderr"
tdiff="$td/test$N.diff" tdiff="$td/test$N.diff"
da="test$N $(date) $RANDOM" da="test$N $(date) $RANDOM"
CMD0="$TRACE $SOCAT $opts -T 2 PIPE EXEC:\"$CAT\",pty,setsid,sigint" #CMD0="$TRACE $SOCAT $opts -T 2 PIPE EXEC:\"socat\ -d\ -d\ -d\ -d\ -lu\ PIPE\ PIPE\",pty,setsid,sigint"
#CMD0="$TRACE $SOCAT $opts -T 2 PIPE EXEC:\"$CAT\",pty,setsid,sigint"
CMD0="$TRACE $SOCAT $opts -T 2 SOCKETPAIR EXEC:\"$CAT\",pty,setsid,sigint"
printf "test $F_n $TEST... " $N printf "test $F_n $TEST... " $N
eval $CMD0 >/dev/null 2>"${te}0" &
$CMD0 >/dev/null 2>"${te}0" & $CMD0 >/dev/null 2>"${te}0" &
pid0=$! pid0=$!
sleep 1 sleep 1
@ -17120,6 +17126,167 @@ esac
N=$((N+1)) N=$((N+1))
# Test the SHELL address with socketpair (default)
NAME=SHELL_SOCKETPAIR
case "$TESTS" in
*%$N%*|*%functions%*|*%shell%*|*%socketpair%*|*%$NAME%*)
TEST="$NAME: simple echo via SHELL of cat with socketpair"
# testecho ...
if ! eval $NUMCOND; then :;
elif ! F=$(testfeats STDIO SHELL); then
$PRINTF "test $F_n $TEST... ${YELLOW}Feature $F not configured in $SOCAT${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
elif ! A=$(testaddrs STDIO SHELL); then
$PRINTF "test $F_n $TEST... ${YELLOW}Address $A not available in $SOCAT${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
else
testecho "$N" "$TEST" "" "SHELL:$CAT" "$opts" "$val_t"
fi
esac
N=$((N+1))
# Test the SHELL address with pipes
NAME=SHELL_PIPES
case "$TESTS" in
*%$N%*|*%functions%*|*%shell%*|*%pipe%*|*%$NAME%*)
TEST="$NAME: simple echo via SHELL of cat with pipes"
# testecho ...
if ! eval $NUMCOND; then :;
elif ! F=$(testfeats STDIO SHELL SOCKETPAIR); then
$PRINTF "test $F_n $TEST... ${YELLOW}Feature $F not configured in $SOCAT${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
elif ! A=$(testaddrs STDIO SHELL PIPE); then
$PRINTF "test $F_n $TEST... ${YELLOW}Address $A not available in $SOCAT${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
else
testecho "$N" "$TEST" "" "SHELL:$CAT,pipes" "$opts" "$val_t"
fi
esac
N=$((N+1))
# Test the SHELL address with pty
NAME=SHELL_PTY
case "$TESTS" in
*%$N%*|*%functions%*|*%shell%*|*%pty%*|*%$NAME%*)
TEST="$NAME: simple echo via SHELL of cat with pty"
# testecho ...
if ! eval $NUMCOND; then :;
elif ! F=$(testfeats STDIO SHELL PTY); then
$PRINTF "test $F_n $TEST... ${YELLOW}Feature $F not configured in $SOCAT${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
elif ! A=$(testaddrs STDIO SHELL PTY); then
$PRINTF "test $F_n $TEST... ${YELLOW}Address $A not available in $SOCAT${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
else
testecho "$N" "$TEST" "" "SHELL:$CAT,pty,$PTYOPTS,$PTYOPTS2" "$opts" "$val_t"
fi
esac
N=$((N+1))
# Test the SHELL address halfclose with socketpair
NAME=SHELL_SOCKETPAIR_FLUSH
case "$TESTS" in
*%$N%*|*%functions%*|*%shell%*|*%socketpair%*|*%halfclose%*|*%$NAME%*)
TEST="$NAME: call od -c via SHELL using socketpair"
# testecho ...
if ! eval $NUMCOND; then :;
elif ! F=$(testfeats STDIO SHELL); then
$PRINTF "test $F_n $TEST... ${YELLOW}Feature $F not configured in $SOCAT${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
elif ! A=$(testaddrs STDIO SHELL); then
$PRINTF "test $F_n $TEST... ${YELLOW}Address $A not available in $SOCAT${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
else
testod "$N" "$TEST" "" "SHELL:$OD_C" "$opts" "$val_t"
fi
esac
N=$((N+1))
# Test SHELL address halfclose with pipes
NAME=SHELL_PIPES
case "$TESTS" in
*%$N%*|*%functions%*|*%shell%*|*%pipe%*|*%halfclose%*|*%$NAME%*)
TEST="$NAME: call od -c via SHELL using pipes"
# testecho ...
if ! eval $NUMCOND; then :;
elif ! F=$(testfeats STDIO SHELL SOCKETPAIR); then
$PRINTF "test $F_n $TEST... ${YELLOW}Feature $F not configured in $SOCAT${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
elif ! A=$(testaddrs STDIO SHELL PIPE); then
$PRINTF "test $F_n $TEST... ${YELLOW}Address $A not available in $SOCAT${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
else
testod "$N" "$TEST" "" "SHELL:$OD_C,pipes" "$opts" "$val_t"
fi
esac
N=$((N+1))
# Test the sigint option with SYSTEM address
NAME=SYSTEM_SIGINT
case "$TESTS" in
*%$N%*|*%functions%*|*%socket%*|*%progcall%*|*%system%*|*%sigint%*|*%$NAME%*)
TEST="$NAME: sigint option with SYSTEM"
# Run Socat with a SYSTEM address invoking Socat, with option sigint
# Send the parent a SIGINT; when the child gets SIGINT too (vs.SIGTERM)
# the test succeeded
# setsid is required so the initial SIGINT is not delivered to the sub process.
if ! eval $NUMCOND; then :;
# Remove unneeded checks, adapt lists of the remaining ones
elif ! F=$(testfeats STDIO SYSTEM PIPE); then
$PRINTF "test $F_n $TEST... ${YELLOW}Feature $F not configured in $SOCAT${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
elif ! A=$(testaddrs STDIO SYSTEM PIPE); then
$PRINTF "test $F_n $TEST... ${YELLOW}Address $A not available in $SOCAT${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
elif ! o=$(testoptions setsid sigint) >/dev/null; then
$PRINTF "test $F_n $TEST... ${YELLOW}Option $o not available in $SOCAT${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 PIPE SHELL:\"$SOCAT\ -dddd\ -lf ${te}1\ PIPE\ PIPE\",setsid,sigint"
CMD0="$TRACE $SOCAT $opts SOCKETPAIR SHELL:\"$SOCAT\ -dddd\ -lf ${te}1\ PIPE\ PIPE\",setsid,sigint"
printf "test $F_n $TEST... " $N
eval $CMD0 >/dev/null 2>"${te}0" &
pid0=$!
sleep 1
kill -INT $(childpids $pid0)
wait
if grep -q " W waitpid..: child .* exited with status 130" "${te}0"; 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
################################################################################## ##################################################################################
@ -17308,14 +17475,25 @@ wait<something>port $PORT 1
echo "$da" |$CMD1 >"${tf}1" 2>"${te}1" echo "$da" |$CMD1 >"${tf}1" 2>"${te}1"
rc1=$? rc1=$?
kill $pid0 2>/dev/null; wait kill $pid0 2>/dev/null; wait
if echo "$da" |diff "${tf}1" - >$tdiff; then if [ "$rc1" -ne 0 ]; then
$PRINTF "$OK\n" $PRINTF "$FAILED\n"
if [ "$VERBOSE" ]; then echo "$CMD0 &"; fi echo "$CMD0 &"
if [ "$DEBUG" ]; then cat "${te}0" >&2; fi cat "${te}0" >&2
if [ "$VERBOSE" ]; then echo "$CMD1"; fi echo "$CMD1"
if [ "$DEBUG" ]; then cat "${te}1" >&2; fi cat "${te}1" >&2
numOK=$((numOK+1)) numFAIL=$((numFAIL+1))
elif [ !?!?! ]; then listFAIL="$listFAIL $N"
namesFAIL="$namesFAIL $NAME"
elif ! echo "$da" |diff "${tf}1" - >$tdiff; then
$PRINTF "$FAILED\n"
echo "$CMD0 &"
cat "${te}0" >&2
echo "$CMD1"
cat "${te}1" >&2
numFAIL=$((numFAIL+1))
listFAIL="$listFAIL $N"
namesFAIL="$namesFAIL $NAME"
elif [ ??? ]; then
# The test could not run meaningfully # The test could not run meaningfully
$PRINTF "$CANT\n" $PRINTF "$CANT\n"
if [ "$VERBOSE" ]; then echo "$CMD0 &"; fi if [ "$VERBOSE" ]; then echo "$CMD0 &"; fi
@ -17325,14 +17503,12 @@ elif [ !?!?! ]; then
numCANT=$((numCANT+1)) numCANT=$((numCANT+1))
listCANT="$listCANT $N" listCANT="$listCANT $N"
else else
$PRINTF "$FAILED\n" $PRINTF "$OK\n"
echo "$CMD0 &" if [ "$VERBOSE" ]; then echo "$CMD0 &"; fi
cat "${te}0" >&2 if [ "$DEBUG" ]; then cat "${te}0" >&2; fi
echo "$CMD1" if [ "$VERBOSE" ]; then echo "$CMD1"; fi
cat "${te}1" >&2 if [ "$DEBUG" ]; then cat "${te}1" >&2; fi
numFAIL=$((numFAIL+1)) numOK=$((numOK+1))
listFAIL="$listFAIL $N"
namesFAIL="$namesFAIL $NAME"
fi fi
fi # NUMCOND fi # NUMCOND
;; ;;

98
xio-shell.c Normal file
View file

@ -0,0 +1,98 @@
/* source: xio-shell.c */
/* Copyright Gerhard Rieger and contributors (see file CHANGES) */
/* Published under the GNU General Public License V.2, see file COPYING */
/* this file contains the source for opening addresses of shell type */
#include "xiosysincludes.h"
#include "xioopen.h"
#include "xio-progcall.h"
#include "xio-shell.h"
#if WITH_SHELL
static int xioopen_shell(int arg, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xfd, const struct addrdesc *addrdesc);
const struct addrdesc xioaddr_shell = { "SHELL", 3, xioopen_shell, GROUP_FD|GROUP_FORK|GROUP_EXEC|GROUP_SOCKET|GROUP_SOCK_UNIX|GROUP_TERMIOS|GROUP_FIFO|GROUP_PTY|GROUP_PARENT|GROUP_SHELL, 1, 0, 0 HELP(":<shell-command>") };
const struct optdesc opt_shell = { "shell", NULL, OPT_SHELL, GROUP_SHELL, PH_PREEXEC, TYPE_STRING, OFUNC_SPEC, 0, 0 };
static int xioopen_shell(
int argc,
const char *argv[],
struct opt *opts,
int xioflags,
xiofile_t *xfd,
const struct addrdesc *addrdesc)
{
struct single *sfd = &xfd->stream;
int status;
char *path = NULL;
int duptostderr;
int result;
char *shellpath = NULL;
const char *shellname;
const char *string = argv[1];
if (argc != 2) {
xio_syntax(argv[0], 1, argc-1, addrdesc->syntax);
return STAT_NORETRY;
}
shellpath = getenv("SHELL");
retropt_string(opts, OPT_SHELL, &shellpath);
if (shellpath == NULL) {
Error("SHELL variable undefined");
errno = EINVAL;
return -1;
}
shellname = strrchr(shellpath, '/');
if (shellname == NULL) {
Error1("SHELL \"%s\" variable does not specify a path (has no '/')", shellpath);
errno = EINVAL;
return -1;
}
++shellname;
status = _xioopen_foxec(xioflags, sfd, addrdesc->groups, &opts, &duptostderr);
if (status < 0) return status;
if (status == 0) { /* child */
int numleft;
if (setopt_path(opts, &path) < 0) {
/* this could be dangerous, so let us abort this child... */
Exit(1);
}
if ((numleft = leftopts(opts)) > 0) {
Error1("%d option(s) could not be used", numleft);
showleft(opts);
return STAT_NORETRY;
}
/* only now redirect stderr */
if (duptostderr >= 0) {
diag_dup();
Dup2(duptostderr, 2);
}
Setenv("SHELL", shellpath, 1);
Info1("executing shell command \"%s\"", string);
Debug3("execl(\"%s\", \"%s\", \"-c\", \"%s\", NULL",
shellpath, shellname, string);
result = execl(shellpath, shellname, "-c", string, (char *)NULL);
if (result != 0) {
Warn2("execl(\"%s\") returned with status %d", string, result);
Warn1("execl(): %s", strerror(errno));
}
Exit(0); /* this child process */
}
/* parent */
return 0;
}
#endif /* WITH_SHELL */

12
xio-shell.h Normal file
View file

@ -0,0 +1,12 @@
/* source: xio-shell.h */
/* Copyright Gerhard Rieger and contributors (see file CHANGES) */
/* Published under the GNU General Public License V.2, see file COPYING */
#ifndef __xio_shell_h_included
#define __xio_shell_h_included 1
extern const struct addrdesc xioaddr_shell;
extern const struct optdesc opt_shell;
#endif /* !defined(__xio_shell_h_included) */

View file

@ -43,7 +43,7 @@ static const char *addressgroupnames[] = {
"FD", "FIFO", "CHR", "BLK", "FD", "FIFO", "CHR", "BLK",
"REG", "SOCKET", "READLINE", "undef", "REG", "SOCKET", "READLINE", "undef",
"NAMED", "OPEN", "EXEC", "FORK", "NAMED", "OPEN", "EXEC", "FORK",
"LISTEN", "DEVICE", "CHILD", "RETRY", "LISTEN", "SHELL", "CHILD", "RETRY",
"TERMIOS", "RANGE", "PTY", "PARENT", "TERMIOS", "RANGE", "PTY", "PARENT",
"UNIX", "IP4", "IP6", "INTERFACE", "UNIX", "IP4", "IP6", "INTERFACE",
"UDP", "TCP", "SOCKS4", "OPENSSL", "UDP", "TCP", "SOCKS4", "OPENSSL",

View file

@ -41,6 +41,7 @@
#include "xio-progcall.h" #include "xio-progcall.h"
#include "xio-exec.h" #include "xio-exec.h"
#include "xio-system.h" #include "xio-system.h"
#include "xio-shell.h"
#include "xio-termios.h" #include "xio-termios.h"
#include "xio-readline.h" #include "xio-readline.h"
#include "xio-pty.h" #include "xio-pty.h"

View file

@ -194,6 +194,9 @@ const struct addrname addressnames[] = {
#if WITH_GENERICSOCKET #if WITH_GENERICSOCKET
{ "SENDTO", &xioaddr_socket_sendto }, { "SENDTO", &xioaddr_socket_sendto },
#endif #endif
#if WITH_SHELL
{ "SHELL", &xioaddr_shell },
#endif
#if WITH_GENERICSOCKET #if WITH_GENERICSOCKET
{ "SOCKET-CONNECT", &xioaddr_socket_connect }, { "SOCKET-CONNECT", &xioaddr_socket_connect },
{ "SOCKET-DATAGRAM", &xioaddr_socket_datagram }, { "SOCKET-DATAGRAM", &xioaddr_socket_datagram },

View file

@ -1518,6 +1518,9 @@ const struct optname optionnames[] = {
IF_SOCKET ("setsockopt-string", &opt_setsockopt_string) IF_SOCKET ("setsockopt-string", &opt_setsockopt_string)
IF_ANY ("setuid", &opt_setuid) IF_ANY ("setuid", &opt_setuid)
IF_ANY ("setuid-early", &opt_setuid_early) IF_ANY ("setuid-early", &opt_setuid_early)
#if WITH_SHELL
IF_ANY ("shell", &opt_shell)
#endif
IF_ANY ("shut-close", &opt_shut_close) IF_ANY ("shut-close", &opt_shut_close)
IF_ANY ("shut-down", &opt_shut_down) IF_ANY ("shut-down", &opt_shut_down)
IF_ANY ("shut-none", &opt_shut_none) IF_ANY ("shut-none", &opt_shut_none)

View file

@ -163,7 +163,7 @@ enum e_func {
#define GROUP_FORK 0x00000800 /* communication with forked process */ #define GROUP_FORK 0x00000800 /* communication with forked process */
#define GROUP_LISTEN 0x00001000 /* socket in listening mode */ #define GROUP_LISTEN 0x00001000 /* socket in listening mode */
/* 0x00002000 */ #define GROUP_SHELL 0x00002000
#define GROUP_CHILD 0x00004000 /* autonom child process */ #define GROUP_CHILD 0x00004000 /* autonom child process */
#define GROUP_RETRY 0x00008000 /* when open/connect etc. fails */ #define GROUP_RETRY 0x00008000 /* when open/connect etc. fails */
#define GROUP_TERMIOS 0x00010000 #define GROUP_TERMIOS 0x00010000
@ -655,6 +655,7 @@ enum e_optcode {
OPT_SETUID, OPT_SETUID,
OPT_SETUID_EARLY, OPT_SETUID_EARLY,
OPT_SET_NETNS, /* set net namespace */ OPT_SET_NETNS, /* set net namespace */
OPT_SHELL,
OPT_SHUT_CLOSE, OPT_SHUT_CLOSE,
OPT_SHUT_DOWN, OPT_SHUT_DOWN,
OPT_SHUT_NONE, OPT_SHUT_NONE,

View file

@ -55,7 +55,7 @@ void socatsignalpass(int sig) {
_errno = errno; _errno = errno;
diag_in_handler = 1; diag_in_handler = 1;
Notice1("socatsignalpass(%d)", sig); Notice1("socatsignalpass(sig=%d)", sig);
if ((sigdesc = socat_get_sig_desc(sig)) == NULL) { /* is async-signal-safe */ if ((sigdesc = socat_get_sig_desc(sig)) == NULL) { /* is async-signal-safe */
diag_in_handler = 0; diag_in_handler = 0;
errno = _errno; errno = _errno;
@ -73,7 +73,8 @@ void socatsignalpass(int sig) {
} }
} }
} }
Info1("socatsignalpass(): n=%d", n); if (n >= 0)
Info1("socatsignalpass(): propagated signal to %d sub processes", n);
} }
#if !HAVE_SIGACTION #if !HAVE_SIGACTION
Signal(sig, socatsignalpass); Signal(sig, socatsignalpass);