#!/bin/sh - # # expect-shell functions # # Script to interact with a telnet network connection # # NOTE: This script is ancient. It relies on the executables "pty" and # "waitfor", and uses "telnet" for its network connection. But is still # a highly relevant example of co-processing techniques. It only used normal # "Bourne shell" scripting. # # "pty" allows programs needing a tty (like telnet) to accept standard input # and output when program does not allow piping. It also ensures the data # remains line buffered, rather than waiting for a full 'block' of text. # # "waitfor" reads standard input until it sees the given character string or # a timeout is reached, (a shell "expect" statement). It does NOT read standard # input any further than the last character of this string. # # WARNING: some versions of telnet flushes its input after it has connected, As # such you need to delay any automatic input by about 5 seconds, or have the # script wait until it sees the connection message before sending input. # # It may be better to use "netcat", "nc", "mconnect", "socat" or some other # network connection program that does not have the 'buffering' and 'input # flushing' problems of "telnet". I did not have that option at the time. # # File Descriptors used # >&4 Send to the telnet server # <&5 Receive from the telnet server # >&6 Terminal output (for errors and debugging) # ><&8 File opened for upload or download (transfers to remote daemon) # # NOTE: the syntax "exec <&5; read I" was used, due to the way SunOS Bourne # shell "read" only reads stdin (FD:0) It is no longer required in newer POSIX # complaint shells. # #### # # Originally written by Anthony Thyssen, May 1991 # PROGNAME=`basename $0` # base name of program ORIGDIR=`pwd` # original directory BINDIR=`dirname $0` # directory of script cd $BINDIR # cd to script directory BINDIR=`pwd` # set correctly, full path to script DEBUG= # turn debugging output on/off Server="remote.machine" # the server to call Port=echo # on this port (echo port for this example) Prompt='telnet>' # telnet prompt to look for tmpfiles="" # the temporary file to remove on trap abort # ---------------------------------------------------------- # NOTE: these routines use $I as an input buffer. Read() { # just read a line (read telnet info) exec <&5; read I [ "$DEBUG" ] && echo "--> $I" >&6 } ReadIgnore() { # Read and ignore the line, don't even log it exec <&5; read I } CheckRead() { # Read one line and check it matches expected response exec <&5; read I [ "$DEBUG" ] && echo "--> $I" >&6 case "$I" in $1) return 0 ;; *) echo >&6 "$name : \`\`$I''"; return 1 ;; esac } ReadFile() { # Download to the given filename # NOTE: 'end-of-data' marker is a '.' on a line by itself exec <&5 8>"$1"; # open file for write : > "$1" while :; do exec <&5; read I [ "$DEBUG" ] && echo "==> $I" >&6 [ "$I" = '.' ] && break echo >&8 "$I" done exec 8>&- } Send() { # Send a string to the server [ "$DEBUG" ] && echo "<-" "$@" >&6 echo >&4 "$@" } SendRead() { # Send and ignore the normal command echo send "$@"; ReadIgnore } SendFile() { # Upload the given filename to the server # NOTE: the EOD marker is a '.' on a line by itself exec 8<"$1" while exec <&8; read I; do [ "$DEBUG" ] && echo "<=" "$I" >&6 echo >&4 "$I" ReadIgnore done [ "$DEBUG" ] && echo "<= ." >&6 echo >&4 "." ReadIgnore exec 8<&- } WaitFor() { # wait for the given string to be received if [ "$DEBUG" ]; then waitfor "$1" <&5 2>&1 | sed 's/^/ <- /' >&6 else waitfor "$1" <&5 2>/dev/null fi } QuitServer() { # Close the telnet server connection sleep 1 # wait for things to settle echo >&4 "^]" # break back to telnet prompt WaitFor "$Prompt" Send "quit" Read } OpenServer() { # Open the telnet connection to server # also connects shell file handles to the named pipes echo "Contacting $Server on $Port..." pty -d0E telnet recv & exec 4>send 5&- 5<&-; exit 1; } CheckRead "Connected*" || { QuitServer; exec 4>&- 5<&-; exit 1; } CheckRead "Escape*" || { QuitServer; exec 4>&- 5<&-; exit 1; } echo "Connected!" } # ---- Preparation/Cleanup for above --- Cleanup() { exec 4>&- 5<&- 9<&- rm -f $tmpfiles if [ "$DEBUG" ] ; then echo >&6 "TRAP: aborting" exec 6>&- fi } # the abort trap trap 'Cleanup; exit 1;' 1 2 3 15 # Normal exit trap trap 'Cleanup; exit 0;' 0 # create the named pipes required tmpfiles="$tmpfiles send recv" mknod send p mknod recv p exec 6>&2 # set where error are to go. # ---------------------------------------------------------- OpenServer exec <&5; read I; # read the servers first response (if it does) set - $I # and examine it SendRead "TESTING 1 2 3" CheckRead "TESTING*" || { QuitServer; Cleanup; exit; } # ... etc ... QuitServer exit 0 # ----------------------------------------------------------