diff options
author | Peter Stephenson <pws@users.sourceforge.net> | 2003-07-04 16:27:36 +0000 |
---|---|---|
committer | Peter Stephenson <pws@users.sourceforge.net> | 2003-07-04 16:27:36 +0000 |
commit | 554605ee04946160e360354c4aee9102c877ac19 (patch) | |
tree | 0488e3d86aefe4a93707e44a5b267a5379cb4393 | |
parent | e50b688cd5d246a6134fe614ae4efdf9e7dd649e (diff) | |
download | zsh-554605ee04946160e360354c4aee9102c877ac19.tar.gz zsh-554605ee04946160e360354c4aee9102c877ac19.tar.xz zsh-554605ee04946160e360354c4aee9102c877ac19.zip |
18810: Various enhancements and bug fixes for the TCP function suite
-rw-r--r-- | ChangeLog | 7 | ||||
-rw-r--r-- | Doc/Zsh/tcpsys.yo | 126 | ||||
-rw-r--r-- | Functions/TCP/tcp_expect | 8 | ||||
-rw-r--r-- | Functions/TCP/tcp_open | 10 | ||||
-rw-r--r-- | Functions/TCP/tcp_output | 9 | ||||
-rw-r--r-- | Functions/TCP/tcp_point | 29 | ||||
-rw-r--r-- | Functions/TCP/tcp_read | 24 | ||||
-rw-r--r-- | Functions/TCP/tcp_send | 8 | ||||
-rw-r--r-- | Functions/TCP/tcp_shoot | 21 | ||||
-rw-r--r-- | Functions/TCP/tcp_spam | 28 | ||||
-rw-r--r-- | Functions/TCP/tcp_wait | 9 |
11 files changed, 242 insertions, 37 deletions
diff --git a/ChangeLog b/ChangeLog index 6cd615b47..1c6bf0f40 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,12 @@ 2003-07-04 Peter Stephenson <pws@csr.com> + * 18810: Doc/Zsh/tcpsys.yo, Functions/TCP/tcp_expect, + Functions/TCP/tcp_open, Functions/TCP/tcp_output, + Functions/TCP/tcp_point, Functions/TCP/tcp_read, + Functions/TCP/tcp_send, Functions/TCP/tcp_shoot, + Functions/TCP/tcp_spam, Functions/TCP/tcp_wait: Various + enhancements and bug fixes for the TCP function suite. + * 18571: Doc/Zsh/params.yo: Note easy way of replacing associative array elements using +=. (Posted ages ago and forgotten about.) diff --git a/Doc/Zsh/tcpsys.yo b/Doc/Zsh/tcpsys.yo index 2875b8981..1b52b4f4b 100644 --- a/Doc/Zsh/tcpsys.yo +++ b/Doc/Zsh/tcpsys.yo @@ -353,7 +353,7 @@ The command is run in the background, so tt(tcp_proxy) can then accept new connections. It continues to accept new connections until interrupted. ) findex(tcp_spam) -item(tt(tcp_spam [-rtv] [ -a | -s ) var(sess) tt(| -l) var(sess)tt(,... ]) var(cmd) tt(...))( +item(tt(tcp_spam [-ertv] [ -a | -s ) var(sess) tt(| -l) var(sess)tt(,... ]) var(cmd) tt(...))( Execute `var(cmd) tt(...)' for each session in turn. Note this executes the command and arguments; it does not send the command line as data unless the tt(-t) (transmit) option is given. @@ -374,6 +374,9 @@ The tt(-v) flag specifies that a tt($TCP_PROMPT) will be output before each session. This is output after any modification to TCP_SESS by the user-defined tt(tcp_on_spam) function described below. (Obviously that function is able to generate its own output.) + +If the option tt(-e) is present, the line given as var(cmd ...) is executed +using tt(eval), otherwise it is executed without any further processing. ) findex(tcp_talk) item(tt(tcp_talk))( @@ -403,7 +406,33 @@ installed. ) enditem() -sect(TCP User-defined Function) +subsect(`One-shot' file transfer) +startitem() +xitem(tt(tcp_point) var(port)) +item(tt(tcp_shoot) var(host) var(port))( +This pair of functions provide a simple way to transfer a file between +two hosts within the shell. Note, however, that bulk data transfer is +currently done using tt(cat). tt(tcp_point) reads any data arriving at +var(port) and sends it to standard output; tt(tcp_shoot) connects to +var(port) on var(host) and sends its standard input. Any unused var(port) +may be used; the standard mechanism for picking a port is to think of a +random four-digit number above 1024 until one works. + +To transfer a file from host tt(woodcock) to host tt(springes), on +tt(springes): + +example(tcp_point 8091 >output_file) + +and on tt(woodcock): + +example(tcp_shoot springes 8091 <input_file) + +As these two functions do not require tt(tcp_open) to set up a TCP +connection first, they may need to be autoloaded separately. +) +enditem() + +sect(TCP User-defined Functions) Certain functions, if defined by the user, will be called by the function system in certain contexts. This facility depends on the module @@ -485,9 +514,23 @@ output, from within tt(tcp_read) and (if tt($TCP_OUTPUT) is set) tt(tcp_send). The var(prompt) to use is specified by tt(-P); the default is the empty -string. It can contain `tt(%s)' which is replaced by the session name, or -`tt(%f)' which is replaced by the session's file descriptor; `tt(%%)' is -replaced by a single `tt(%)'. +string. It can contain: +startitem() +item(tt(%c))( +Expands to 1 if the session is the current session, otherwise 0. Used +with ternary expresions such as `tt(%LPAR()c.-.PLUS()RPAR())' to +output `tt(PLUS())' for the current session and `tt(-)' otherwise. +) +item(tt(%f))( +Replaced by the session's file descriptor. +) +item(tt(%s))( +Replaced by the session name. +) +item(tt(%%))( +Replaced by a single `tt(%)'. +) +enditem() The option tt(-q) suppresses output to standard output, but not to any log files which are configured. @@ -514,33 +557,33 @@ setting outside the function. Likewise, `tt(local TCP_SESS=)var(sess)' sets a session for the duration of a function. startitem() -findex(tcp_expect_lines) +vindex(tcp_expect_lines) item(tt(tcp_expect_lines))( Array. The set of lines read during the last call to tt(tcp_expect), including the last (tt($TCP_LINE)). ) -findex(tcp_filter) +vindex(tcp_filter) item(tt(tcp_filter))( Array. May be set directly. A set of extended globbing patterns which, if matched in tt(tcp_output), will cause the line not to be printed to standard output. The patterns should be defined as described for the arguments to tt(tcp_expect). Output of line to log files is not affected. ) -findex(TCP_LINE) +vindex(TCP_LINE) item(tt(TCP_LINE))( The last line read by tt(tcp_read), and hence also tt(tcp_expect). ) -findex(TCP_LINE_FD) +vindex(TCP_LINE_FD) item(tt(TCP_LINE_FD))( The file descriptor from which tt($TCP_LINE) was read. tt(${tcp_by_fd[$TCP_LINE_FD]}) will give the corresponding session name. ) -findex(tcp_lines) +vindex(tcp_lines) item(tt(tcp_lines))( Array. The set of lines read during the last call to tt(tcp_read), including the last (tt($TCP_LINE)). ) -findex(TCP_LOG) +vindex(TCP_LOG) item(tt(TCP_LOG))( May be set directly, although it is also controlled by tt(tcp_log). The name of a file to which output from all sessions will be sent. @@ -555,11 +598,11 @@ will be sent; the full filename is tt(${TCP_LOG_SESS}.)var(sess). Output to each file is raw; no prompt is added. If it is not an absolute path name, it will follow the user's current directory. ) -findex(tcp_nospam_list) +vindex(tcp_nospam_list) item(tt(tcp_nospam_list))( Array. May be set directly. See tt(tcp_spam) for how this is used. ) -findex(TCP_OUTPUT) +vindex(TCP_OUTPUT) item(tt(TCP_OUTPUT))( May be set directly. If a non-empty string, any data sent to a session by tt(tcp_send) will be logged. The prompt has the same format as @@ -567,47 +610,88 @@ tt(TCP_PROMPT) and the same rules for its use apply: it is used in a file specified by tt($TCP_LOG), but not in a file generated from tt($TCP_LOG_SESS). ) -findex(TCP_PROMPT) +vindex(TCP_PROMPT) item(tt(TCP_PROMPT))( May be set directly. Used as the prefix for data read by tt(tcp_read) which is printed to standard output or to the log file given by tt($TCP_LOG), if any. Any `tt(%s)', `tt(%f)' or `tt(%%)' occurring in the string will be replaced by the name of the session, the session's -underlying file descriptor, or a single `tt(%)', respectively. +underlying file descriptor, or a single `tt(%)', respectively. The +expression `tt(%c)' expands to 1 if the session being read is the current +session, else 0; this is most useful in ternary expressions such as +`tt(%LPAR()c.-.PLUS()RPAR())' which outputs `tt(PLUS())' if the session is +the current one, else `tt(-)'. ) -findex(TCP_READ_DEBUG) +vindex(TCP_READ_DEBUG) item(tt(TCP_READ_DEBUG))( May be set directly. If this has non-zero length, tt(tcp_read) will give some limited diagnostics about data being read. ) -findex(TCP_SESS) +vindex(TCP_SECONDS_START) +item(tt(TCP_SECONDS_START))( +This value is created and initialised to zero by tcp_open. + +The functions tt(tcp_read) and tt(tcp_expect) use the shell's +tt(SECONDS) parameter for their own timing purposes. If that parameter +is not of floating point type on entry to one of the functions, it will +create a local parameter tt(SECONDS) which is floating point and set the +parameter tt(TCP_SECONDS_START) to the previous value of tt($SECONDS). +If the parameter is already floating point, it is used without a local +copy being created and tt(TCP_SECONDS_START) is not set. As the global +value is zero, the shell elapsed time is guaranteed to be the sum of +tt($SECONDS) and tt($TCP_SECONDS_START). + +This can be avoided by setting tt(SECONDS) globally to a floating point +value using `tt(typeset -F SECONDS)'; then the TCP functions will never +make a local copy and never set tt(TCP_SECONDS_START) to a non-zero value. +) +vindex(TCP_SESS) item(tt(TCP_SESS))( May be set directly. The current session; must refer to one of the sessions established by tt(tcp_open). ) -findex(TCP_SILENT) +vindex(TCP_SILENT) item(tt(TCP_SILENT))( May be set directly, although it is also controlled by tt(tcp_log). If of non-zero length, data read by tt(tcp_read) will not be written to standard output, though may still be written to a log file. ) -findex(tcp_spam_list) +vindex(tcp_spam_list) item(tt(tcp_spam_list))( Array. May be set directly. See the description of the function tt(tcp_spam) for how this is used. ) -findex(TCP_TALK_ESCAPE) +vindex(TCP_TALK_ESCAPE) item(tt(TCP_TALK_ESCAPE))( May be set directly. See the description of the function tt(tcp_talk) for how this is used. ) -findex(TCP_TIMEOUT) +vindex(TCP_TIMEOUT) item(tt(TCP_TIMEOUT))( May be set directly. Currently this is only used by the function tt(tcp_command), see above. ) enditem() +sect(TCP User-defined Parameters) + +The following parameters are not set by the function system, but have +a special effect if set by the user. + +startitem() +vindex(tcp_on_read) +item(tt(tcp_on_read))( +This should be an associative array; if it is not, the behaviour is +undefined. Each key is the name of a shell function or other command, +and the corresponding value is a shell pattern (using tt(EXTENDED_GLOB)). +Every line read from a TCP session directly or indirectly using +tt(tcp_read) (which includes lines read by tt(tcp_expect)) is compared +against the pattern. If the line matches, the command given in the key is +called with two arguments: the name of the session from which the line was +read, and the line itself. +) +enditem() + sect(TCP Utility Parameters) These parameters are controlled by the function system; they may be read diff --git a/Functions/TCP/tcp_expect b/Functions/TCP/tcp_expect index 14963a3e6..e49f93804 100644 --- a/Functions/TCP/tcp_expect +++ b/Functions/TCP/tcp_expect @@ -37,8 +37,12 @@ emulate -L zsh setopt extendedglob -# Get extra accuracy by making SECONDS floating point locally -typeset -F SECONDS +if [[ ${(t)SECONDS} != float* ]]; then + # If called from another function, use that + typeset -F TCP_SECONDS_START=$SECONDS + # Get extra accuracy by making SECONDS floating point locally + typeset -F SECONDS +fi # Variables are all named _expect_* to avoid problems with the -p param. local _expect_opt _expect_pvar diff --git a/Functions/TCP/tcp_open b/Functions/TCP/tcp_open index f762d932a..1f7a65c9a 100644 --- a/Functions/TCP/tcp_open +++ b/Functions/TCP/tcp_open @@ -53,11 +53,19 @@ emulate -L zsh setopt extendedglob cbases +# Global set up for TCP function suite. + zmodload -i zsh/net/tcp || return 1 zmodload -i zsh/zutil autoload -U tcp_alias tcp_close tcp_command tcp_expect tcp_fd_handler autoload -U tcp_log tcp_output tcp_proxy tcp_read tcp_rename tcp_send -autoload -U tcp_sess tcp_spam tcp_talk tcp_wait +autoload -U tcp_sess tcp_spam tcp_talk tcp_wait tcp_point tcp_shoot + +# TCP_SECONDS_START is only set if we override TCP_SECONDS locally, +# so provide a global value for convenience. Should probably always be 0. +(( ${+TCP_SECONDS_START} )) || typeset -gF TCP_SECONDS_START + +# Processing for new connection. local opt accept fake nozle sessfile sess quiet local -a sessnames sessargs diff --git a/Functions/TCP/tcp_output b/Functions/TCP/tcp_output index 69177bae0..781c46c33 100644 --- a/Functions/TCP/tcp_output +++ b/Functions/TCP/tcp_output @@ -1,7 +1,7 @@ emulate -L zsh setopt extendedglob -local opt tprompt sess read_fd tpat quiet +local opt tprompt sess read_fd tpat quiet cursess while getopts "F:P:qS:" opt; do case $opt in @@ -29,7 +29,12 @@ fi # where data is coming from; also, it allows more predictable # behaviour in tcp_expect. if [[ -n $tprompt ]]; then - zformat -f REPLY $tprompt "s:$sess" "f:$read_fd" + if [[ $sess = $TCP_SESS ]]; then + cursess="c:1" + else + cursess="c:0" + fi + zformat -f REPLY $tprompt "s:$sess" "f:$read_fd" $cursess # We will pass this back up. REPLY="$REPLY$*" else diff --git a/Functions/TCP/tcp_point b/Functions/TCP/tcp_point new file mode 100644 index 000000000..6a8f75d3a --- /dev/null +++ b/Functions/TCP/tcp_point @@ -0,0 +1,29 @@ +emulate -L zsh +setopt extendedglob cbases + + +if [[ $# -ne 1 ]]; then + print "Usage: $0 port +Listen on the given port; send anything that arrives to standard output." >&2 + return 1 +fi + +local REPLY lfd afd +if ! ztcp -l $1; then + print "Failed to listen on port $1" >&2 + return 1 +fi + +lfd=$REPLY + +if ! ztcp -a $lfd; then + print "Failed to accept on fd $lfd" >&2 + ztcp -c $lfd +fi + +afd=$REPLY + +cat <&$afd + +ztcp -c $lfd +ztcp -c $afd diff --git a/Functions/TCP/tcp_read b/Functions/TCP/tcp_read index d9aa47e0e..51e5356cc 100644 --- a/Functions/TCP/tcp_read +++ b/Functions/TCP/tcp_read @@ -60,7 +60,7 @@ setopt extendedglob cbases zmodload -i zsh/mathfunc -local opt drain line quiet block read_fd all sess +local opt drain line quiet block read_fd all sess key val local -A read_fds read_fds=() float timeout timeout_all endtime @@ -139,8 +139,12 @@ tcp_lines=() local helper_stat=2 skip tpat reply REPLY float newtimeout -# Get extra accuracy by making SECONDS floating point locally -typeset -F SECONDS +if [[ ${(t)SECONDS} != float* ]]; then + # If called from another function, don't override + typeset -F TCP_SECONDS_START=$SECONDS + # Get extra accuracy by making SECONDS floating point locally + typeset -F SECONDS +fi if (( timeout_all )); then (( endtime = SECONDS + timeout_all )) @@ -194,11 +198,23 @@ while (( ${#read_fds} )); do helper_stat=0 sess=${tcp_by_fd[$read_fd]} - tcp_output -P "${TCP_PROMPT:=<-[%s] }" -S $sess -F $read_fd \ + tcp_output -P "${TCP_PROMPT=<-[%s] }" -S $sess -F $read_fd \ ${TCP_SILENT:+-q} "$line" # REPLY is now set to the line with an appropriate prompt. tcp_lines+=($REPLY) TCP_LINE=$REPLY TCP_LINE_FD=$read_fd + + # Handle user-defined triggers + if (( ${+tcp_on_read} )); then + # Call the function given in the key for each matching value. + # It is this way round because function names must be + # unique, while patterns do not need to be. Furthermore, + # this keeps the use of subscripting under control. + for key val in ${(kv)tcp_on_read}; do + [[ $line = ${~val} ]] && $key "$sess" "$line" + done + fi + # Only handle one line from one device at a time unless draining. [[ -z $drain ]] && return $stat done diff --git a/Functions/TCP/tcp_send b/Functions/TCP/tcp_send index e7dfca771..c5f902f71 100644 --- a/Functions/TCP/tcp_send +++ b/Functions/TCP/tcp_send @@ -3,6 +3,7 @@ setopt extendedglob cbases local opt quiet all sess fd nonewline local -a sessions write_fds +integer mystat while getopts "al:nqs:" opt; do case $opt in @@ -56,6 +57,11 @@ local TCP_SESS for TCP_SESS in $sessions; do fd=${tcp_by_name[$TCP_SESS]} + if [[ -z $fd ]]; then + print "No such session: $TCP_SESS" >&2 + mystat=1 + continue + fi print $nonewline -r -- $* >&$fd if [[ $? -ne 0 || -n $TCP_FD_CLOSED ]]; then print "Session ${TCP_SESS}: fd $fd unusable." >&2 @@ -65,3 +71,5 @@ for TCP_SESS in $sessions; do tcp_output -P "$TCP_OUTPUT" -S $TCP_SESS -F $fd -q "${(j. .)*}" fi done + +return $mystat diff --git a/Functions/TCP/tcp_shoot b/Functions/TCP/tcp_shoot new file mode 100644 index 000000000..8ff9866ba --- /dev/null +++ b/Functions/TCP/tcp_shoot @@ -0,0 +1,21 @@ +emulate -L zsh +setopt extendedglob + +local REPLY tfd + +if [[ $# -ne 2 ]]; then + print "Usage: tcp_dump host port +Connect to the given host and port; send standard input.">&2 + return 1 +fi + +if ! ztcp $1 $2; then + print "Failed to open connection to host $1 port $2" >&2 + return 1 +fi + +tfd=$REPLY + +cat >&$tfd + +ztcp -c $tfd diff --git a/Functions/TCP/tcp_spam b/Functions/TCP/tcp_spam index 8b84cd0d2..345be7f21 100644 --- a/Functions/TCP/tcp_spam +++ b/Functions/TCP/tcp_spam @@ -4,6 +4,8 @@ # If not given and tcp_spam_list is set to a list of sessions, # only those will be spammed. If tcp_no_spam_list is set, those # will (also) be excluded from spamming. +# -e use `eval' to run the command list instead of executing as +# a normal command line. # -l sess1,sess2 give comma separated list of sessions to spam # -r reverse, spam in opposite order (default is alphabetic, -r means # omegapsiic). Note tcp_spam_list is not sorted (but may be reversed). @@ -19,14 +21,17 @@ emulate -L zsh setopt extendedglob -local TCP_SESS cmd opt verbose reverse sesslist transmit all +local cursess=$TCP_SESS sessstr +local TCP_SESS cmd opt verbose reverse sesslist transmit all eval local match mbegin mend REPLY local -a sessions -while getopts "al:rtv" opt; do +while getopts "ael:rtv" opt; do case $opt in (a) all=1 ;; + (e) eval=1 + ;; (l) sessions+=(${(s.,.)OPTARG}) ;; (r) reverse=1 @@ -82,7 +87,7 @@ fi if [[ -n $transmit ]]; then cmd=tcp_send -else +elif [[ -z $eval ]]; then cmd=$1 shift fi @@ -95,7 +100,18 @@ for TCP_SESS in $sessions; do tcp_on_spam $TCP_SESS $cmd $* [[ $REPLY = done ]] && continue fi - [[ -n $verbose ]] && zformat -f REPLY $TCP_PROMPT "s:$TCP_SESS" \ - "f:${tcp_by_name[$TCP_SESS]}" && print -r $REPLY - eval $cmd '$*' + if [[ -n $verbose ]]; then + if [[ $TCP_SESS = $cursess ]]; then + sessstr="c:1" + else + sessstr="c:0" + fi + zformat -f REPLY $TCP_PROMPT "s:$TCP_SESS" \ + "f:${tcp_by_name[$TCP_SESS]}" $sessstr && print -r $REPLY + fi + if [[ -n $eval ]]; then + eval $* + else + eval $cmd '$*' + fi done diff --git a/Functions/TCP/tcp_wait b/Functions/TCP/tcp_wait index d18068a66..ee04cf23b 100644 --- a/Functions/TCP/tcp_wait +++ b/Functions/TCP/tcp_wait @@ -1,7 +1,14 @@ # Wait for given number of seconds, reading any data from # all TCP connections while doing so. -typeset -F SECONDS to end +if [[ ${(t)SECONDS} != float* ]]; then + # If called from tcp_expect, don't override + typeset -F TCP_SECONDS_START=$SECONDS + # Get extra accuracy by making SECONDS floating point locally + typeset -F SECONDS +fi + +typeset to end (( to = $1, end = SECONDS + to )) while (( SECONDS < end )); do |