"Fossies" - the Fresh Open Source Software Archive

Member "mussh/mussh.bak" (25 Oct 2011, 20807 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 # INITAILIZE THIS CRAP #
   17 ########################
   18 DEBUG=0
   19 SSH_VERBOSE="-q"
   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 infinate. (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     -n <netgroup>   Get list of hosts from netgroup
   79 COMMAND ARGS:
   80 If neither is specified, commands will be read from standard input.
   81         -c <command>    Add a command or quoted list of commands and
   82                         args to list of commands to be executed on
   83                         each host.  May be used more than once.
   84         -C <file> [file ..]
   85                         Add file contents to list of commands to be
   86                         executed on each host.  May be used more
   87                         than once.
   88 
   89 At least one host is required.  Arguments are in no particular order.
   90 
   91 EXAMPLES:
   92 mussh -H ./linuxhosts -C spfiles/testscript.sh
   93 mussh -c \"cat /etc/hosts\" -h myhost.mydomain.com
   94 
   95 Comments and Bug Reports: doughnut@doughnut.net
   96 "
   97 
   98 ###########################
   99 # FUNCTIONS FOR SSH-AGENT #
  100 ###########################
  101 load_keys() {
  102         [ "$AGENT_LOADED" = "0" -a "$IDENT" = "" ] && return
  103         [ "$DEBUG" -ge 1 ] && echo "DEBUG: Adding Keys" 1>&2
  104         ssh-add $* 1>&2
  105 }
  106 
  107 start_agent() {
  108         [ "$DEBUG" -ge 1 ] && echo "DEBUG: Starting Agent" 1>&2
  109         if [ "$FORCE_AGENT" -ge 1 ] ; then
  110                 # Load it anyway
  111                 [ "$DEBUG" -ge 1 ] && echo "DEBUG: Forcing SSH Agent" 1>&2
  112         elif [ -S "$SSH_AUTH_SOCK" ] ; then
  113                 [ "$DEBUG" -ge 1 ] && echo "DEBUG: SSH Agent already loaded" 1>&2
  114                 return
  115         fi
  116         eval $(ssh-agent -s) >/dev/null
  117         AGENT_LOADED=1
  118 }
  119 stop_agent() {
  120         # get rid of the keys that we added (if any)
  121         if [ "$IDENT" != "" ] ; then
  122                 [ "$DEBUG" -ge 1 ] && echo "DEBUG: Removing keys from agent" 1>&2
  123                 ssh-add -d $IDENT > /dev/null 2>&1
  124         fi
  125         [ "$AGENT_LOADED" = "0" ] && return
  126         [ "$DEBUG" -ge 1 ] && echo "DEBUG: Stopping Agent" 1>&2
  127     eval $(ssh-agent -s -k) >/dev/null
  128 }
  129 
  130 ####################################
  131 # FUNCTIONS FOR REMOTE CONNECTIONS #
  132 ####################################
  133 proxy_connect() {
  134         [ "$DEBUG" -ge 1 ] && echo "DEBUG: PROXY CONNECT $PROXY" 1>&2
  135         echo "$THESCRIPT" \
  136                 | ssh -T $SSH_ARGS $PROXY \
  137                         "mussh -h $HOSTLIST \
  138                                 -C - \
  139                                 -d$DEBUG \
  140                                 $PROXY_SSH_ARGS 2>&1 " \
  141                 | while read SSH_LINE ; do
  142                     if [ "$QUIET" -lt 1 -a "$SSH_LINE" != "" ] ; then
  143                         echo "$SSH_LINE" | sed -e "s/^/$HOST: /"
  144                     fi
  145                 done
  146 }
  147 
  148 ssh_connect() {
  149     echo "$THESCRIPT" \
  150         | ssh -T $SSH_ARGS $HOST "$REMOTE_SHELL" 2>&1 \
  151         | while read SSH_LINE ; do
  152         if [ "$QUIET" -lt 1 -a "$SSH_LINE" != "" ] ; then
  153             echo "$SSH_LINE" | sed -e "s/^/$HOST: /"
  154         fi
  155         done
  156 }
  157 
  158 ##############################
  159 # FUNCTIONS FOR FORKED PROCS #
  160 ##############################
  161 set_hostlist() {
  162 # Create a hostlist file.
  163     [ "$DEBUG" -ge 2 ] && echo "DEBUG: BUILDING HOST LIST FILE $TEMP_DIR/hostlist" 1>&2
  164     rm -f $TEMP_DIR/hostlist || exit 1
  165     for HOST in $HOSTLIST ; do
  166         echo $HOST >> "$TEMP_DIR/hostlist" || exit 1
  167     done
  168 }
  169 
  170 get_next_host() {
  171 # lock file
  172     while [ 1 ] ; do
  173         echo $CHILDNUM >> "$TEMP_DIR/hostlist.lock"
  174         TOP_PID=$(head -1 "$TEMP_DIR/hostlist.lock" 2>/dev/null)
  175         if [ "$TOP_PID" = $CHILDNUM ] ; then
  176             break
  177         fi
  178         [ "$DEBUG" -ge 2 ] && echo "DEBUG[#$CHILDNUM]: hostlist file already locked.  Sleep..." 1>&2
  179         #usleep 1000
  180         sleep 1
  181     done
  182     [ "$DEBUG" -ge 2 ] && echo "DEBUG[#$CHILDNUM]: Locked hostfile." 1>&2
  183 
  184 # get next host
  185     NEXT_HOST=$(head -1 $TEMP_DIR/hostlist)
  186     HOSTFILE_LEN=$(wc -l $TEMP_DIR/hostlist | awk '{print $1}')
  187         if [ -z "$HOSTFILE_LEN" -o "$HOSTFILE_LEN" = 0 ] ; then
  188         rm -f "$TEMP_DIR/hostlist.lock"
  189         return
  190     fi
  191     [ "$DEBUG" -ge 2 ] && echo "DEBUG[#$CHILDNUM]: Next host: $NEXT_HOST" 1>&2
  192 
  193 # re-write file removing new host
  194     rm -f "$TEMP_DIR/hostlist.new"
  195     echo tail -$(expr $HOSTFILE_LEN - 1) $TEMP_DIR/hostlist > $TEMP_DIR/hostlist.new || exit 1
  196     tail -$(expr $HOSTFILE_LEN - 1) $TEMP_DIR/hostlist > $TEMP_DIR/hostlist.new || exit 1
  197         rm -f "$TEMP_DIR/hostlist"
  198     mv "$TEMP_DIR/hostlist.new" "$TEMP_DIR/hostlist"
  199 
  200 # unlock file
  201     [ "$DEBUG" -ge 2 ] && echo "DEBUG[#$CHILDNUM]: Removing hostfile lock." 1>&2
  202     rm -f "$TEMP_DIR/hostlist.lock"
  203 
  204 # return hostname
  205     echo $NEXT_HOST
  206 }
  207 
  208 run_child() {
  209     trap "exit 0" SIGHUP
  210     CHILDNUM=$1
  211     [ "$DEBUG" -ge 2 ] && echo "DEBUG: FORKING CHILD #$CHILDNUM of $CONCURRENT (pid $!/$$)" 1>&2
  212     while [ 1 ] ; do
  213 
  214 # issue:  Cannot call get_next_host inside $() or `` because our trap won't be able to kill that.
  215 # solution: avoid subshell here by directing to a file.
  216         rm -f $TEMP_DIR/$CHILDNUM.next_host
  217         get_next_host >$TEMP_DIR/$CHILDNUM.next_host
  218         HOST=$(<$TEMP_DIR/$CHILDNUM.next_host)
  219         if [ -z "$HOST" ] ; then
  220             rm -f "$TEMP_DIR/$CHILDNUM.pid"
  221             break
  222         fi
  223         [ "$DEBUG" -ge 1 ] && echo "DEBUG[#$CHILDNUM]: CONNECT $HOST" 1>&2
  224 
  225         rm -f "$TEMP_DIR/$CHILDNUM.active"
  226         echo "$HOST" > "$TEMP_DIR/$CHILDNUM.active"
  227         if [ -n "$BLOCKING" ] ; then
  228             ssh_connect > $TEMP_DIR/$HOST.out 
  229             cat $TEMP_DIR/$HOST.out 
  230         else
  231             ssh_connect
  232         fi
  233     done
  234     [ "$DEBUG" -ge 2 ] && echo "DEBUG: CHILD #$CHILDNUM done" 1>&2
  235     rm -f "$TEMP_DIR/$CHILDNUM.pid" "$TEMP_DIR/$CHILDNUM.active"
  236 }
  237 
  238 
  239 ###########################
  240 # FUNCTIONS FOR TEMP DIRS #
  241 ###########################
  242 create_temp() {
  243     MKTEMP=$(which mktemp 2>/dev/null)
  244     if [ -x "$MKTEMP" ] ; then
  245             [ "$DEBUG" -ge 2 ] && echo "DEBUG: using mktemp ($MKTEMP)." 1>&2
  246         TEMP_DIR=$(mktemp -d $TEMP_BASE/$(basename $0).XXXXXX) || exit 1
  247     else
  248             [ "$DEBUG" -ge 2 ] && echo "DEBUG: can't find mktemp... using alternate." 1>&2
  249             TEMP_DIR="$TEMP_BASE/$(basename $0).$(date +%s)"
  250             [ "$DEBUG" -ge 2 ] && echo "DEBUG: Creating temp dir ($TEMP_DIR)." 1>&2
  251         if [ -e "$TEMP_DIR" ] ; then
  252             echo "$0: Temp dir \"$TEMP_DIR\" already exists!" 1>&2
  253             exit 1
  254         fi
  255         mkdir -m 700 $TEMP_DIR
  256     fi
  257 }
  258 
  259 destroy_temp() {
  260     if [ -d "$TEMP_DIR" ] ; then
  261             [ "$DEBUG" -ge 2 ] && echo "DEBUG: Removing temp dir ($TEMP_DIR)." 1>&2
  262         rm -rf "$TEMP_DIR" 2>/dev/null
  263     fi
  264 }
  265 
  266 
  267 
  268 ########################################
  269 # REMEMBER TO CLEAN UP BEFORE WE PANIC #
  270 ########################################
  271 shutdown() {
  272     [ "$DEBUG" -ge 1 ] && echo "DEBUG: shutting down children." 1>&2
  273     CPIDS=$(cat $TEMP_DIR/*.pid 2>/dev/null)
  274     for CPID in $CPIDS ; do 
  275         [ "$DEBUG" -ge 2 ] && echo "DEBUG: Killing pid: $CPID" 1>&2
  276         kill -HUP $CPID
  277     done
  278     [ "$DEBUG" -ge 2 ] && echo "DEBUG: shutting down ssh-agent" 1>&2
  279     stop_agent
  280     [ "$DEBUG" -ge 2 ] && echo "DEBUG: removing temp dir" 1>&2
  281     destroy_temp
  282     [ "$DEBUG" -ge 2 ] && echo "DEBUG: done shutting down." 1>&2
  283         exit 1
  284 }
  285 
  286 spew_hostlist() {
  287     echo "HOSTS RUNNING:"  1>&2
  288     cat $TEMP_DIR/*.active 2>/dev/null | sed 's/^/    /'  1>&2
  289     echo "HOSTS REMAINING:"  1>&2
  290     cat $TEMP_DIR/hostlist 2>/dev/null | sed 's/^/    /'  1>&2
  291     return
  292 }
  293 
  294 trap shutdown SIGINT
  295 trap shutdown SIGTERM
  296 trap spew_hostlist SIGQUIT
  297 trap "exit 0" SIGHUP
  298 
  299 #############################
  300 # PARSE THE COMMAND OPTIONS #
  301 #############################
  302 while [ "$1" != "" ]; do
  303         case "$1" in
  304         ###########
  305         # OPTIONS #
  306         ###########
  307           -A)
  308                 NO_AGENT=1
  309                 shift
  310                 ;;
  311           -a)
  312                 FORCE_AGENT=1
  313                 shift
  314                 ;;
  315           -b)
  316                 BLOCKING=1
  317                 shift
  318                 ;;
  319           -B)
  320                 unset BLOCKING
  321                 shift
  322                 ;;
  323           -q)
  324                 QUIET=1
  325                 DEBUG=0
  326                 SSH_VERBOSE="-q"
  327                 shift
  328                 ;;
  329           -o)
  330                 SSH_ARGS="$SSH_ARGS -o $2"
  331                 shift 2
  332                 ;;
  333           -u)
  334                 UNIQUE_HOSTS=1
  335                 shift
  336                 ;;
  337           -U)
  338                 UNIQUE_HOSTS=0
  339                 shift
  340                 ;;
  341           -l)
  342                 DEFAULT_LOGIN=$2
  343                 shift 2
  344                 ;;
  345           -L)
  346                 FORCE_LOGIN=$2
  347                 shift 2
  348                 ;;
  349           -s)
  350                 REMOTE_SHELL=$2
  351                 shift 2
  352                 ;;
  353           -t*)
  354                 SSH_TIMEOUT="${1#-t}"
  355                 if [ -z "$SSH_TIMEOUT" -a -n "$2" -a "${2#-?*}" = "$2" ] ; then 
  356                         SSH_TIMEOUT=$2 
  357                         shift 
  358                 fi 
  359                 if [ "${SSH_TIMEOUT//[^0-9]/}" != "$SSH_TIMEOUT" -o -z "$SSH_TIMEOUT" ] ; then 
  360                         echo "mussh: Arguement should be numeric: -t $SSH_TIMEOUT" 1>&2 
  361                         exit 1 
  362                 fi 
  363                 shift 
  364                 SSH_ARGS="$SSH_ARGS -o ConnectTimeout=$SSH_TIMEOUT"
  365                 ;;
  366           -m*)  # -m0 .. -m999
  367                 CONCURRENT="${1#-m}"
  368                 if [ -z "$CONCURRENT" -a -n "$2" -a "${2#-?*}" = "$2" ] ; then 
  369                         CONCURRENT=$2 
  370                         shift 
  371                 elif [ -z "$CONCURRENT" ] ; then 
  372                         CONCURRENT=0 
  373                 fi 
  374                 if [ "${CONCURRENT//[^0-9]/}" != "$CONCURRENT" ] ; then 
  375                         echo "mussh: Arguement should be numeric: -m $CONCURRENT" 1>&2 
  376                         exit 1 
  377                 fi 
  378                 shift
  379                 ;;
  380           -d*)
  381                 DEBUG="${1#-d}"
  382                 if [ -z "$DEBUG" -a -n "$2" -a "${2#-?*}" = "$2" ] ; then
  383                         DEBUG=$2
  384                         shift
  385                 elif [ -z "$DEBUG" ] ; then
  386                         DEBUG=1
  387                 fi
  388                 if [ "${DEBUG//[^0-9]/}" != "$DEBUG" ] ; then
  389                         echo "mussh: Arguement should be numeric: -d $DEBUG" 1>&2
  390                         exit 1
  391                 fi
  392                 shift
  393                 ;;
  394           -v*)
  395                 TMP_ARG="${1#-v}"
  396                 if [ -z "$TMP_ARG" -a -n "$2" -a "${2#-?*}" = "$2" ] ; then
  397                         TMP_ARG=$2
  398                         shift
  399                 elif [ -z "$TMP_ARG" ] ; then
  400                         TMP_ARG=1
  401                 fi
  402                 if [ "${TMP_ARG//[^0-9]/}" != "$TMP_ARG" ] ; then
  403                         echo "mussh: Arguement should be numeric: -v $TMP_ARG" 1>&2
  404                         exit 1
  405                 elif [ "${TMP_ARG//[^0-3]/}" != "$TMP_ARG" ] ; then
  406                         echo "mussh: Arguement should be between 0 and 3: -v $TMP_ARG" 1>&2
  407                         exit 1
  408                 fi
  409                 SSH_VERBOSE="-v"
  410                 [ "$TMP_ARG" -ge 2 ] && SSH_VERBOSE="$SSH_VERBOSE -v"
  411                 [ "$TMP_ARG" -ge 3 ] && SSH_VERBOSE="$SSH_VERBOSE -v"
  412                 [ "$TMP_ARG" -eq 0 ] && SSH_VERBOSE="-q"
  413                 shift
  414                 ;;
  415           -V)
  416                 echo "Version: $MUSSH_VERSION"
  417                 exit
  418                 ;;
  419           -P)
  420                 SSH_ARGS="$SSH_ARGS -o PasswordAuthentication=no"
  421                 shift
  422                 ;;
  423           --help)
  424                 # print help text
  425                 echo "$USAGE"
  426                 echo "$HELPTEXT"
  427                 exit 
  428                 ;;
  429           -i)
  430                 # Load the identity file in ssh-agent 
  431                 while [ "$2" != "" -a "${2#-}" = "$2" ] ; do
  432                         IDENT="$IDENT $2"
  433                         shift
  434                 done
  435                 shift
  436                 ;;
  437         ##############
  438         # PROXY ARGS #
  439         ##############
  440           -p)
  441                 PROXY=$2
  442                 SSH_ARGS="$SSH_ARGS -o ForwardAgent=yes"
  443                 shift 2
  444                 ;;
  445           -po)
  446                 PROXY_SSH_ARGS="$PROXY_SSH_ARGS -o $2"
  447                 shift 2
  448                 ;;
  449         #############
  450         # HOST ARGS #
  451         #############
  452           -h)
  453                 while [ "$2" != "" -a "${2#-?*}" = "$2" ] ; do
  454                           HOSTLIST="$2
  455 $HOSTLIST"
  456                         shift
  457                 done
  458                 shift 
  459                 ;;
  460           -H)
  461                 while [ "$2" != "" -a "${2#-?*}" = "$2" ] ; do
  462                         HOSTFILE="$2"
  463                         if [ ! -e "$HOSTFILE" -a "$HOSTFILE" != "-" ] ; then
  464                                 echo "mussh: Host file '$HOSTFILE' does not exist!" 1>&2
  465                                 exit 1
  466                         fi
  467                         HOSTLIST="$(cat $HOSTFILE | sed -e 's/#.*//' | egrep -v "^ *$" )
  468 $HOSTLIST"
  469                         shift
  470                 done
  471                 shift 
  472                 ;;
  473           -n)
  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 # INFINATE 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