From 03f028a985619579ebcf9becddee8e445a7969f1 Mon Sep 17 00:00:00 2001 From: Gerhard Rieger Date: Sun, 1 Oct 2023 21:24:46 +0200 Subject: [PATCH] New feature SHELL address --- CHANGES | 2 + Makefile.in | 5 +- config.h.in | 1 + configure.ac | 8 ++ doc/socat.yo | 56 ++++++++++-- socat.c | 5 ++ test.sh | 240 ++++++++++++++++++++++++++++++++++++++++++++------- xio-shell.c | 98 +++++++++++++++++++++ xio-shell.h | 12 +++ xiohelp.c | 2 +- xiomodes.h | 1 + xioopen.c | 3 + xioopts.c | 3 + xioopts.h | 3 +- xiosignal.c | 5 +- 15 files changed, 399 insertions(+), 45 deletions(-) create mode 100644 xio-shell.c create mode 100644 xio-shell.h diff --git a/CHANGES b/CHANGES index 550cb53..aeda542 100644 --- a/CHANGES +++ b/CHANGES @@ -133,6 +133,8 @@ Features: Tests: LINUX_POSIXMQ_READ_PRIO LINUX_POSIXMQ_RECV_FORK LINUX_POSIXMQ_RECV_MAXCHILDREN LINUX_POSIXMQ_SEND_MAXCHILDREN + New address SHELL invokes a shell but without the overhead of SYSTEM + 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 dd46d7a..3f0c1bf 100644 --- a/Makefile.in +++ b/Makefile.in @@ -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-sctp.c xio-rawip.c xio-posixmq.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-ascii.c xiolockfile.c xio-tcpwrap.c xio-fs.c xio-tun.c 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-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-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-ascii.h xiolockfile.h xio-tcpwrap.h xio-fs.h xio-tun.h diff --git a/config.h.in b/config.h.in index f24152d..15833ed 100644 --- a/config.h.in +++ b/config.h.in @@ -713,6 +713,7 @@ #undef WITH_PROXY #undef WITH_EXEC #undef WITH_SYSTEM +#undef WITH_SHELL #undef WITH_READLINE #undef WITH_TUN #undef WITH_PTY diff --git a/configure.ac b/configure.ac index 90362c0..338f427 100644 --- a/configure.ac +++ b/configure.ac @@ -489,6 +489,14 @@ AC_ARG_ENABLE(system, [ --disable-system disable system (shell) support] esac], [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_ARG_ENABLE(pty, [ --disable-pty disable pty support], [case "$enableval" in diff --git a/doc/socat.yo b/doc/socat.yo index 8ba7fa1..fe7bffd 100644 --- a/doc/socat.yo +++ b/doc/socat.yo @@ -335,6 +335,7 @@ label(ADDRESS_EXEC)dit(bf(tt(EXEC:))) 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), @@ -344,7 +345,7 @@ label(ADDRESS_EXEC)dit(bf(tt(EXEC:))) link(sigint)(OPTION_SIGINT), link(sigquit)(OPTION_SIGQUIT), 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:))) Uses the file descriptor link()(TYPE_FDNUM). It must already exist as valid unix() file descriptor.nl() @@ -785,7 +786,7 @@ label(ADDRESS_PTY)dit(bf(tt(PTY))) See also: link(UNIX-LISTEN)(ADDRESS_UNIX_LISTEN), 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))) 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 @@ -1087,6 +1088,32 @@ label(ADDRESS_STDOUT)dit(bf(tt(STDOUT))) 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() See also: link(FD)(ADDRESS_FD) +label(ADDRESS_SHELL)dit(bf(tt(SHELL:))) + Forks a sub process that establishes communication with its parent process + and invokes the specified program with the configured shell ($SHELL). + Note that [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:))) Forks a sub process that establishes communication with its parent process and invokes the specified program with code(system()). Please note that @@ -1104,6 +1131,7 @@ label(ADDRESS_SYSTEM)dit(bf(tt(SYSTEM:))) 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), @@ -1112,7 +1140,7 @@ label(ADDRESS_SYSTEM)dit(bf(tt(SYSTEM:))) link(sigint)(OPTION_SIGINT), link(sigquit)(OPTION_SIGQUIT), 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::))) Connects to [link(TCP service)(TYPE_TCP_SERVICE)] on [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=))) [link(int)(TYPE_INT)]. Address resolution is not affected by this option. Under Linux, 1 means stream oriented socket, 2 means datagram socket, 3 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))) Sets the protocol of the socket, specified as third argument to the code(socket()) or code(socketpair()) calls, to @@ -2786,6 +2815,19 @@ enddit() startdit()enddit()nl() +label(GROUP_SHELL) + +Options for link(address SHELL)(ADDRESS_SHELL) + +label(OPTION_SHELL)dit(bf(tt(shell=))) + 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)) 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 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 -not change SOCAT_PID. +id. Forking for link(exec)(ADDRESS_EXEC), link(system)(ADDRESS_SYSTEM), and +link(SHELL)(ADDRESS_SHELL) does not change SOCAT_PID. 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. @@ -4506,8 +4548,8 @@ link(su-d)(OPTION_SUBSTUSER_DELAYED), SHELL is set to the login shell of the given user. dit(bf(PATH) (output)) -Can be set with option link(path)(OPTION_PATH) for link(exec)(ADDRESS_EXEC) and -link(system)(ADDRESS_SYSTEM) addresses. +Can be set with option link(path)(OPTION_PATH) for link(exec)(ADDRESS_EXEC), +link(system)(ADDRESS_SYSTEM), and link(SHELL)(ADDRESS_SHELL) addresses. dit(bf(HOME) (output)) With options link(su)(OPTION_SUBSTUSER) and diff --git a/socat.c b/socat.c index 864415b..8632093 100644 --- a/socat.c +++ b/socat.c @@ -644,6 +644,11 @@ void socat_version(FILE *fd) { #else fputs(" #undef WITH_SYSTEM\n", fd); #endif +#ifdef WITH_SHELL + fprintf(fd, " #define WITH_SHELL %d\n", WITH_SHELL); +#else + fputs(" #undef WITH_SHELL\n", fd); +#endif #ifdef WITH_EXEC fprintf(fd, " #define WITH_EXEC %d\n", WITH_EXEC); #else diff --git a/test.sh b/test.sh index 214524a..6e6c98d 100755 --- a/test.sh +++ b/test.sh @@ -814,11 +814,11 @@ childprocess () { return 0 } -# return a list of child process pids +# return a list of child process pids [killchild] childpids () { case "$UNAME" in 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);}')" ;; # 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);}')" ;; @@ -4466,7 +4466,7 @@ elif ! feat=$(testfeats readline pty); then numCANT=$((numCANT+1)) listCANT="$listCANT $N" 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 #MICROS=2000000 ts="$td/test$N.sh" @@ -4478,7 +4478,7 @@ tr="$td/test$N.ref" tdiff="$td/test$N.diff" 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 -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" #chmod a+x "$ts" printf "test $F_n $TEST... " $N @@ -4487,6 +4487,7 @@ mkfifo "$tpi" touch "$tpo" # # 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 # # the following cat, in case of socat failure, reads the pipe to prevent below writer from hanging @@ -4530,14 +4531,16 @@ EOF wait if ! tr "$($ECHO '\r \c')" "% " <$tpo |sed 's/%$//g' |sed 's/.*%//g' |diff "$tr" - >"$tdiff" 2>&1; then $PRINTF "$FAILED: $TRACE $SOCAT:\n" - echo "$CMD" - cat "$te" - cat "$tdiff" + echo "$CMD" 2>&1 + cat "$te" 2>&1 + echo diff: 2>&1 + cat "$tdiff" 2>&1 numFAIL=$((numFAIL+1)) listFAIL="$listFAIL $N" else $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)) fi kill $pid 2>/dev/null # necc on OpenBSD @@ -5978,7 +5981,7 @@ to="$td/test$N.out" te="$td/test$N.err" tdiff="$td/test$N.diff" 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 -" printf "test $F_n $TEST... " $N rm -f "$tf" "$ti" "$to" @@ -17066,21 +17069,21 @@ esac N=$((N+1)) -# Test the sigint option -NAME=EXEC_SIGINT +# Test the sigint option with SHELL address +NAME=SHELL_SIGINT case "$TESTS" in -*%$N%*|*%functions%*|*%socket%*|*%exec%*|*%sigint%*|*%$NAME%*) -TEST="$NAME: sigint option with EXEC" +*%$N%*|*%functions%*|*%socket%*|*%shell%*|*%progcall%*|*%sigint%*|*%$NAME%*) +TEST="$NAME: sigint option with SHELL" # Run Socat with an EXEC address invoking Socat, with option sigint # Send the parent a SIGINT; when the child gets SIGINT too (vs.SIGTERM) # the test succeeded if ! eval $NUMCOND; then :; # 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 numCANT=$((numCANT+1)) 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 numCANT=$((numCANT+1)) listCANT="$listCANT $N" @@ -17093,8 +17096,11 @@ tf="$td/test$N.stdout" te="$td/test$N.stderr" tdiff="$td/test$N.diff" 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 +eval $CMD0 >/dev/null 2>"${te}0" & $CMD0 >/dev/null 2>"${te}0" & pid0=$! sleep 1 @@ -17120,6 +17126,167 @@ esac 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 ################################################################################## @@ -17308,14 +17475,25 @@ waitport $PORT 1 echo "$da" |$CMD1 >"${tf}1" 2>"${te}1" rc1=$? kill $pid0 2>/dev/null; wait -if echo "$da" |diff "${tf}1" - >$tdiff; then - $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)) -elif [ !?!?! ]; then +if [ "$rc1" -ne 0 ]; 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 ! 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 $PRINTF "$CANT\n" if [ "$VERBOSE" ]; then echo "$CMD0 &"; fi @@ -17325,14 +17503,12 @@ elif [ !?!?! ]; then numCANT=$((numCANT+1)) listCANT="$listCANT $N" else - $PRINTF "$FAILED\n" - echo "$CMD0 &" - cat "${te}0" >&2 - echo "$CMD1" - cat "${te}1" >&2 - numFAIL=$((numFAIL+1)) - listFAIL="$listFAIL $N" - namesFAIL="$namesFAIL $NAME" + $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 ;; diff --git a/xio-shell.c b/xio-shell.c new file mode 100644 index 0000000..8a41e21 --- /dev/null +++ b/xio-shell.c @@ -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(":") }; + +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 */ diff --git a/xio-shell.h b/xio-shell.h new file mode 100644 index 0000000..7524c25 --- /dev/null +++ b/xio-shell.h @@ -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) */ diff --git a/xiohelp.c b/xiohelp.c index 36edf7e..2e0b66b 100644 --- a/xiohelp.c +++ b/xiohelp.c @@ -43,7 +43,7 @@ static const char *addressgroupnames[] = { "FD", "FIFO", "CHR", "BLK", "REG", "SOCKET", "READLINE", "undef", "NAMED", "OPEN", "EXEC", "FORK", - "LISTEN", "DEVICE", "CHILD", "RETRY", + "LISTEN", "SHELL", "CHILD", "RETRY", "TERMIOS", "RANGE", "PTY", "PARENT", "UNIX", "IP4", "IP6", "INTERFACE", "UDP", "TCP", "SOCKS4", "OPENSSL", diff --git a/xiomodes.h b/xiomodes.h index 971a9a7..68b6fbd 100644 --- a/xiomodes.h +++ b/xiomodes.h @@ -41,6 +41,7 @@ #include "xio-progcall.h" #include "xio-exec.h" #include "xio-system.h" +#include "xio-shell.h" #include "xio-termios.h" #include "xio-readline.h" #include "xio-pty.h" diff --git a/xioopen.c b/xioopen.c index 110aef3..ca7e4d1 100644 --- a/xioopen.c +++ b/xioopen.c @@ -194,6 +194,9 @@ const struct addrname addressnames[] = { #if WITH_GENERICSOCKET { "SENDTO", &xioaddr_socket_sendto }, #endif +#if WITH_SHELL + { "SHELL", &xioaddr_shell }, +#endif #if WITH_GENERICSOCKET { "SOCKET-CONNECT", &xioaddr_socket_connect }, { "SOCKET-DATAGRAM", &xioaddr_socket_datagram }, diff --git a/xioopts.c b/xioopts.c index a751ee4..ef8db90 100644 --- a/xioopts.c +++ b/xioopts.c @@ -1518,6 +1518,9 @@ const struct optname optionnames[] = { IF_SOCKET ("setsockopt-string", &opt_setsockopt_string) IF_ANY ("setuid", &opt_setuid) 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-down", &opt_shut_down) IF_ANY ("shut-none", &opt_shut_none) diff --git a/xioopts.h b/xioopts.h index 99a0708..496b088 100644 --- a/xioopts.h +++ b/xioopts.h @@ -163,7 +163,7 @@ enum e_func { #define GROUP_FORK 0x00000800 /* communication with forked process */ #define GROUP_LISTEN 0x00001000 /* socket in listening mode */ -/* 0x00002000 */ +#define GROUP_SHELL 0x00002000 #define GROUP_CHILD 0x00004000 /* autonom child process */ #define GROUP_RETRY 0x00008000 /* when open/connect etc. fails */ #define GROUP_TERMIOS 0x00010000 @@ -655,6 +655,7 @@ enum e_optcode { OPT_SETUID, OPT_SETUID_EARLY, OPT_SET_NETNS, /* set net namespace */ + OPT_SHELL, OPT_SHUT_CLOSE, OPT_SHUT_DOWN, OPT_SHUT_NONE, diff --git a/xiosignal.c b/xiosignal.c index 36d0595..6fe1e3b 100644 --- a/xiosignal.c +++ b/xiosignal.c @@ -55,7 +55,7 @@ void socatsignalpass(int sig) { _errno = errno; 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 */ diag_in_handler = 0; 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 Signal(sig, socatsignalpass);