"Fossies" - the Fresh Open Source Software Archive

Member "mussh/mussh" (25 Oct 2011, 20815 Bytes) of package /linux/privat/old/mussh-1.0.tgz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Bash source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 #!/bin/bash
    2 #
    3 # $Id: mussh,v 1.12 2006/12/26 21:57:22 doughnut Exp $
    4 MUSSH_VERSION="1.0"
    5 #
    6 # Description:  This script is used to execute the same command(s) on
    7 #               many hosts.
    8 #
    9 # by doughnut
   10 #
   11 
   12 
   13 
   14 
   15 ########################
   16 # INITIALIZE THIS CRAP #
   17 ########################
   18 DEBUG=0
   19 SSH_VERBOSE=""
   20 QUIET=0
   21 FORCE_AGENT=0
   22 UNIQUE_HOSTS=1
   23 PROXY_SSH_ARGS="-o PasswordAuthentication=no"
   24 SSH_ARGS="-o BatchMode=yes"
   25 AGENT_LOADED=0
   26 REMOTE_SHELL='bash'
   27 TEMP_BASE=/tmp
   28 CONCURRENT=1
   29 
   30 ############################
   31 # GOTTA LOVE DOCUMENTATION #
   32 ############################
   33 USAGE="Usage: mussh [OPTIONS] <-h host.. | -H hostfile> [-c cmd] [-C scriptfile]
   34 mussh --help        for full help text"
   35 HELPTEXT="
   36 Send a command or list of commands to multiple hosts.
   37 
   38 OPTIONS:
   39         --help          This text.
   40         -d [n]          Verbose debug.  Prints each action, all hosts
   41                         and commands to be executed to STDERR.  'n' can
   42                         be from 0 to 2.
   43         -v [n]          Ssh debug levels.  Can be from 0 to 3.
   44         -m [n]          Run concurrently on 'n' hosts at a time (asynchronous).
   45                         Use '0' (zero) for infinite. (default if -m)
   46         -q              No output unless necessary. 
   47         -i <identity> [identity ..]
   48                         Load an identity file.  May be used
   49                         more than once.
   50         -o <ssh-args>   Args to pass to ssh with -o option.
   51         -a              Force loading ssh-agent.
   52         -A              Do NOT load ssh-agent.
   53         -b              Print each hosts' output in a block without mingling
   54                         with other hosts' output.
   55         -B              Allow hosts' output to mingle. (default)
   56         -u              Unique.  Eliminate duplicate hosts. (default)
   57         -U              Do NOT make host list unique.
   58         -P              Do NOT fall back to passwords on any host.  This will
   59                         skip hosts where keys fail.
   60     -l <login>      Use 'login' when no other is specified with hostname.
   61     -L <login>      Force use of 'login' name on all hosts.
   62         -s <shell>      Path to shell on remote host. (Default: $REMOTE_SHELL)
   63         -t <secs>       Timeout setting for each session.
   64                         (requires openssh 3.8 or newer)
   65         -V              Print version info and exit.
   66 PROXY ARGS:
   67         -p [user@]<host>
   68                         Host to use as proxy.  (Must have mussh installed)
   69         -po <ssh-args>        Args to pass to ssh on proxy with -o option.
   70 HOST ARGS:
   71         -h [user@]<host> [[user@]<host> ..]
   72                         Add a host to list of hosts.  May be
   73                         used more than once.
   74         -H <file> [file ..]
   75                         Add contents of file(s) to list of hosts.
   76                         Files should have one host per line.  Use
   77                         \"#\" for comments.
   78 COMMAND ARGS:
   79 If neither is specified, commands will be read from standard input.
   80         -c <command>    Add a command or quoted list of commands and
   81                         args to list of commands to be executed on
   82                         each host.  May be used more than once.
   83         -C <file> [file ..]
   84                         Add file contents to list of commands to be
   85                         executed on each host.  May be used more
   86                         than once.
   87 
   88 At least one host is required.  Arguments are in no particular order.
   89 
   90 EXAMPLES:
   91 mussh -H ./linuxhosts -C spfiles/testscript.sh
   92 mussh -c \"cat /etc/hosts\" -h myhost.mydomain.com
   93 
   94 Comments and Bug Reports: doughnut@doughnut.net
   95 "
   96 
   97 ###########################
   98 # FUNCTIONS FOR SSH-AGENT #
   99 ###########################
  100 load_keys() {
  101         [ "$AGENT_LOADED" = "0" -a "$IDENT" = "" ] && return
  102         [ "$DEBUG" -ge 1 ] && echo "DEBUG: Adding Keys" 1>&2
  103         ssh-add $* 1>&2
  104 }
  105 
  106 start_agent() {
  107         [ "$DEBUG" -ge 1 ] && echo "DEBUG: Starting Agent" 1>&2
  108         if [ "$FORCE_AGENT" -ge 1 ] ; then
  109                 # Load it anyway
  110                 [ "$DEBUG" -ge 1 ] && echo "DEBUG: Forcing SSH Agent" 1>&2
  111         elif [ -S "$SSH_AUTH_SOCK" ] ; then
  112                 [ "$DEBUG" -ge 1 ] && echo "DEBUG: SSH Agent already loaded" 1>&2
  113                 return
  114         fi
  115         eval $(ssh-agent -s) >/dev/null
  116         AGENT_LOADED=1
  117 }
  118 stop_agent() {
  119         # get rid of the keys that we added (if any)
  120         if [ "$IDENT" != "" ] ; then
  121                 [ "$DEBUG" -ge 1 ] && echo "DEBUG: Removing keys from agent" 1>&2
  122                 ssh-add -d $IDENT > /dev/null 2>&1
  123         fi
  124         [ "$AGENT_LOADED" = "0" ] && return
  125         [ "$DEBUG" -ge 1 ] && echo "DEBUG: Stopping Agent" 1>&2
  126     eval $(ssh-agent -s -k) >/dev/null
  127 }
  128 
  129 ####################################
  130 # FUNCTIONS FOR REMOTE CONNECTIONS #
  131 ####################################
  132 proxy_connect() {
  133         [ "$DEBUG" -ge 1 ] && echo "DEBUG: PROXY CONNECT $PROXY" 1>&2
  134         echo "$THESCRIPT" \
  135                 | ssh -T $SSH_ARGS $PROXY \
  136                         "mussh -h $HOSTLIST \
  137                                 -C - \
  138                                 -d$DEBUG \
  139                                 $PROXY_SSH_ARGS 2>&1 " \
  140                 | while read SSH_LINE ; do
  141                     if [ "$QUIET" -lt 1 -a "$SSH_LINE" != "" ] ; then
  142                         echo "$SSH_LINE" | sed -e "s/^/$HOST: /"
  143                     fi
  144                 done
  145 }
  146 
  147 ssh_connect() {
  148     echo "$THESCRIPT" \
  149         | ssh -T $SSH_ARGS $HOST "$REMOTE_SHELL" 2>&1 \
  150         | while read SSH_LINE ; do
  151         if [ "$QUIET" -lt 1 -a "$SSH_LINE" != "" ] ; then
  152             echo "$SSH_LINE" | sed -e "s/^/$HOST: /"
  153         fi
  154         done
  155 }
  156 
  157 ##############################
  158 # FUNCTIONS FOR FORKED PROCS #
  159 ##############################
  160 set_hostlist() {
  161 # Create a hostlist file.
  162     [ "$DEBUG" -ge 2 ] && echo "DEBUG: BUILDING HOST LIST FILE $TEMP_DIR/hostlist" 1>&2
  163     rm -f $TEMP_DIR/hostlist || exit 1
  164     for HOST in $HOSTLIST ; do
  165         echo $HOST >> "$TEMP_DIR/hostlist" || exit 1
  166     done
  167 }
  168 
  169 get_next_host() {
  170 # lock file
  171     while [ 1 ] ; do
  172         echo $CHILDNUM >> "$TEMP_DIR/hostlist.lock"
  173         TOP_PID=$(head -1 "$TEMP_DIR/hostlist.lock" 2>/dev/null)
  174         if [ "$TOP_PID" = $CHILDNUM ] ; then
  175             break
  176         fi
  177         [ "$DEBUG" -ge 2 ] && echo "DEBUG[#$CHILDNUM]: hostlist file already locked.  Sleep..." 1>&2
  178         #usleep 1000
  179         sleep 1
  180     done
  181     [ "$DEBUG" -ge 2 ] && echo "DEBUG[#$CHILDNUM]: Locked hostfile." 1>&2
  182 
  183 # get next host
  184     NEXT_HOST=$(head -1 $TEMP_DIR/hostlist)
  185     HOSTFILE_LEN=$(wc -l $TEMP_DIR/hostlist | awk '{print $1}')
  186         if [ -z "$HOSTFILE_LEN" -o "$HOSTFILE_LEN" = 0 ] ; then
  187         rm -f "$TEMP_DIR/hostlist.lock"
  188         return
  189     fi
  190     [ "$DEBUG" -ge 2 ] && echo "DEBUG[#$CHILDNUM]: Next host: $NEXT_HOST" 1>&2
  191 
  192 # re-write file removing new host
  193     rm -f "$TEMP_DIR/hostlist.new"
  194     echo tail -$(( $HOSTFILE_LEN - 1 )) $TEMP_DIR/hostlist > $TEMP_DIR/hostlist.new || exit 1
  195     tail -$(( $HOSTFILE_LEN - 1 )) $TEMP_DIR/hostlist > $TEMP_DIR/hostlist.new || exit 1
  196         rm -f "$TEMP_DIR/hostlist"
  197     mv "$TEMP_DIR/hostlist.new" "$TEMP_DIR/hostlist"
  198 
  199 # unlock file
  200     [ "$DEBUG" -ge 2 ] && echo "DEBUG[#$CHILDNUM]: Removing hostfile lock." 1>&2
  201     rm -f "$TEMP_DIR/hostlist.lock"
  202 
  203 # return hostname
  204     echo $NEXT_HOST
  205 }
  206 
  207 run_child() {
  208     trap "exit 0" SIGHUP
  209     CHILDNUM=$1
  210     [ "$DEBUG" -ge 2 ] && echo "DEBUG: FORKING CHILD #$CHILDNUM of $CONCURRENT (pid $!/$$)" 1>&2
  211     while [ 1 ] ; do
  212 
  213 # issue:  Cannot call get_next_host inside $() or `` because our trap won't be able to kill that.
  214 # solution: avoid subshell here by directing to a file.
  215         rm -f $TEMP_DIR/$CHILDNUM.next_host
  216         get_next_host >$TEMP_DIR/$CHILDNUM.next_host
  217         HOST=$(<$TEMP_DIR/$CHILDNUM.next_host)
  218         if [ -z "$HOST" ] ; then
  219             rm -f "$TEMP_DIR/$CHILDNUM.pid"
  220             break
  221         fi
  222         [ "$DEBUG" -ge 1 ] && echo "DEBUG[#$CHILDNUM]: CONNECT $HOST" 1>&2
  223 
  224         rm -f "$TEMP_DIR/$CHILDNUM.active"
  225         echo "$HOST" > "$TEMP_DIR/$CHILDNUM.active"
  226         if [ -n "$BLOCKING" ] ; then
  227             ssh_connect > $TEMP_DIR/$HOST.out 
  228             cat $TEMP_DIR/$HOST.out 
  229         else
  230             ssh_connect
  231         fi
  232     done
  233     [ "$DEBUG" -ge 2 ] && echo "DEBUG: CHILD #$CHILDNUM done" 1>&2
  234     rm -f "$TEMP_DIR/$CHILDNUM.pid" "$TEMP_DIR/$CHILDNUM.active"
  235 }
  236 
  237 
  238 ###########################
  239 # FUNCTIONS FOR TEMP DIRS #
  240 ###########################
  241 create_temp() {
  242     MKTEMP=$(which mktemp 2>/dev/null)
  243     if [ -x "$MKTEMP" ] ; then
  244             [ "$DEBUG" -ge 2 ] && echo "DEBUG: using mktemp ($MKTEMP)." 1>&2
  245         TEMP_DIR=$(mktemp -d $TEMP_BASE/$(basename $0).XXXXXX) || exit 1
  246     else
  247             [ "$DEBUG" -ge 2 ] && echo "DEBUG: can't find mktemp... using alternate." 1>&2
  248             TEMP_DIR="$TEMP_BASE/$(basename $0).$(date +%s)"
  249             [ "$DEBUG" -ge 2 ] && echo "DEBUG: Creating temp dir ($TEMP_DIR)." 1>&2
  250         if [ -e "$TEMP_DIR" ] ; then
  251             echo "$0: Temp dir \"$TEMP_DIR\" already exists!" 1>&2
  252             exit 1
  253         fi
  254         mkdir -m 700 $TEMP_DIR
  255     fi
  256 }
  257 
  258 destroy_temp() {
  259     if [ -d "$TEMP_DIR" ] ; then
  260             [ "$DEBUG" -ge 2 ] && echo "DEBUG: Removing temp dir ($TEMP_DIR)." 1>&2
  261         rm -rf "$TEMP_DIR" 2>/dev/null
  262     fi
  263 }
  264 
  265 
  266 
  267 ########################################
  268 # REMEMBER TO CLEAN UP BEFORE WE PANIC #
  269 ########################################
  270 shutdown() {
  271     [ "$DEBUG" -ge 1 ] && echo "DEBUG: shutting down children." 1>&2
  272     CPIDS=$(cat $TEMP_DIR/*.pid 2>/dev/null)
  273     for CPID in $CPIDS ; do 
  274         [ "$DEBUG" -ge 2 ] && echo "DEBUG: Killing pid: $CPID" 1>&2
  275         kill -HUP $CPID
  276     done
  277     [ "$DEBUG" -ge 2 ] && echo "DEBUG: shutting down ssh-agent" 1>&2
  278     stop_agent
  279     [ "$DEBUG" -ge 2 ] && echo "DEBUG: removing temp dir" 1>&2
  280     destroy_temp
  281     [ "$DEBUG" -ge 2 ] && echo "DEBUG: done shutting down." 1>&2
  282         exit 1
  283 }
  284 
  285 spew_hostlist() {
  286     echo "HOSTS RUNNING:"  1>&2
  287     cat $TEMP_DIR/*.active 2>/dev/null | sed 's/^/    /'  1>&2
  288     echo "HOSTS REMAINING:"  1>&2
  289     cat $TEMP_DIR/hostlist 2>/dev/null | sed 's/^/    /'  1>&2
  290     return
  291 }
  292 
  293 trap shutdown SIGINT
  294 trap shutdown SIGTERM
  295 trap spew_hostlist SIGQUIT
  296 trap "exit 0" SIGHUP
  297 
  298 #############################
  299 # PARSE THE COMMAND OPTIONS #
  300 #############################
  301 while [ "$1" != "" ]; do
  302         case "$1" in
  303         ###########
  304         # OPTIONS #
  305         ###########
  306           -A)
  307                 NO_AGENT=1
  308                 shift
  309                 ;;
  310           -a)
  311                 FORCE_AGENT=1
  312                 shift
  313                 ;;
  314           -b)
  315                 BLOCKING=1
  316                 shift
  317                 ;;
  318           -B)
  319                 unset BLOCKING
  320                 shift
  321                 ;;
  322           -q)
  323                 QUIET=1
  324                 DEBUG=0
  325                 SSH_VERBOSE="-q"
  326                 shift
  327                 ;;
  328           -o)
  329                 SSH_ARGS="$SSH_ARGS -o $2"
  330                 shift 2
  331                 ;;
  332           -u)
  333                 UNIQUE_HOSTS=1
  334                 shift
  335                 ;;
  336           -U)
  337                 UNIQUE_HOSTS=0
  338                 shift
  339                 ;;
  340           -l)
  341                 DEFAULT_LOGIN=$2
  342                 shift 2
  343                 ;;
  344           -L)
  345                 FORCE_LOGIN=$2
  346                 shift 2
  347                 ;;
  348           -s)
  349                 REMOTE_SHELL=$2
  350                 shift 2
  351                 ;;
  352           -t*)
  353                 SSH_TIMEOUT="${1#-t}"
  354                 if [ -z "$SSH_TIMEOUT" -a -n "$2" -a "${2#-?*}" = "$2" ] ; then 
  355                         SSH_TIMEOUT=$2 
  356                         shift 
  357                 fi 
  358                 if [ "${SSH_TIMEOUT//[^0-9]/}" != "$SSH_TIMEOUT" -o -z "$SSH_TIMEOUT" ] ; then 
  359                         echo "mussh: Argument should be numeric: -t $SSH_TIMEOUT" 1>&2 
  360                         exit 1 
  361                 fi 
  362                 shift 
  363                 SSH_ARGS="$SSH_ARGS -o ConnectTimeout=$SSH_TIMEOUT"
  364                 ;;
  365           -m*)  # -m0 .. -m999
  366                 CONCURRENT="${1#-m}"
  367                 if [ -z "$CONCURRENT" -a -n "$2" -a "${2#-?*}" = "$2" ] ; then 
  368                         CONCURRENT=$2 
  369                         shift 
  370                 elif [ -z "$CONCURRENT" ] ; then 
  371                         CONCURRENT=0 
  372                 fi 
  373                 if [ "${CONCURRENT//[^0-9]/}" != "$CONCURRENT" ] ; then 
  374                         echo "mussh: Argument should be numeric: -m $CONCURRENT" 1>&2 
  375                         exit 1 
  376                 fi 
  377                 shift
  378                 ;;
  379           -d*)
  380                 DEBUG="${1#-d}"
  381                 if [ -z "$DEBUG" -a -n "$2" -a "${2#-?*}" = "$2" ] ; then
  382                         DEBUG=$2
  383                         shift
  384                 elif [ -z "$DEBUG" ] ; then
  385                         DEBUG=1
  386                 fi
  387                 if [ "${DEBUG//[^0-9]/}" != "$DEBUG" ] ; then
  388                         echo "mussh: Argument should be numeric: -d $DEBUG" 1>&2
  389                         exit 1
  390                 fi
  391                 shift
  392                 ;;
  393           -v*)
  394                 TMP_ARG="${1#-v}"
  395                 if [ -z "$TMP_ARG" -a -n "$2" -a "${2#-?*}" = "$2" ] ; then
  396                         TMP_ARG=$2
  397                         shift
  398                 elif [ -z "$TMP_ARG" ] ; then
  399                         TMP_ARG=1
  400                 fi
  401                 if [ "${TMP_ARG//[^0-9]/}" != "$TMP_ARG" ] ; then
  402                         echo "mussh: Argument should be numeric: -v $TMP_ARG" 1>&2
  403                         exit 1
  404                 elif [ "${TMP_ARG//[^0-3]/}" != "$TMP_ARG" ] ; then
  405                         echo "mussh: Argument should be between 0 and 3: -v $TMP_ARG" 1>&2
  406                         exit 1
  407                 fi
  408                 SSH_VERBOSE="-v"
  409                 [ "$TMP_ARG" -ge 2 ] && SSH_VERBOSE="$SSH_VERBOSE -v"
  410                 [ "$TMP_ARG" -ge 3 ] && SSH_VERBOSE="$SSH_VERBOSE -v"
  411                 [ "$TMP_ARG" -eq 0 ] && SSH_VERBOSE="-q"
  412                 shift
  413                 ;;
  414           -V)
  415                 echo "Version: $MUSSH_VERSION"
  416                 exit
  417                 ;;
  418           -P)
  419                 SSH_ARGS="$SSH_ARGS -o PasswordAuthentication=no"
  420                 shift
  421                 ;;
  422           --help)
  423                 # print help text
  424                 echo "$USAGE"
  425                 echo "$HELPTEXT"
  426                 exit 
  427                 ;;
  428           -i)
  429                 # Load the identity file in ssh-agent 
  430                 while [ "$2" != "" -a "${2#-}" = "$2" ] ; do
  431                         IDENT="$IDENT $2"
  432                         shift
  433                 done
  434                 shift
  435                 ;;
  436         ##############
  437         # PROXY ARGS #
  438         ##############
  439           -p)
  440                 PROXY=$2
  441                 SSH_ARGS="$SSH_ARGS -o ForwardAgent=yes"
  442                 shift 2
  443                 ;;
  444           -po)
  445                 PROXY_SSH_ARGS="$PROXY_SSH_ARGS -o $2"
  446                 shift 2
  447                 ;;
  448         #############
  449         # HOST ARGS #
  450         #############
  451           -h)
  452                 while [ "$2" != "" -a "${2#-?*}" = "$2" ] ; do
  453                           HOSTLIST="$2
  454 $HOSTLIST"
  455                         shift
  456                 done
  457                 shift 
  458                 ;;
  459           -H)
  460                 while [ "$2" != "" -a "${2#-?*}" = "$2" ] ; do
  461                         HOSTFILE="$2"
  462                         if [ ! -e "$HOSTFILE" -a "$HOSTFILE" != "-" ] ; then
  463                                 echo "mussh: Host file '$HOSTFILE' does not exist!" 1>&2
  464                                 exit 1
  465                         fi
  466                         HOSTLIST="$(cat $HOSTFILE | sed -e 's/#.*//' | egrep -v "^ *$" )
  467 $HOSTLIST"
  468                         shift
  469                 done
  470                 shift 
  471                 ;;
  472           -n)
  473         # I've left this undocumented for lack of testing.  Volunteers?
  474                 NETGROUP=$2
  475         NETGROUP_LIST="$(getent netgroup $NETGROUP | xargs -n 1 echo | sed -n '/^(.*,$/s/[,(]//gp')"
  476         if [ -z "$NETGROUP_LIST" ] ; then
  477             echo "mussh: Failed to get netgroup: $NETGROUP" 2>&1
  478             exit 1
  479         fi
  480                 HOSTLIST="$NETGROUP_LIST
  481 $HOSTLIST"
  482                 shift 2
  483                 ;;
  484         ################
  485         # COMMAND ARGS #
  486         ################
  487           -c)
  488                 THESCRIPT="$THESCRIPT
  489 $2"
  490                 #set "" ; shift
  491                 shift 2
  492                 ;;
  493           -C)
  494                 while [ "$2" != "" -a "${2#-?*}" = "$2" ] ; do
  495                         SCRIPTFILE="$2"
  496                           if [ ! -e "$SCRIPTFILE" -a "$SCRIPTFILE" != "-" ] ; then
  497                                 echo "mussh: Script File '$SCRIPTFILE' does not exist!" 1>&2
  498                                 exit 1
  499                         fi
  500                         THESCRIPT="$THESCRIPT
  501 $(cat $SCRIPTFILE )"
  502                         shift
  503                 done
  504                 shift
  505                 ;;
  506           *)
  507                 echo "mussh: invalid command - \"$1\"" 1>&2
  508                 echo "$USAGE" 1>&2
  509                 exit 1
  510                 ;;
  511         esac
  512 done
  513 
  514 #####################
  515 # CLEAN UP HOSTLIST #
  516 #####################
  517 HOSTLIST=$(echo "$HOSTLIST" | sed -e 's/#.*//' | egrep -v "^ *$" )
  518 
  519 if [ "$FORCE_LOGIN" != "" ] ; then
  520     [ "$DEBUG" -ge 1 ] && echo "DEBUG: FORCE_LOGIN: $FORCE_LOGIN" 1>&2
  521     HOSTLIST=$(echo "$HOSTLIST" | sed -e "s/^\(.*@\)\{0,1\}\([^@]*\)\$/$FORCE_LOGIN@\2/")
  522 elif [ "$DEFAULT_LOGIN" != "" ] ; then
  523     [ "$DEBUG" -ge 1 ] && echo "DEBUG: DEFAULT_LOGIN: $DEFAULT_LOGIN" 1>&2
  524     HOSTLIST=$(echo "$HOSTLIST" | sed -e "s/^\([^@]*\)\$/$DEFAULT_LOGIN@\1/")
  525 fi
  526 [ $UNIQUE_HOSTS -ge 1 ] && HOSTLIST=$(echo "$HOSTLIST" | sort -uf )
  527 
  528 
  529 ################
  530 # CHECK SYNTAX #
  531 ################
  532 if [ "$HOSTLIST" = "" ] ; then
  533         echo "mussh: ERROR: You must supply at least one host!" 1>&2
  534         echo "$USAGE" 1>&2
  535         exit 1
  536 fi
  537 
  538 ###################################
  539 # DEFAULT TO STDIN IF NO COMMANDS #
  540 ###################################
  541 if [ "$THESCRIPT" = "" ] ; then
  542         echo "Enter your script here.  End with single \".\" or EOF." 1>&2
  543         while read THISLINE
  544         do 
  545                 if [ "$THISLINE" = "." ] ; then
  546                         break
  547                 fi
  548         THESCRIPT="$THESCRIPT
  549 $THISLINE"
  550         done
  551 fi
  552 
  553 ###################################################
  554 # INFINITE CONCURRENCY IS REALLY JUST ALL AT ONCE #
  555 ###################################################
  556 COUNT_HOSTS=$(echo "$HOSTLIST" | wc -w)
  557 if [ $CONCURRENT -eq 0 ] || [ $CONCURRENT -gt $COUNT_HOSTS ] ; then
  558        CONCURRENT=$COUNT_HOSTS
  559        [ "$DEBUG" -ge 1 ] && echo "DEBUG: setting concurrency (-m) to $CONCURRENT (all hosts)" 1>&2
  560 fi
  561 [ "$DEBUG" -ge 1 ] && echo "DEBUG: Concurrency: $CONCURRENT" 1>&2
  562 
  563 #################################
  564 # ADD SSH VERBOSITY TO SSH ARGS #
  565 #################################
  566 if [ "$SSH_VERBOSE" ] ; then
  567     SSH_ARGS="$SSH_VERBOSE $SSH_ARGS"
  568 fi
  569 
  570 ############################
  571 # PRINT VERBOSE DEBUG INFO #
  572 ############################
  573 if [ "$DEBUG" -ge 1 ] ; then
  574     echo "DEBUG: DEBUG LEVEL: $DEBUG" 1>&2
  575     echo "DEBUG: SSH DEBUG LEVEL: $SSH_VERBOSE" 1>&2
  576 fi
  577 if [ "$DEBUG" -ge 2 ] ; then
  578         echo "DEBUG: HOSTLIST: " $HOSTLIST 1>&2
  579         echo "DEBUG: THE SCRIPT: $THESCRIPT" 1>&2
  580         echo "DEBUG: SSH ARGS: $SSH_ARGS" 1>&2
  581 fi
  582 
  583 ############################
  584 # LOAD SSH-AGENT WITH KEYS #
  585 ############################
  586 if [ "$NO_AGENT" = "1" ] ; then
  587         [ "$DEBUG" -ge 1 ] && echo "DEBUG: Not using ssh-agent" 1>&2
  588 elif [ "$IDENT" != "" ] ; then
  589         start_agent
  590         load_keys "$IDENT"
  591 elif [ ! -f "$HOME/.ssh/identity" -a ! -f "$HOME/.ssh/id_dsa" ]  ; then
  592         [ "$DEBUG" -ge 1 ] && echo "DEBUG: No identity file found.  Skipping agent."
  593 else
  594         start_agent 
  595         load_keys "$IDENT"
  596 fi
  597 #echo
  598 
  599 ###################
  600 # CREATE TEMP DIR #
  601 ###################
  602 create_temp
  603 
  604 ########################
  605 # EXECUTE THE COMMANDS #
  606 ########################
  607 if [ -z "$CONCURRENT" ] ; then
  608 CONCURRENT=1
  609 fi
  610 
  611 if [ "$PROXY" != "" ] ; then
  612         proxy_connect
  613 
  614 # Don't background process when we're only doing one at a time
  615 #elif [ -z "$CONCURRENT" -o "$CONCURRENT" = 1 ] ; then
  616 #        set $HOSTLIST
  617 #        while [ "$1" != "" ] ; do
  618 #                HOST="$1"
  619 #                [ "$DEBUG" -ge 1 ] && echo "DEBUG: CONNECT $HOST" 1>&2
  620 #       shift
  621 #       ssh_connect 
  622 #   done
  623 else
  624 # Fork $CONCURRENT children
  625     set_hostlist
  626     CHILDNUM=1
  627     while [ $CHILDNUM -le $CONCURRENT ] ; do 
  628         run_child $CHILDNUM &
  629         [ "$DEBUG" -ge 2 ] && echo "DEBUG: FORKED CHILD #$CHILDNUM with pid $!" 1>&2
  630         rm -f "$TEMP_DIR/$CHILDNUM.pid"
  631         echo $! > "$TEMP_DIR/$CHILDNUM.pid"
  632         (( CHILDNUM++ ))
  633         done
  634     wait
  635 # since trap causes waits to stop waiting with a return value >128
  636 # We need to check that we really meant to stop waiting.
  637     RETVAL=$?
  638     while [ "$RETVAL" -gt 128 ] ; do
  639         [ "$DEBUG" -ge 2 ] && echo "DEBUG: wait returned with a value of $RETVAL" 1>&2
  640         DO_WAIT=0
  641         for CPID in $(cat $TEMP_DIR/*.pid 2>/dev/null) ; do
  642             if [ -d "/proc/$CPID" ] ; then 
  643                 DO_WAIT=1
  644                 [ "$DEBUG" -ge 2 ] && echo "DEBUG: $CPID is still running." 1>&2
  645             fi
  646         done
  647         [ "$DO_WAIT" = 0 ] && break
  648         wait
  649     done
  650 fi
  651 
  652 ############
  653 # CLEAN UP #
  654 ############
  655 destroy_temp
  656 stop_agent
  657