# SccsId[] = "%W% (USL function) %G%"
              FTPH_name="FTP_HOST"
              if [ ".${SECONDS}" = "." ]; then # Bourne function already loaded?
                 [ ."`set|egrep '^$FTPH_name\(\)\{$'`" != . ] && FTPH_loaded=1
              else # Korn or Bash shell and function already loaded?
                 if [ `expr "\`uname -s\`" : "[Ll][Ii][Nn][Uu][Xx]"` -eq 0 ]; then
                    [ ."`typeset +f|awk '/^'$FTPH_name'[=\(]?/'`" != . ] && FTPH_loaded=1
                 else # Linux
                    [ ."`typeset -F|awk '/^'$FTPH_name'[=\(]?/'`" != . ] && FTPH_loaded=1
                 fi
              fi
              if [ 0${FTPH_loaded} -eq 0 ]; then
              #----------------------------------------------------------------------#
              FTP_HOST() # Function documentation located at bottom.                 #
                         # $1 = host, $2-$n remaining ftp commands                   #
              #----------------------------------------------------------------------#
              {
                OZ=`uname -s 2> /dev/null|tr '[A-Z]' '[a-z]' 2> /dev/null`
                if [ ."$OZ" != ."linux" ]; then
                   AWK="nawk"
                   KILL="/bin/kill"
                else # Linux
                   AWK="gawk"
                   KILL="kill"
                fi

                if [ .${SHLIB} = . ]; then SHLIB=/usr/local/scripts; export SHLIB; fi

                . $SHLIB/chmod_exit_err.sh       # Function dependencies
                . $SHLIB/cp_dev_null_exit_err.sh
                . $SHLIB/email_msg.sh            # Calls $SHLIB/exit.sh
                . $SHLIB/ping_host.sh
                . $SHLIB/rm_exit_err.sh

                : ${id_num=`id|sed 's/^\(uid=\)\([0-9]*\)\(.*\)/\2/'`}
                : ${id_hex=`echo "obase=16;$id_num"|bc`}
                : ${script_name:=`basename $0`}
                : ${name_root:=`echo $script_name|$AWK '{sub(/^\.+/,"");sub(/\..*/,"");print}'`}
                : ${yymmddhhmiss:=`date '+%y''%m%d%H''%M''%S'`}
                : ${Xtimestamp:=`echo "obase=16;$yymmddhhmiss+$$"|bc`}
                : ${sp:="                    "}
                : ${tmp:=/var/tmp}

                FTPH_ID="$script_name($FTPH_name)"

                FTPH_usage=`$AWK -v sp="$sp"                   \
                                 -v FTPH_name="$FTPH_name"     \
                                 -v script_name="$script_name" \
                  'BEGIN \
                   {
                     print sp"Usage:   "FTPH_name" -dpvt sss host"        ,
                      "\047ftp_command\047 [...]\n"                       ,
                      "\n"sp"          -d = Use FTP debug option."        ,
                      "\n"sp"          -n = No ping to host (for firewall",
                      "\n"sp"               restricted hosts)."           ,
                      "\n"sp"          -v = Use FTP verbose option."      ,
                      "\n"sp"          -t = Terminate FTP after sss seconds\n"
                   }'`

                #----------------------------#
                # Parse any function options.#
                #----------------------------#
                FTPH_option=0
                FTPH_debug=0
                FTPH_no_ping=0
                FTPH_vervose=0
                FTPH_sss=86400
                FTPH_opts=""

                FTPH_root=$tmp/$name_root"_FTPH_go_"$id_hex
                FTPH_err=$FTPH_root"."$Xtimestamp
                while getopts dnvt: FTPH_opt 2>> $FTPH_err
                do
                   #---------------------------------------------------------------#
                   # The FTPH_option and FTPH_opt_l are necessary to prevent       #
                   # possible command options (which must be specified after       #
                   # function options) from being confused with function options.  #
                   #---------------------------------------------------------------#
                   case $FTPH_opt in
                      d ) FTPH_opts="${FTPH_opts}d"
                          FTPH_option=1
                          ;;
                      n ) FTPH_no_ping=1
                          FTPH_option=1
                          ;;
                      v ) FTPH_opts="${FTPH_opts}v"
                          FTPH_option=1
                          ;;
                      t ) FTPH_sss="$OPTARG"
                          FTPH_option=1
                          #----------------------------------------------#
                          # Make certain seconds is numeric--exit if no. #
                          #----------------------------------------------#
                          FTPH_num=`  expr "$FTPH_sss" : "[0-9]*"` # Number of digits
                          FTPH_chars=`expr "$FTPH_sss" : ".*"`     # Total num chars
                          if [ $FTPH_num -ne $FTPH_chars ]; then
                             EMAIL_MSG "ERROR (Function): $FTPH_ID"        \
                               "${sp}Invalid seconds following -t option." \
                               "$FTPH_usage"
                             return 255
                          fi
                          FTPH_sss=`expr $FTPH_sss + 1`
                          ;;
                     \? ) echo "$FTPH_ID" \
                            "Invalid option: -`sed 's/^.*-- //' $FTPH_err`" 1>&2
                          ;;
                      * ) ;;
                   esac
                done

                # Shift past options to remaining args
                [ $FTPH_option -ne 0 ] && shift `expr $OPTIND - 1`

                [ ."$FTPH_root" != . ] && \rm -f $FTPH_root* > /dev/null 2>&1
                [ ."$FTPH_opts" != . ] && FTPH_opts="-$FTPH_opts"

                #----------------------------------------------------------------#
                # Must reset this dog if this function is apt to be called again #
                OPTIND=1  # (try and find this fact documented anywhere else).   #
                #----------------------------------------------------------------#

                if [ $# -lt 2 ]; then
                   EMAIL_MSG "ERROR (Function): $FTPH_ID" \
                     "${sp}Insufficient args."            \
                     "$FTPH_usage"
                   return 255
                fi

                FTPH_host=$1; shift

                #------------------------------------------------------#
                # PING_HOST tries up tp 10 times.  Returns ping status.#
                #------------------------------------------------------#
                if [ $FTPH_no_ping -ne 0 ]; then
                   PING_HOST $FTPH_host

                   if [ $? -ne 0 ]; then
                      echo "`date '+%Y-%m-%d %T'` ERROR (Comm): $FTPH_ID" 1>&2
                      echo "${sp}Unable to ping $FTPH_host!"              1>&2
                      echo "${sp}FTP operation terminated."               1>&2
                      return 1
                   fi
                fi

                #------------------------------------------------------------------#
                # OK, we can ping the dog.  Build a script and try FTP'ng the file.#
                #------------------------------------------------------------------#
                FTPH_script=$tmp/$name_root"_FTPH_sc_"$id_hex"."$Xtimestamp
                FTPH_stdout=$tmp/$name_root"_FTPH_s1_"$id_hex"."$Xtimestamp
                FTPH_stderr=$tmp/$name_root"_FTPH_s2_"$id_hex"."$Xtimestamp
                FTPH_pidfil=$tmp/$name_root"_FTPH_PI_"$id_hex"."$Xtimestamp

                CP_DEV_NULL_EXIT_ERR $FTPH_script $FTPH_stdout $FTPH_stderr \
                   $FTPH_pidfil
                CHMOD_EXIT_ERR go-w  $FTPH_script $FTPH_stdout $FTPH_stderr \
                   $FTPH_pidfil
                CHMOD_EXIT_ERR 0600  $FTPH_script $FTPH_stdout $FTPH_stderr \
                   $FTPH_pidfil

                for FTPH_arg
                do
                   /bin/echo "$FTPH_arg" >> $FTPH_script
                   FTPH_echo_status=$?
                   if [ $FTPH_echo_status -ne 0 ]; then
                      EMAIL_MSG "ERROR: $FTPH_ID"                              \
                        "${sp}Problems echoing \"$FTPH_arg\" to $FTPH_script!" \
                        "${sp}$script_name terminated."
                      return 255
                   fi
                done
                echo "quit" >> $FTPH_script # In case the user forgets this.

                #-----------------------------------------------#
                # OK, here's what we're paid the big bucks for. #
                #-----------------------------------------------#
                ftp -n $FTPH_opts $FTPH_host < $FTPH_script \
                   1> $FTPH_stdout 2> $FTPH_stderr & # <== Note: background process
                FTPH_pid=$!                          # Save BG process ID ($!)
                echo $FTPH_pid > $FTPH_pidfil

                FTPH_death_certificate="FTP process assinated"
                #----------------------------------------------------------------#
                # Now wait for the dog to complete (or be assassinated).         #
                #                                                                #
                echo "Waiting for FTP operation ($FTPH_pid) to complete." \
                  >> $FTPH_stdout                                                #
                # echo "Sleep \$FTPH_sss=$FTPH_sss">>$FTPH_stdout  ## T E S T ## #
                   #-----------------------------------------------------------# #
                   # The following background task will terminate the FTP      # #
                   # process with a '-9' if, after sleeping, we find the dog   # #
                   # is still trying run.  We cancel this by first removing    # #
                   # the file, $tmpwrk, then killing this entire background    # #
                   # process as well.                                          # #
                   #                                                           # #
                   (                                                           # #
                      sleep $FTPH_sss                                          # #
                      if [ -s $FTPH_pidfil ]; then                             # #
                         echo "$KILL -9 $FTPH_pid $FTPH_sleep_pid" \
                           "(FTP & sleep process)" >> $FTPH_stdout             # #
                         $KILL -9 $FTPH_pid >> $FTPH_stdout 2>&1               # #
                         echo "$FTPH_death_certificate" > $FTPH_pidfil         # #
                      fi                                                       # #
                   ) &                                                         # #
                   FTPH_ninja=$!                                               # #
                   #                                                           # #
                   #-----------------------------------------------------------# #
                wait $FTPH_pid # Wait for FTP operation to finish                #
                #                                                                #
                #----------------------------------------------------------------#

                #--------------------------------------------------------------#
                # When the FTP operation completes (either normally or by      #
                # unnatural causes), remove $FTPH_pidfil and kill the assassin #
                # process (if it's still lurking about).                       #
                #--------------------------------------------------------------#

                if [ ."`cat $FTPH_pidfil`" != ."$FTPH_death_certificate" ]; then
                   #-------------------------------------------------#
                   # Use ptree command to capture sleep (child) PID. #
                   #-------------------------------------------------#
                   FTPH_sleep_pid=`/usr/proc/bin/ptree $FTPH_ninja \
                     | $AWK '/sleep '$FTPH_sss'/ {print $1}'`
                   echo "$KILL -9 $FTPH_ninja $FTPH_sleep_pid" \
                     "(assassin & sleep processes)"               >> $FTPH_stdout
                   $KILL -9 $FTPH_sleep_pid $FTPH_ninja           >> $FTPH_stdout 2>&1
                   echo "FTP Completed OK.  Assassin terminated." >> $FTPH_stdout
                fi

                #------------------------------------------------------#
                # Get rid of $FTPH_script file right away (because     #
                # of the likelihood that it has a password in it).     #
                # Because output is redirected, RM_EXIT_ERR won't exit.#
                #------------------------------------------------------#
                RM_EXIT_ERR $FTPH_script $FTPH_pidfil >> $FTPH_stdout 2>&1

                #-----------------------------------------------------#
                # "Command not found happens only if "ftp" is not in  #
                # the user's path.  The rest are true FTP errors.     #
                #-----------------------------------------------------#
                $AWK 'BEGIN {n=0}
                      {$0=tolower($0)} # Lowercase every line
                      /login failed/               {n++;next}
                      /not connected/              {n++;next}
                      /connection refused/         {n++;next}
                      /connection timed out/       {n++;next}
                      /connection reset by peer/   {n++;next}
                      /no such file or/            {n++;next}
                      /permission denied/          {n++;next}
                      /command not found/          {n++;next}
                      /resource\(s\) in use/       {n++;next}
                      /no space left on device/    {n++;next}
                      /broken pipe/                {n++;next}
                      /please login with user and/ {n++;next}
                      /ftp process assinated/      {n++;next}
                      END {exit n}' $FTPH_stdout $FTPH_stderr
                FTPH_problems=$?

                #----------------------------------------------------#
                # If either of these dogs has anything in them, then #
                # pass each through stderr or stdout as appropriate. #
                #----------------------------------------------------#
                [  -s $FTPH_stdout ] && cat $FTPH_stdout      # This to stdout
                [  -s $FTPH_stderr ] && cat $FTPH_stderr 1>&2 # and this to stderr
                \rm -f $FTPH_stdout $FTPH_stderr > /dev/null 2>&1

                #----------------------------------------------------------------#
                # If no FTP problems, return success, else return $FTPH_problems.#
                #----------------------------------------------------------------#
                [ $FTPH_problems -eq 0 ] && return 0 || return $FTPH_problems
              } # "FTPH_" prefix identifies this function's local variables.
              fi

              #======================================================================#
              #                       D O C U M E N T A T I O N                      #
              #======================================================================#
              #                                                                      #
              #      Author: Bob Orlando                                             #
              #                                                                      #
              #        Date: March 13, 1998                                          #
              #                                                                      #
              #  Program ID: ftp_host.sh                                             #
              #                                                                      #
              #       Usage: FTP_HOST -dnvt sss host "ftp_command" ...               #
              #                       -d        = Use FTP debug option.              #
              #                        -n       = No ping to host (for firewall-     #
              #                                   restricted hosts).                 #
              #                         -v      = Use FTP verbose option.            #
              #                          -t sss = Terminate FTP after sss seconds    #
              #                                   (defaults to 86400 or 24 hrs).     #
              #                                                                      #
              #     Purpose: Issue FTP commands on specified "host", checking for    #
              #              common FTP errors, returning success (0) only when no   #
              #              errors are found.  FTP's stdout and stderr output are   #
              #              written to either stdout or stderr, respectively,       #
              #              where the user can process them as needed.              #
              #                                                                      #
              #       Calls: Shell library functions: CHMOD_EXIT_ERR,                #
              #                                       CP_DEV_NULL_EXIT_ERR,          #
              #                                       EMAIL_MSG, and                 #
              #                                       PING_HOST.                     #
              #                                                                      #
              #     Globals: If unassigned, the following global variables are       #
              #              are assigned: script_name                               #
              #                            name_root                                 #
              #                            yymmddhhmiss                              #
              #                            Xtimestamp                                #
              #                            sp                                        #
              #                                                                      #
              #              "FTPH_" prefix identifies local function variables.     #
              #                                                                      #
              # Exit_status: 255 for function error (e.g. user supplies invalid      #
              #              arguments).  Else, function returns success or failure  #
              #              of FTP operation.  Failure, while not indicated by      #
              #              FTP's exit status may also be signaled by $ftp_problems #
              #              exceeding 0. Finally, it is the caller's responsibility #
              #              to capture/redirect stdout and stderr as needed (see    #
              #              "Example:").                                            #
              #                                                                      #
              #     Example: #--------------------------------------------------#    #
              #              # List the files in "/data" (note: indentation and #    #
              #              # continuation style used in the this example are  #    #
              #              # for readability only--they are NOT required for  #    #
              #              # this process' successful operation).             #    #
              #              #--------------------------------------------------#    #
              #                FTP_HOST hawkings "cd pub/hosttable"               \  #
              #                                  "ls abc[0-9][0-9][0-9][0-9].fil" \  #
              #                                  "quit" > $stdout 2> $stderr         #
              #              #-------------------------------------------------#     #
              #              # Indent vanilla output with appropriate spaces.  #     #
              #              #-------------------------------------------------#     #
              #                [ -s $stdout ] \                                      #
              #                  && sed "s/^/${stdout_sp}/" $stdout | $teelog        #
              #                [ -s $stderr ] \                                      #
              #                  && sed "s/^/${stderr_sp}/" $stderr | $teelog        #
              #                                                                      #
              #       Notes: The user may test the error variables directly (see     #
              #              FTPH_problems above) to see precisely which are         #
              #              determined to be errors.                                #
              #                                                                      #
              #    Modified: 2004-03-03 Bob Orlando                                  #
              #                 v1.7  * Change set|egrep|awk to just set|egrep.      #
              #                                                                      #
              #----------------------------------------------------------------------#
            
Artificial Intelligence is no match for natural stupidity.
©Copyright Bob Orlando, 1998-2011
All rights reserved.
http://www.OrlandoKuntao.com
E-mail: Bob@OrlandoKuntao.com
Last update: Jan. 26, 2011
by Bob Orlando