mussh/0000775000076400007640000000000011651643632012512 5ustar doughnutdoughnutmussh/INSTALL0000664000076400007640000000010611234163024013525 0ustar doughnutdoughnutMake sure the file is executable and place it anywhere in your path. mussh/test/0000775000076400007640000000000011651634561013472 5ustar doughnutdoughnutmussh/test/12_async.sh0000775000076400007640000000076011234163024015437 0ustar doughnutdoughnut#!/bin/bash echo " ###################################################################### # TEST: hostfile # $Id: 12_async.sh,v 1.2 2005-09-12 22:22:15 doughnut Exp $ ###################################################################### " CMD="mussh -d2 -m -U -H $(dirname $0)/testhosts.list -c 'date'" echo "COMMAND: $CMD" $CMD RETVAL=$? if [ "$RETVAL" -eq 0 ] ; then echo "PASSED" else echo "FAILED (returned $RETVAL)" echo "Hit any key to continue" read fi exit $RETVAL mussh/test/11_hostfile.sh0000775000076400007640000000074711651634452016156 0ustar doughnutdoughnut#!/bin/bash echo " ###################################################################### # TEST: hostfile # $Id: 11_hostfile.sh,v 1.2 2005-09-12 22:22:15 doughnut Exp $ ###################################################################### " CMD="mussh -U -t 10 -d -H $(dirname $0)/testhosts.list -c 'date'" echo "COMMAND: $CMD" $CMD RETVAL=$? if [ "$RETVAL" -eq 0 ] ; then echo "PASSED" else echo "FAILED (returned $RETVAL)" echo "Hit any key to continue" read fi exit $RETVAL mussh/test/10_basic_connect.sh0000775000076400007640000000072411234163024017112 0ustar doughnutdoughnut#!/bin/bash echo " ###################################################################### # TEST: basic connection # $Id: 10_basic_connect.sh,v 1.2 2005-09-12 22:22:15 doughnut Exp $ ###################################################################### " CMD='mussh -h localhost -c "date"' echo "COMMAND: $CMD" $CMD RETVAL=$? if [ "$RETVAL" -eq 0 ] ; then echo "PASSED" else echo "FAILED (returned $RETVAL)" echo "Hit any key to continue" read fi exit $RETVAL mussh/test/testhosts.list0000664000076400007640000000017211651634557016434 0ustar doughnutdoughnutlocalhost.localdomain localhost localhost localhost localhost localhost localhost localhost localhost localhost localhost mussh/mussh.spec0000664000076400007640000000327011651637243014530 0ustar doughnutdoughnut# # mussh spec file # $Id: mussh.spec,v 1.10 2006-12-26 23:10:11 doughnut Exp $ # Summary: MUltihost SSH Name: mussh Version: 1.0 Release: 1 License: GPL BuildArch: noarch BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) Group: Applications/System Source: %{name}-%{version}.tgz URL: http://www.sourceforge.net/projects/mussh Packager: Dave Fogarty %description Mussh is a shell script that allows you to execute a command or script over ssh on multiple hosts with one command. When possible mussh will use ssh-agent and RSA/DSA keys to minimize the need to enter your password more than once. %prep rm -rf $RPM_BUILD_ROOT %setup -n mussh %install mkdir -p $RPM_BUILD_ROOT/usr/bin/ mkdir -p ${RPM_BUILD_ROOT}%{_mandir}/man1/ install mussh $RPM_BUILD_ROOT/usr/bin/ gzip mussh.1 install mussh.1.gz ${RPM_BUILD_ROOT}%{_mandir}/man1/ %files %defattr(-, root, root) %doc INSTALL README BUGS CHANGES EXAMPLES /usr/bin/mussh %{_mandir}/man1/* %changelog * Mon Oct 25 2011 Dave Fogarty 1.0-1 - Another fix to CTRL-\. "HOSTS RUNNING:" was not working. - Increased efficiency when async. - No more need for 'seq'. - Spelling correction and other minor fixes. - Support for netgroups added. - Debug mode fix when using proxy. - Numeric args more intuitive but backwards compatible. - Verbose ssh now works. * Tue Dec 26 2006 Dave Fogarty 0.7-2 - Added ssh timeout option * Thu Aug 23 2005 Dave Fogarty - Added manpage * Thu Aug 11 2005 Dave Fogarty - Re-package for 0.6-1BETA - Async mode added * Tue Jul 30 2002 Dave Fogarty - Re-package for 0.5 mussh/mussh0000775000076400007640000005051711651643323013604 0ustar doughnutdoughnut#!/bin/bash # # $Id: mussh,v 1.12 2006/12/26 21:57:22 doughnut Exp $ MUSSH_VERSION="1.0" # # Description: This script is used to execute the same command(s) on # many hosts. # # by doughnut # ######################## # INITIALIZE THIS CRAP # ######################## DEBUG=0 SSH_VERBOSE="" QUIET=0 FORCE_AGENT=0 UNIQUE_HOSTS=1 PROXY_SSH_ARGS="-o PasswordAuthentication=no" SSH_ARGS="-o BatchMode=yes" AGENT_LOADED=0 REMOTE_SHELL='bash' TEMP_BASE=/tmp CONCURRENT=1 ############################ # GOTTA LOVE DOCUMENTATION # ############################ USAGE="Usage: mussh [OPTIONS] <-h host.. | -H hostfile> [-c cmd] [-C scriptfile] mussh --help for full help text" HELPTEXT=" Send a command or list of commands to multiple hosts. OPTIONS: --help This text. -d [n] Verbose debug. Prints each action, all hosts and commands to be executed to STDERR. 'n' can be from 0 to 2. -v [n] Ssh debug levels. Can be from 0 to 3. -m [n] Run concurrently on 'n' hosts at a time (asynchronous). Use '0' (zero) for infinite. (default if -m) -q No output unless necessary. -i [identity ..] Load an identity file. May be used more than once. -o Args to pass to ssh with -o option. -a Force loading ssh-agent. -A Do NOT load ssh-agent. -b Print each hosts' output in a block without mingling with other hosts' output. -B Allow hosts' output to mingle. (default) -u Unique. Eliminate duplicate hosts. (default) -U Do NOT make host list unique. -P Do NOT fall back to passwords on any host. This will skip hosts where keys fail. -l Use 'login' when no other is specified with hostname. -L Force use of 'login' name on all hosts. -s Path to shell on remote host. (Default: $REMOTE_SHELL) -t Timeout setting for each session. (requires openssh 3.8 or newer) -V Print version info and exit. PROXY ARGS: -p [user@] Host to use as proxy. (Must have mussh installed) -po Args to pass to ssh on proxy with -o option. HOST ARGS: -h [user@] [[user@] ..] Add a host to list of hosts. May be used more than once. -H [file ..] Add contents of file(s) to list of hosts. Files should have one host per line. Use \"#\" for comments. COMMAND ARGS: If neither is specified, commands will be read from standard input. -c Add a command or quoted list of commands and args to list of commands to be executed on each host. May be used more than once. -C [file ..] Add file contents to list of commands to be executed on each host. May be used more than once. At least one host is required. Arguments are in no particular order. EXAMPLES: mussh -H ./linuxhosts -C spfiles/testscript.sh mussh -c \"cat /etc/hosts\" -h myhost.mydomain.com Comments and Bug Reports: doughnut@doughnut.net " ########################### # FUNCTIONS FOR SSH-AGENT # ########################### load_keys() { [ "$AGENT_LOADED" = "0" -a "$IDENT" = "" ] && return [ "$DEBUG" -ge 1 ] && echo "DEBUG: Adding Keys" 1>&2 ssh-add $* 1>&2 } start_agent() { [ "$DEBUG" -ge 1 ] && echo "DEBUG: Starting Agent" 1>&2 if [ "$FORCE_AGENT" -ge 1 ] ; then # Load it anyway [ "$DEBUG" -ge 1 ] && echo "DEBUG: Forcing SSH Agent" 1>&2 elif [ -S "$SSH_AUTH_SOCK" ] ; then [ "$DEBUG" -ge 1 ] && echo "DEBUG: SSH Agent already loaded" 1>&2 return fi eval $(ssh-agent -s) >/dev/null AGENT_LOADED=1 } stop_agent() { # get rid of the keys that we added (if any) if [ "$IDENT" != "" ] ; then [ "$DEBUG" -ge 1 ] && echo "DEBUG: Removing keys from agent" 1>&2 ssh-add -d $IDENT > /dev/null 2>&1 fi [ "$AGENT_LOADED" = "0" ] && return [ "$DEBUG" -ge 1 ] && echo "DEBUG: Stopping Agent" 1>&2 eval $(ssh-agent -s -k) >/dev/null } #################################### # FUNCTIONS FOR REMOTE CONNECTIONS # #################################### proxy_connect() { [ "$DEBUG" -ge 1 ] && echo "DEBUG: PROXY CONNECT $PROXY" 1>&2 echo "$THESCRIPT" \ | ssh -T $SSH_ARGS $PROXY \ "mussh -h $HOSTLIST \ -C - \ -d$DEBUG \ $PROXY_SSH_ARGS 2>&1 " \ | while read SSH_LINE ; do if [ "$QUIET" -lt 1 -a "$SSH_LINE" != "" ] ; then echo "$SSH_LINE" | sed -e "s/^/$HOST: /" fi done } ssh_connect() { echo "$THESCRIPT" \ | ssh -T $SSH_ARGS $HOST "$REMOTE_SHELL" 2>&1 \ | while read SSH_LINE ; do if [ "$QUIET" -lt 1 -a "$SSH_LINE" != "" ] ; then echo "$SSH_LINE" | sed -e "s/^/$HOST: /" fi done } ############################## # FUNCTIONS FOR FORKED PROCS # ############################## set_hostlist() { # Create a hostlist file. [ "$DEBUG" -ge 2 ] && echo "DEBUG: BUILDING HOST LIST FILE $TEMP_DIR/hostlist" 1>&2 rm -f $TEMP_DIR/hostlist || exit 1 for HOST in $HOSTLIST ; do echo $HOST >> "$TEMP_DIR/hostlist" || exit 1 done } get_next_host() { # lock file while [ 1 ] ; do echo $CHILDNUM >> "$TEMP_DIR/hostlist.lock" TOP_PID=$(head -1 "$TEMP_DIR/hostlist.lock" 2>/dev/null) if [ "$TOP_PID" = $CHILDNUM ] ; then break fi [ "$DEBUG" -ge 2 ] && echo "DEBUG[#$CHILDNUM]: hostlist file already locked. Sleep..." 1>&2 #usleep 1000 sleep 1 done [ "$DEBUG" -ge 2 ] && echo "DEBUG[#$CHILDNUM]: Locked hostfile." 1>&2 # get next host NEXT_HOST=$(head -1 $TEMP_DIR/hostlist) HOSTFILE_LEN=$(wc -l $TEMP_DIR/hostlist | awk '{print $1}') if [ -z "$HOSTFILE_LEN" -o "$HOSTFILE_LEN" = 0 ] ; then rm -f "$TEMP_DIR/hostlist.lock" return fi [ "$DEBUG" -ge 2 ] && echo "DEBUG[#$CHILDNUM]: Next host: $NEXT_HOST" 1>&2 # re-write file removing new host rm -f "$TEMP_DIR/hostlist.new" echo tail -$(( $HOSTFILE_LEN - 1 )) $TEMP_DIR/hostlist > $TEMP_DIR/hostlist.new || exit 1 tail -$(( $HOSTFILE_LEN - 1 )) $TEMP_DIR/hostlist > $TEMP_DIR/hostlist.new || exit 1 rm -f "$TEMP_DIR/hostlist" mv "$TEMP_DIR/hostlist.new" "$TEMP_DIR/hostlist" # unlock file [ "$DEBUG" -ge 2 ] && echo "DEBUG[#$CHILDNUM]: Removing hostfile lock." 1>&2 rm -f "$TEMP_DIR/hostlist.lock" # return hostname echo $NEXT_HOST } run_child() { trap "exit 0" SIGHUP CHILDNUM=$1 [ "$DEBUG" -ge 2 ] && echo "DEBUG: FORKING CHILD #$CHILDNUM of $CONCURRENT (pid $!/$$)" 1>&2 while [ 1 ] ; do # issue: Cannot call get_next_host inside $() or `` because our trap won't be able to kill that. # solution: avoid subshell here by directing to a file. rm -f $TEMP_DIR/$CHILDNUM.next_host get_next_host >$TEMP_DIR/$CHILDNUM.next_host HOST=$(<$TEMP_DIR/$CHILDNUM.next_host) if [ -z "$HOST" ] ; then rm -f "$TEMP_DIR/$CHILDNUM.pid" break fi [ "$DEBUG" -ge 1 ] && echo "DEBUG[#$CHILDNUM]: CONNECT $HOST" 1>&2 rm -f "$TEMP_DIR/$CHILDNUM.active" echo "$HOST" > "$TEMP_DIR/$CHILDNUM.active" if [ -n "$BLOCKING" ] ; then ssh_connect > $TEMP_DIR/$HOST.out cat $TEMP_DIR/$HOST.out else ssh_connect fi done [ "$DEBUG" -ge 2 ] && echo "DEBUG: CHILD #$CHILDNUM done" 1>&2 rm -f "$TEMP_DIR/$CHILDNUM.pid" "$TEMP_DIR/$CHILDNUM.active" } ########################### # FUNCTIONS FOR TEMP DIRS # ########################### create_temp() { MKTEMP=$(which mktemp 2>/dev/null) if [ -x "$MKTEMP" ] ; then [ "$DEBUG" -ge 2 ] && echo "DEBUG: using mktemp ($MKTEMP)." 1>&2 TEMP_DIR=$(mktemp -d $TEMP_BASE/$(basename $0).XXXXXX) || exit 1 else [ "$DEBUG" -ge 2 ] && echo "DEBUG: can't find mktemp... using alternate." 1>&2 TEMP_DIR="$TEMP_BASE/$(basename $0).$(date +%s)" [ "$DEBUG" -ge 2 ] && echo "DEBUG: Creating temp dir ($TEMP_DIR)." 1>&2 if [ -e "$TEMP_DIR" ] ; then echo "$0: Temp dir \"$TEMP_DIR\" already exists!" 1>&2 exit 1 fi mkdir -m 700 $TEMP_DIR fi } destroy_temp() { if [ -d "$TEMP_DIR" ] ; then [ "$DEBUG" -ge 2 ] && echo "DEBUG: Removing temp dir ($TEMP_DIR)." 1>&2 rm -rf "$TEMP_DIR" 2>/dev/null fi } ######################################## # REMEMBER TO CLEAN UP BEFORE WE PANIC # ######################################## shutdown() { [ "$DEBUG" -ge 1 ] && echo "DEBUG: shutting down children." 1>&2 CPIDS=$(cat $TEMP_DIR/*.pid 2>/dev/null) for CPID in $CPIDS ; do [ "$DEBUG" -ge 2 ] && echo "DEBUG: Killing pid: $CPID" 1>&2 kill -HUP $CPID done [ "$DEBUG" -ge 2 ] && echo "DEBUG: shutting down ssh-agent" 1>&2 stop_agent [ "$DEBUG" -ge 2 ] && echo "DEBUG: removing temp dir" 1>&2 destroy_temp [ "$DEBUG" -ge 2 ] && echo "DEBUG: done shutting down." 1>&2 exit 1 } spew_hostlist() { echo "HOSTS RUNNING:" 1>&2 cat $TEMP_DIR/*.active 2>/dev/null | sed 's/^/ /' 1>&2 echo "HOSTS REMAINING:" 1>&2 cat $TEMP_DIR/hostlist 2>/dev/null | sed 's/^/ /' 1>&2 return } trap shutdown SIGINT trap shutdown SIGTERM trap spew_hostlist SIGQUIT trap "exit 0" SIGHUP ############################# # PARSE THE COMMAND OPTIONS # ############################# while [ "$1" != "" ]; do case "$1" in ########### # OPTIONS # ########### -A) NO_AGENT=1 shift ;; -a) FORCE_AGENT=1 shift ;; -b) BLOCKING=1 shift ;; -B) unset BLOCKING shift ;; -q) QUIET=1 DEBUG=0 SSH_VERBOSE="-q" shift ;; -o) SSH_ARGS="$SSH_ARGS -o $2" shift 2 ;; -u) UNIQUE_HOSTS=1 shift ;; -U) UNIQUE_HOSTS=0 shift ;; -l) DEFAULT_LOGIN=$2 shift 2 ;; -L) FORCE_LOGIN=$2 shift 2 ;; -s) REMOTE_SHELL=$2 shift 2 ;; -t*) SSH_TIMEOUT="${1#-t}" if [ -z "$SSH_TIMEOUT" -a -n "$2" -a "${2#-?*}" = "$2" ] ; then SSH_TIMEOUT=$2 shift fi if [ "${SSH_TIMEOUT//[^0-9]/}" != "$SSH_TIMEOUT" -o -z "$SSH_TIMEOUT" ] ; then echo "mussh: Argument should be numeric: -t $SSH_TIMEOUT" 1>&2 exit 1 fi shift SSH_ARGS="$SSH_ARGS -o ConnectTimeout=$SSH_TIMEOUT" ;; -m*) # -m0 .. -m999 CONCURRENT="${1#-m}" if [ -z "$CONCURRENT" -a -n "$2" -a "${2#-?*}" = "$2" ] ; then CONCURRENT=$2 shift elif [ -z "$CONCURRENT" ] ; then CONCURRENT=0 fi if [ "${CONCURRENT//[^0-9]/}" != "$CONCURRENT" ] ; then echo "mussh: Argument should be numeric: -m $CONCURRENT" 1>&2 exit 1 fi shift ;; -d*) DEBUG="${1#-d}" if [ -z "$DEBUG" -a -n "$2" -a "${2#-?*}" = "$2" ] ; then DEBUG=$2 shift elif [ -z "$DEBUG" ] ; then DEBUG=1 fi if [ "${DEBUG//[^0-9]/}" != "$DEBUG" ] ; then echo "mussh: Argument should be numeric: -d $DEBUG" 1>&2 exit 1 fi shift ;; -v*) TMP_ARG="${1#-v}" if [ -z "$TMP_ARG" -a -n "$2" -a "${2#-?*}" = "$2" ] ; then TMP_ARG=$2 shift elif [ -z "$TMP_ARG" ] ; then TMP_ARG=1 fi if [ "${TMP_ARG//[^0-9]/}" != "$TMP_ARG" ] ; then echo "mussh: Argument should be numeric: -v $TMP_ARG" 1>&2 exit 1 elif [ "${TMP_ARG//[^0-3]/}" != "$TMP_ARG" ] ; then echo "mussh: Argument should be between 0 and 3: -v $TMP_ARG" 1>&2 exit 1 fi SSH_VERBOSE="-v" [ "$TMP_ARG" -ge 2 ] && SSH_VERBOSE="$SSH_VERBOSE -v" [ "$TMP_ARG" -ge 3 ] && SSH_VERBOSE="$SSH_VERBOSE -v" [ "$TMP_ARG" -eq 0 ] && SSH_VERBOSE="-q" shift ;; -V) echo "Version: $MUSSH_VERSION" exit ;; -P) SSH_ARGS="$SSH_ARGS -o PasswordAuthentication=no" shift ;; --help) # print help text echo "$USAGE" echo "$HELPTEXT" exit ;; -i) # Load the identity file in ssh-agent while [ "$2" != "" -a "${2#-}" = "$2" ] ; do IDENT="$IDENT $2" shift done shift ;; ############## # PROXY ARGS # ############## -p) PROXY=$2 SSH_ARGS="$SSH_ARGS -o ForwardAgent=yes" shift 2 ;; -po) PROXY_SSH_ARGS="$PROXY_SSH_ARGS -o $2" shift 2 ;; ############# # HOST ARGS # ############# -h) while [ "$2" != "" -a "${2#-?*}" = "$2" ] ; do HOSTLIST="$2 $HOSTLIST" shift done shift ;; -H) while [ "$2" != "" -a "${2#-?*}" = "$2" ] ; do HOSTFILE="$2" if [ ! -e "$HOSTFILE" -a "$HOSTFILE" != "-" ] ; then echo "mussh: Host file '$HOSTFILE' does not exist!" 1>&2 exit 1 fi HOSTLIST="$(cat $HOSTFILE | sed -e 's/#.*//' | egrep -v "^ *$" ) $HOSTLIST" shift done shift ;; -n) # I've left this undocumented for lack of testing. Volunteers? NETGROUP=$2 NETGROUP_LIST="$(getent netgroup $NETGROUP | xargs -n 1 echo | sed -n '/^(.*,$/s/[,(]//gp')" if [ -z "$NETGROUP_LIST" ] ; then echo "mussh: Failed to get netgroup: $NETGROUP" 2>&1 exit 1 fi HOSTLIST="$NETGROUP_LIST $HOSTLIST" shift 2 ;; ################ # COMMAND ARGS # ################ -c) THESCRIPT="$THESCRIPT $2" #set "" ; shift shift 2 ;; -C) while [ "$2" != "" -a "${2#-?*}" = "$2" ] ; do SCRIPTFILE="$2" if [ ! -e "$SCRIPTFILE" -a "$SCRIPTFILE" != "-" ] ; then echo "mussh: Script File '$SCRIPTFILE' does not exist!" 1>&2 exit 1 fi THESCRIPT="$THESCRIPT $(cat $SCRIPTFILE )" shift done shift ;; *) echo "mussh: invalid command - \"$1\"" 1>&2 echo "$USAGE" 1>&2 exit 1 ;; esac done ##################### # CLEAN UP HOSTLIST # ##################### HOSTLIST=$(echo "$HOSTLIST" | sed -e 's/#.*//' | egrep -v "^ *$" ) if [ "$FORCE_LOGIN" != "" ] ; then [ "$DEBUG" -ge 1 ] && echo "DEBUG: FORCE_LOGIN: $FORCE_LOGIN" 1>&2 HOSTLIST=$(echo "$HOSTLIST" | sed -e "s/^\(.*@\)\{0,1\}\([^@]*\)\$/$FORCE_LOGIN@\2/") elif [ "$DEFAULT_LOGIN" != "" ] ; then [ "$DEBUG" -ge 1 ] && echo "DEBUG: DEFAULT_LOGIN: $DEFAULT_LOGIN" 1>&2 HOSTLIST=$(echo "$HOSTLIST" | sed -e "s/^\([^@]*\)\$/$DEFAULT_LOGIN@\1/") fi [ $UNIQUE_HOSTS -ge 1 ] && HOSTLIST=$(echo "$HOSTLIST" | sort -uf ) ################ # CHECK SYNTAX # ################ if [ "$HOSTLIST" = "" ] ; then echo "mussh: ERROR: You must supply at least one host!" 1>&2 echo "$USAGE" 1>&2 exit 1 fi ################################### # DEFAULT TO STDIN IF NO COMMANDS # ################################### if [ "$THESCRIPT" = "" ] ; then echo "Enter your script here. End with single \".\" or EOF." 1>&2 while read THISLINE do if [ "$THISLINE" = "." ] ; then break fi THESCRIPT="$THESCRIPT $THISLINE" done fi ################################################### # INFINITE CONCURRENCY IS REALLY JUST ALL AT ONCE # ################################################### COUNT_HOSTS=$(echo "$HOSTLIST" | wc -w) if [ $CONCURRENT -eq 0 ] || [ $CONCURRENT -gt $COUNT_HOSTS ] ; then CONCURRENT=$COUNT_HOSTS [ "$DEBUG" -ge 1 ] && echo "DEBUG: setting concurrency (-m) to $CONCURRENT (all hosts)" 1>&2 fi [ "$DEBUG" -ge 1 ] && echo "DEBUG: Concurrency: $CONCURRENT" 1>&2 ################################# # ADD SSH VERBOSITY TO SSH ARGS # ################################# if [ "$SSH_VERBOSE" ] ; then SSH_ARGS="$SSH_VERBOSE $SSH_ARGS" fi ############################ # PRINT VERBOSE DEBUG INFO # ############################ if [ "$DEBUG" -ge 1 ] ; then echo "DEBUG: DEBUG LEVEL: $DEBUG" 1>&2 echo "DEBUG: SSH DEBUG LEVEL: $SSH_VERBOSE" 1>&2 fi if [ "$DEBUG" -ge 2 ] ; then echo "DEBUG: HOSTLIST: " $HOSTLIST 1>&2 echo "DEBUG: THE SCRIPT: $THESCRIPT" 1>&2 echo "DEBUG: SSH ARGS: $SSH_ARGS" 1>&2 fi ############################ # LOAD SSH-AGENT WITH KEYS # ############################ if [ "$NO_AGENT" = "1" ] ; then [ "$DEBUG" -ge 1 ] && echo "DEBUG: Not using ssh-agent" 1>&2 elif [ "$IDENT" != "" ] ; then start_agent load_keys "$IDENT" elif [ ! -f "$HOME/.ssh/identity" -a ! -f "$HOME/.ssh/id_dsa" ] ; then [ "$DEBUG" -ge 1 ] && echo "DEBUG: No identity file found. Skipping agent." else start_agent load_keys "$IDENT" fi #echo ################### # CREATE TEMP DIR # ################### create_temp ######################## # EXECUTE THE COMMANDS # ######################## if [ -z "$CONCURRENT" ] ; then CONCURRENT=1 fi if [ "$PROXY" != "" ] ; then proxy_connect # Don't background process when we're only doing one at a time #elif [ -z "$CONCURRENT" -o "$CONCURRENT" = 1 ] ; then # set $HOSTLIST # while [ "$1" != "" ] ; do # HOST="$1" # [ "$DEBUG" -ge 1 ] && echo "DEBUG: CONNECT $HOST" 1>&2 # shift # ssh_connect # done else # Fork $CONCURRENT children set_hostlist CHILDNUM=1 while [ $CHILDNUM -le $CONCURRENT ] ; do run_child $CHILDNUM & [ "$DEBUG" -ge 2 ] && echo "DEBUG: FORKED CHILD #$CHILDNUM with pid $!" 1>&2 rm -f "$TEMP_DIR/$CHILDNUM.pid" echo $! > "$TEMP_DIR/$CHILDNUM.pid" (( CHILDNUM++ )) done wait # since trap causes waits to stop waiting with a return value >128 # We need to check that we really meant to stop waiting. RETVAL=$? while [ "$RETVAL" -gt 128 ] ; do [ "$DEBUG" -ge 2 ] && echo "DEBUG: wait returned with a value of $RETVAL" 1>&2 DO_WAIT=0 for CPID in $(cat $TEMP_DIR/*.pid 2>/dev/null) ; do if [ -d "/proc/$CPID" ] ; then DO_WAIT=1 [ "$DEBUG" -ge 2 ] && echo "DEBUG: $CPID is still running." 1>&2 fi done [ "$DO_WAIT" = 0 ] && break wait done fi ############ # CLEAN UP # ############ destroy_temp stop_agent mussh/mussh.bak0000775000076400007640000005050711651642106014335 0ustar doughnutdoughnut#!/bin/bash # # $Id: mussh,v 1.12 2006/12/26 21:57:22 doughnut Exp $ MUSSH_VERSION="1.0" # # Description: This script is used to execute the same command(s) on # many hosts. # # by doughnut # ######################## # INITAILIZE THIS CRAP # ######################## DEBUG=0 SSH_VERBOSE="-q" QUIET=0 FORCE_AGENT=0 UNIQUE_HOSTS=1 PROXY_SSH_ARGS="-o PasswordAuthentication=no" SSH_ARGS="-o BatchMode=yes" AGENT_LOADED=0 REMOTE_SHELL='bash' TEMP_BASE=/tmp CONCURRENT=1 ############################ # GOTTA LOVE DOCUMENTATION # ############################ USAGE="Usage: mussh [OPTIONS] <-h host.. | -H hostfile> [-c cmd] [-C scriptfile] mussh --help for full help text" HELPTEXT=" Send a command or list of commands to multiple hosts. OPTIONS: --help This text. -d [n] Verbose debug. Prints each action, all hosts and commands to be executed to STDERR. 'n' can be from 0 to 2. -v [n] Ssh debug levels. Can be from 0 to 3. -m [n] Run concurrently on 'n' hosts at a time (asynchronous). Use '0' (zero) for infinate. (default if -m) -q No output unless necessary. -i [identity ..] Load an identity file. May be used more than once. -o Args to pass to ssh with -o option. -a Force loading ssh-agent. -A Do NOT load ssh-agent. -b Print each hosts' output in a block without mingling with other hosts' output. -B Allow hosts' output to mingle. (default) -u Unique. Eliminate duplicate hosts. (default) -U Do NOT make host list unique. -P Do NOT fall back to passwords on any host. This will skip hosts where keys fail. -l Use 'login' when no other is specified with hostname. -L Force use of 'login' name on all hosts. -s Path to shell on remote host. (Default: $REMOTE_SHELL) -t Timeout setting for each session. (requires openssh 3.8 or newer) -V Print version info and exit. PROXY ARGS: -p [user@] Host to use as proxy. (Must have mussh installed) -po Args to pass to ssh on proxy with -o option. HOST ARGS: -h [user@] [[user@] ..] Add a host to list of hosts. May be used more than once. -H [file ..] Add contents of file(s) to list of hosts. Files should have one host per line. Use \"#\" for comments. -n Get list of hosts from netgroup COMMAND ARGS: If neither is specified, commands will be read from standard input. -c Add a command or quoted list of commands and args to list of commands to be executed on each host. May be used more than once. -C [file ..] Add file contents to list of commands to be executed on each host. May be used more than once. At least one host is required. Arguments are in no particular order. EXAMPLES: mussh -H ./linuxhosts -C spfiles/testscript.sh mussh -c \"cat /etc/hosts\" -h myhost.mydomain.com Comments and Bug Reports: doughnut@doughnut.net " ########################### # FUNCTIONS FOR SSH-AGENT # ########################### load_keys() { [ "$AGENT_LOADED" = "0" -a "$IDENT" = "" ] && return [ "$DEBUG" -ge 1 ] && echo "DEBUG: Adding Keys" 1>&2 ssh-add $* 1>&2 } start_agent() { [ "$DEBUG" -ge 1 ] && echo "DEBUG: Starting Agent" 1>&2 if [ "$FORCE_AGENT" -ge 1 ] ; then # Load it anyway [ "$DEBUG" -ge 1 ] && echo "DEBUG: Forcing SSH Agent" 1>&2 elif [ -S "$SSH_AUTH_SOCK" ] ; then [ "$DEBUG" -ge 1 ] && echo "DEBUG: SSH Agent already loaded" 1>&2 return fi eval $(ssh-agent -s) >/dev/null AGENT_LOADED=1 } stop_agent() { # get rid of the keys that we added (if any) if [ "$IDENT" != "" ] ; then [ "$DEBUG" -ge 1 ] && echo "DEBUG: Removing keys from agent" 1>&2 ssh-add -d $IDENT > /dev/null 2>&1 fi [ "$AGENT_LOADED" = "0" ] && return [ "$DEBUG" -ge 1 ] && echo "DEBUG: Stopping Agent" 1>&2 eval $(ssh-agent -s -k) >/dev/null } #################################### # FUNCTIONS FOR REMOTE CONNECTIONS # #################################### proxy_connect() { [ "$DEBUG" -ge 1 ] && echo "DEBUG: PROXY CONNECT $PROXY" 1>&2 echo "$THESCRIPT" \ | ssh -T $SSH_ARGS $PROXY \ "mussh -h $HOSTLIST \ -C - \ -d$DEBUG \ $PROXY_SSH_ARGS 2>&1 " \ | while read SSH_LINE ; do if [ "$QUIET" -lt 1 -a "$SSH_LINE" != "" ] ; then echo "$SSH_LINE" | sed -e "s/^/$HOST: /" fi done } ssh_connect() { echo "$THESCRIPT" \ | ssh -T $SSH_ARGS $HOST "$REMOTE_SHELL" 2>&1 \ | while read SSH_LINE ; do if [ "$QUIET" -lt 1 -a "$SSH_LINE" != "" ] ; then echo "$SSH_LINE" | sed -e "s/^/$HOST: /" fi done } ############################## # FUNCTIONS FOR FORKED PROCS # ############################## set_hostlist() { # Create a hostlist file. [ "$DEBUG" -ge 2 ] && echo "DEBUG: BUILDING HOST LIST FILE $TEMP_DIR/hostlist" 1>&2 rm -f $TEMP_DIR/hostlist || exit 1 for HOST in $HOSTLIST ; do echo $HOST >> "$TEMP_DIR/hostlist" || exit 1 done } get_next_host() { # lock file while [ 1 ] ; do echo $CHILDNUM >> "$TEMP_DIR/hostlist.lock" TOP_PID=$(head -1 "$TEMP_DIR/hostlist.lock" 2>/dev/null) if [ "$TOP_PID" = $CHILDNUM ] ; then break fi [ "$DEBUG" -ge 2 ] && echo "DEBUG[#$CHILDNUM]: hostlist file already locked. Sleep..." 1>&2 #usleep 1000 sleep 1 done [ "$DEBUG" -ge 2 ] && echo "DEBUG[#$CHILDNUM]: Locked hostfile." 1>&2 # get next host NEXT_HOST=$(head -1 $TEMP_DIR/hostlist) HOSTFILE_LEN=$(wc -l $TEMP_DIR/hostlist | awk '{print $1}') if [ -z "$HOSTFILE_LEN" -o "$HOSTFILE_LEN" = 0 ] ; then rm -f "$TEMP_DIR/hostlist.lock" return fi [ "$DEBUG" -ge 2 ] && echo "DEBUG[#$CHILDNUM]: Next host: $NEXT_HOST" 1>&2 # re-write file removing new host rm -f "$TEMP_DIR/hostlist.new" echo tail -$(expr $HOSTFILE_LEN - 1) $TEMP_DIR/hostlist > $TEMP_DIR/hostlist.new || exit 1 tail -$(expr $HOSTFILE_LEN - 1) $TEMP_DIR/hostlist > $TEMP_DIR/hostlist.new || exit 1 rm -f "$TEMP_DIR/hostlist" mv "$TEMP_DIR/hostlist.new" "$TEMP_DIR/hostlist" # unlock file [ "$DEBUG" -ge 2 ] && echo "DEBUG[#$CHILDNUM]: Removing hostfile lock." 1>&2 rm -f "$TEMP_DIR/hostlist.lock" # return hostname echo $NEXT_HOST } run_child() { trap "exit 0" SIGHUP CHILDNUM=$1 [ "$DEBUG" -ge 2 ] && echo "DEBUG: FORKING CHILD #$CHILDNUM of $CONCURRENT (pid $!/$$)" 1>&2 while [ 1 ] ; do # issue: Cannot call get_next_host inside $() or `` because our trap won't be able to kill that. # solution: avoid subshell here by directing to a file. rm -f $TEMP_DIR/$CHILDNUM.next_host get_next_host >$TEMP_DIR/$CHILDNUM.next_host HOST=$(<$TEMP_DIR/$CHILDNUM.next_host) if [ -z "$HOST" ] ; then rm -f "$TEMP_DIR/$CHILDNUM.pid" break fi [ "$DEBUG" -ge 1 ] && echo "DEBUG[#$CHILDNUM]: CONNECT $HOST" 1>&2 rm -f "$TEMP_DIR/$CHILDNUM.active" echo "$HOST" > "$TEMP_DIR/$CHILDNUM.active" if [ -n "$BLOCKING" ] ; then ssh_connect > $TEMP_DIR/$HOST.out cat $TEMP_DIR/$HOST.out else ssh_connect fi done [ "$DEBUG" -ge 2 ] && echo "DEBUG: CHILD #$CHILDNUM done" 1>&2 rm -f "$TEMP_DIR/$CHILDNUM.pid" "$TEMP_DIR/$CHILDNUM.active" } ########################### # FUNCTIONS FOR TEMP DIRS # ########################### create_temp() { MKTEMP=$(which mktemp 2>/dev/null) if [ -x "$MKTEMP" ] ; then [ "$DEBUG" -ge 2 ] && echo "DEBUG: using mktemp ($MKTEMP)." 1>&2 TEMP_DIR=$(mktemp -d $TEMP_BASE/$(basename $0).XXXXXX) || exit 1 else [ "$DEBUG" -ge 2 ] && echo "DEBUG: can't find mktemp... using alternate." 1>&2 TEMP_DIR="$TEMP_BASE/$(basename $0).$(date +%s)" [ "$DEBUG" -ge 2 ] && echo "DEBUG: Creating temp dir ($TEMP_DIR)." 1>&2 if [ -e "$TEMP_DIR" ] ; then echo "$0: Temp dir \"$TEMP_DIR\" already exists!" 1>&2 exit 1 fi mkdir -m 700 $TEMP_DIR fi } destroy_temp() { if [ -d "$TEMP_DIR" ] ; then [ "$DEBUG" -ge 2 ] && echo "DEBUG: Removing temp dir ($TEMP_DIR)." 1>&2 rm -rf "$TEMP_DIR" 2>/dev/null fi } ######################################## # REMEMBER TO CLEAN UP BEFORE WE PANIC # ######################################## shutdown() { [ "$DEBUG" -ge 1 ] && echo "DEBUG: shutting down children." 1>&2 CPIDS=$(cat $TEMP_DIR/*.pid 2>/dev/null) for CPID in $CPIDS ; do [ "$DEBUG" -ge 2 ] && echo "DEBUG: Killing pid: $CPID" 1>&2 kill -HUP $CPID done [ "$DEBUG" -ge 2 ] && echo "DEBUG: shutting down ssh-agent" 1>&2 stop_agent [ "$DEBUG" -ge 2 ] && echo "DEBUG: removing temp dir" 1>&2 destroy_temp [ "$DEBUG" -ge 2 ] && echo "DEBUG: done shutting down." 1>&2 exit 1 } spew_hostlist() { echo "HOSTS RUNNING:" 1>&2 cat $TEMP_DIR/*.active 2>/dev/null | sed 's/^/ /' 1>&2 echo "HOSTS REMAINING:" 1>&2 cat $TEMP_DIR/hostlist 2>/dev/null | sed 's/^/ /' 1>&2 return } trap shutdown SIGINT trap shutdown SIGTERM trap spew_hostlist SIGQUIT trap "exit 0" SIGHUP ############################# # PARSE THE COMMAND OPTIONS # ############################# while [ "$1" != "" ]; do case "$1" in ########### # OPTIONS # ########### -A) NO_AGENT=1 shift ;; -a) FORCE_AGENT=1 shift ;; -b) BLOCKING=1 shift ;; -B) unset BLOCKING shift ;; -q) QUIET=1 DEBUG=0 SSH_VERBOSE="-q" shift ;; -o) SSH_ARGS="$SSH_ARGS -o $2" shift 2 ;; -u) UNIQUE_HOSTS=1 shift ;; -U) UNIQUE_HOSTS=0 shift ;; -l) DEFAULT_LOGIN=$2 shift 2 ;; -L) FORCE_LOGIN=$2 shift 2 ;; -s) REMOTE_SHELL=$2 shift 2 ;; -t*) SSH_TIMEOUT="${1#-t}" if [ -z "$SSH_TIMEOUT" -a -n "$2" -a "${2#-?*}" = "$2" ] ; then SSH_TIMEOUT=$2 shift fi if [ "${SSH_TIMEOUT//[^0-9]/}" != "$SSH_TIMEOUT" -o -z "$SSH_TIMEOUT" ] ; then echo "mussh: Arguement should be numeric: -t $SSH_TIMEOUT" 1>&2 exit 1 fi shift SSH_ARGS="$SSH_ARGS -o ConnectTimeout=$SSH_TIMEOUT" ;; -m*) # -m0 .. -m999 CONCURRENT="${1#-m}" if [ -z "$CONCURRENT" -a -n "$2" -a "${2#-?*}" = "$2" ] ; then CONCURRENT=$2 shift elif [ -z "$CONCURRENT" ] ; then CONCURRENT=0 fi if [ "${CONCURRENT//[^0-9]/}" != "$CONCURRENT" ] ; then echo "mussh: Arguement should be numeric: -m $CONCURRENT" 1>&2 exit 1 fi shift ;; -d*) DEBUG="${1#-d}" if [ -z "$DEBUG" -a -n "$2" -a "${2#-?*}" = "$2" ] ; then DEBUG=$2 shift elif [ -z "$DEBUG" ] ; then DEBUG=1 fi if [ "${DEBUG//[^0-9]/}" != "$DEBUG" ] ; then echo "mussh: Arguement should be numeric: -d $DEBUG" 1>&2 exit 1 fi shift ;; -v*) TMP_ARG="${1#-v}" if [ -z "$TMP_ARG" -a -n "$2" -a "${2#-?*}" = "$2" ] ; then TMP_ARG=$2 shift elif [ -z "$TMP_ARG" ] ; then TMP_ARG=1 fi if [ "${TMP_ARG//[^0-9]/}" != "$TMP_ARG" ] ; then echo "mussh: Arguement should be numeric: -v $TMP_ARG" 1>&2 exit 1 elif [ "${TMP_ARG//[^0-3]/}" != "$TMP_ARG" ] ; then echo "mussh: Arguement should be between 0 and 3: -v $TMP_ARG" 1>&2 exit 1 fi SSH_VERBOSE="-v" [ "$TMP_ARG" -ge 2 ] && SSH_VERBOSE="$SSH_VERBOSE -v" [ "$TMP_ARG" -ge 3 ] && SSH_VERBOSE="$SSH_VERBOSE -v" [ "$TMP_ARG" -eq 0 ] && SSH_VERBOSE="-q" shift ;; -V) echo "Version: $MUSSH_VERSION" exit ;; -P) SSH_ARGS="$SSH_ARGS -o PasswordAuthentication=no" shift ;; --help) # print help text echo "$USAGE" echo "$HELPTEXT" exit ;; -i) # Load the identity file in ssh-agent while [ "$2" != "" -a "${2#-}" = "$2" ] ; do IDENT="$IDENT $2" shift done shift ;; ############## # PROXY ARGS # ############## -p) PROXY=$2 SSH_ARGS="$SSH_ARGS -o ForwardAgent=yes" shift 2 ;; -po) PROXY_SSH_ARGS="$PROXY_SSH_ARGS -o $2" shift 2 ;; ############# # HOST ARGS # ############# -h) while [ "$2" != "" -a "${2#-?*}" = "$2" ] ; do HOSTLIST="$2 $HOSTLIST" shift done shift ;; -H) while [ "$2" != "" -a "${2#-?*}" = "$2" ] ; do HOSTFILE="$2" if [ ! -e "$HOSTFILE" -a "$HOSTFILE" != "-" ] ; then echo "mussh: Host file '$HOSTFILE' does not exist!" 1>&2 exit 1 fi HOSTLIST="$(cat $HOSTFILE | sed -e 's/#.*//' | egrep -v "^ *$" ) $HOSTLIST" shift done shift ;; -n) NETGROUP=$2 NETGROUP_LIST="$(getent netgroup $NETGROUP | xargs -n 1 echo | sed -n '/^(.*,$/s/[,(]//gp')" if [ -z "$NETGROUP_LIST" ] ; then echo "mussh: Failed to get netgroup: $NETGROUP" 2>&1 exit 1 fi HOSTLIST="$NETGROUP_LIST $HOSTLIST" shift 2 ;; ################ # COMMAND ARGS # ################ -c) THESCRIPT="$THESCRIPT $2" #set "" ; shift shift 2 ;; -C) while [ "$2" != "" -a "${2#-?*}" = "$2" ] ; do SCRIPTFILE="$2" if [ ! -e "$SCRIPTFILE" -a "$SCRIPTFILE" != "-" ] ; then echo "mussh: Script File '$SCRIPTFILE' does not exist!" 1>&2 exit 1 fi THESCRIPT="$THESCRIPT $(cat $SCRIPTFILE )" shift done shift ;; *) echo "mussh: invalid command - \"$1\"" 1>&2 echo "$USAGE" 1>&2 exit 1 ;; esac done ##################### # CLEAN UP HOSTLIST # ##################### HOSTLIST=$(echo "$HOSTLIST" | sed -e 's/#.*//' | egrep -v "^ *$" ) if [ "$FORCE_LOGIN" != "" ] ; then [ "$DEBUG" -ge 1 ] && echo "DEBUG: FORCE_LOGIN: $FORCE_LOGIN" 1>&2 HOSTLIST=$(echo "$HOSTLIST" | sed -e "s/^\(.*@\)\{0,1\}\([^@]*\)\$/$FORCE_LOGIN@\2/") elif [ "$DEFAULT_LOGIN" != "" ] ; then [ "$DEBUG" -ge 1 ] && echo "DEBUG: DEFAULT_LOGIN: $DEFAULT_LOGIN" 1>&2 HOSTLIST=$(echo "$HOSTLIST" | sed -e "s/^\([^@]*\)\$/$DEFAULT_LOGIN@\1/") fi [ $UNIQUE_HOSTS -ge 1 ] && HOSTLIST=$(echo "$HOSTLIST" | sort -uf ) ################ # CHECK SYNTAX # ################ if [ "$HOSTLIST" = "" ] ; then echo "mussh: ERROR: You must supply at least one host!" 1>&2 echo "$USAGE" 1>&2 exit 1 fi ################################### # DEFAULT TO STDIN IF NO COMMANDS # ################################### if [ "$THESCRIPT" = "" ] ; then echo "Enter your script here. End with single \".\" or EOF." 1>&2 while read THISLINE do if [ "$THISLINE" = "." ] ; then break fi THESCRIPT="$THESCRIPT $THISLINE" done fi ################################################### # INFINATE CONCURRENCY IS REALLY JUST ALL AT ONCE # ################################################### COUNT_HOSTS=$(echo "$HOSTLIST" | wc -w) if [ $CONCURRENT -eq 0 ] || [ $CONCURRENT -gt $COUNT_HOSTS ] ; then CONCURRENT=$COUNT_HOSTS [ "$DEBUG" -ge 1 ] && echo "DEBUG: setting concurrency (-m) to $CONCURRENT (all hosts)" 1>&2 fi [ "$DEBUG" -ge 1 ] && echo "DEBUG: Concurrency: $CONCURRENT" 1>&2 ################################# # ADD SSH VERBOSITY TO SSH ARGS # ################################# if [ "$SSH_VERBOSE" ] ; then SSH_ARGS="$SSH_VERBOSE $SSH_ARGS" fi ############################ # PRINT VERBOSE DEBUG INFO # ############################ if [ "$DEBUG" -ge 1 ] ; then echo "DEBUG: DEBUG LEVEL: $DEBUG" 1>&2 echo "DEBUG: SSH DEBUG LEVEL: $SSH_VERBOSE" 1>&2 fi if [ "$DEBUG" -ge 2 ] ; then echo "DEBUG: HOSTLIST: " $HOSTLIST 1>&2 echo "DEBUG: THE SCRIPT: $THESCRIPT" 1>&2 echo "DEBUG: SSH ARGS: $SSH_ARGS" 1>&2 fi ############################ # LOAD SSH-AGENT WITH KEYS # ############################ if [ "$NO_AGENT" = "1" ] ; then [ "$DEBUG" -ge 1 ] && echo "DEBUG: Not using ssh-agent" 1>&2 elif [ "$IDENT" != "" ] ; then start_agent load_keys "$IDENT" elif [ ! -f "$HOME/.ssh/identity" -a ! -f "$HOME/.ssh/id_dsa" ] ; then [ "$DEBUG" -ge 1 ] && echo "DEBUG: No identity file found. Skipping agent." else start_agent load_keys "$IDENT" fi #echo ################### # CREATE TEMP DIR # ################### create_temp ######################## # EXECUTE THE COMMANDS # ######################## if [ -z "$CONCURRENT" ] ; then CONCURRENT=1 fi if [ "$PROXY" != "" ] ; then proxy_connect # Don't background process when we're only doing one at a time #elif [ -z "$CONCURRENT" -o "$CONCURRENT" = 1 ] ; then # set $HOSTLIST # while [ "$1" != "" ] ; do # HOST="$1" # [ "$DEBUG" -ge 1 ] && echo "DEBUG: CONNECT $HOST" 1>&2 # shift # ssh_connect # done else # Fork $CONCURRENT children set_hostlist CHILDNUM=1 while [ $CHILDNUM -le $CONCURRENT ] ; do run_child $CHILDNUM & [ "$DEBUG" -ge 2 ] && echo "DEBUG: FORKED CHILD #$CHILDNUM with pid $!" 1>&2 rm -f "$TEMP_DIR/$CHILDNUM.pid" echo $! > "$TEMP_DIR/$CHILDNUM.pid" (( CHILDNUM++ )) done wait # since trap causes waits to stop waiting with a return value >128 # We need to check that we really meant to stop waiting. RETVAL=$? while [ "$RETVAL" -gt 128 ] ; do [ "$DEBUG" -ge 2 ] && echo "DEBUG: wait returned with a value of $RETVAL" 1>&2 DO_WAIT=0 for CPID in $(cat $TEMP_DIR/*.pid 2>/dev/null) ; do if [ -d "/proc/$CPID" ] ; then DO_WAIT=1 [ "$DEBUG" -ge 2 ] && echo "DEBUG: $CPID is still running." 1>&2 fi done [ "$DO_WAIT" = 0 ] && break wait done fi ############ # CLEAN UP # ############ destroy_temp stop_agent mussh/casetest.sh0000775000076400007640000000451711651621502014663 0ustar doughnutdoughnut#!/bin/bash while [ "$1" != "" ]; do case "$1" in ########### # OPTIONS # ########### -A) NO_AGENT=1 shift ;; -m*) # -m0 .. -m999 CONCURRENT="${1#-m}" if [ -z "$CONCURRENT" -a "${2#-?*}" = "$2" ] ; then CONCURRENT=$2 shift elif [ -z "$CONCURRENT" ] ; then CONCURRENT=0 fi if [ "${CONCURRENT//[^0-9]/}" != "$CONCURRENT" ] ; then echo "Arguement should be numeric: $CONCURRENT" 1>&2 exit 1 fi shift echo "Concurrency: $CONCURRENT" ;; -d*) DEBUG="${1#-d}" if [ -z "$DEBUG" -a "${2#-?*}" = "$2" ] ; then DEBUG=$2 shift elif [ -z "$DEBUG" ] ; then DEBUG=1 fi if [ "${DEBUG//[^0-9]/}" != "$DEBUG" ] ; then echo "Arguement should be numeric: $DEBUG" 1>&2 exit 1 fi shift echo "Debug: $DEBUG" ;; -v*) TMP_ARG="${1#-v}" if [ -z "$TMP_ARG" -a "${2#-?*}" = "$2" ] ; then TMP_ARG=$2 shift elif [ -z "$TMP_ARG" ] ; then TMP_ARG=1 fi if [ "${TMP_ARG//[^0-9]/}" != "$TMP_ARG" ] ; then echo "Arguement should be numeric: $TMP_ARG" 1>&2 exit 1 elif [ "${TMP_ARG//[^0-3]/}" != "$TMP_ARG" ] ; then echo "Arguement should be between 0 and 3: $TMP_ARG" 1>&2 exit 1 fi SSH_VERBOSE="-v" [ "$TMP_ARG" -ge 2 ] && SSH_VERBOSE="$SSH_VERBOSE -v" [ "$TMP_ARG" -ge 3 ] && SSH_VERBOSE="$SSH_VERBOSE -v" shift echo "Verbosity: $SSH_VERBOSE" ;; -h) while [ "$2" != "" -a "${2#-?*}" = "$2" ] ; do HOSTLIST="$2 $HOSTLIST" shift done shift ;; *) echo "mussh: invalid command - \"$1\"" 1>&2 #exit 1 shift ;; esac done mussh/.swp0000600000076400007640000003000011651637711013304 0ustar doughnutdoughnutb0VIM 7.3oKdoughnutshoe.unival.com U3210#"! Utpadfor mussh/README0000664000076400007640000001332311651632513013370 0ustar doughnutdoughnutmussh - MUltihost SSH $Id: README,v 1.7 2005/08/24 00:34:31 doughnut Exp $ ===================== Mussh is a shell script that allows you to execute a command or script over ssh on multiple hosts with one command. When possible mussh will use ssh-agent and RSA/DSA keys to minimize the need to enter your password more than once. Requirements ============ General: - ssh with ssh-agent When Proxying: - Proxy server must have OpenSSH 2.3 or greater, or an sshd that works with ForwardAgent under ssh2. - Proxy server must have mussh installed in your PATH. To verify that it is in your path use "ssh user@proxy 'which mussh'". Use "ssh user@proxy 'echo $PATH'" to determine what your path is. License ======= GPL Compatibility ============ I wrote and tested this on Mandrake 8.x, Redhat 7.x, RedHat Enterprise (2.1,3,4), and Fedora Core (3,4,5). Other than that, it needs more testing! If you get this to run _anywhere_ else, email me at doughnut@doughnut.net. Interaction with ssh-agent ========================== Assuming that you're not turning off the agent with '-A' mussh will attempt to use ssh-agent. Normally mussh will get rid of the agent when it exits. See EXAMPLES for examples. Usage ===== mussh [OPTIONS] Arguments can be in any order. OPTIONS: --help This gives a brief help message. -d see -d1 -d 0 Turns debug mode off. -d 1 On STDERR prints out basic actions and ssh-agent activity and which host is being connected to. -d 2 Includes all of the output from -d1, the list of hosts and the command/script as it will be executed on each host. -v see -v1 -v 1 Sets ssh in debug1 mode by passing "-v" to ssh. -v 2 Sets ssh in debug2 mode by passing "-v -v" to ssh. -v 3 Sets ssh in debug3 mode by passing "-v -v -v" to ssh. -m [n] Run concurrently on 'n' hosts at a time (async). Use '0' (zero) for infinite. (default if -m) -q No output unless necessary. This will cancel -d and -v if after them on the command line. It also suppresses the output of each host. This will NOT suppress the password/passphrase prompts required to log into each host. -P Do NOT fall back to passwords on any host. This will skip hosts where keys fail. If you use this with '-d' you'll still see which hosts failed. -i [identity ..] Load an identity file. When -i is used, the specified identity file(s) is loaded instead of the default identity. You can load as many RSA/DSA identities as you'd like. -o Args to pass to ssh with -o option. See the ssh(1) man page for more info on the -o option. -a Force loading ssh-agent. Without this flag, mussh will not load another agent when one is already loaded. -A Do NOT load ssh-agent. If no agent is loaded you will be prompted for a password or passphrase by ssh for each host. If you do not have RSA/DSA keys for the destination hosts, this will save you some hassle. -b Print each hosts' output in a block without mingling with other hosts' output. -B Allow hosts' output to mingle. (default) -l Use 'login' when no other is specified with hostname. -L Force use of 'login' name on all hosts. These can be handy for adding 'root@' to hostnames kept in a file for -H option. With -h it means you get to type less. -u Unique. Eliminate duplicate hosts (default). If you a host or user@host occurs more than once across files specified with -H or hosts specified with -h, the host or user@host is used only once. -U Do NOT make host list unique. This simply overrides the -u flag. This will cause scripts to be executed on duplicate hosts once per listing. -s Path to shell on remote host. (Default: bash) This will allow you to run commands in a shell other than bash. Sorry but one had to be specified. -t Timeout setting for each session. (requires openssh 3.8 or newer) -V Print version info and exit. HOST ARGS: -h [user@] [[user@] ..] As with ssh itself, the username can be supplied for a host but is not required. -H [file ..] Add contents of file(s) to list of hosts. Files should have one host per line. Use "#" for comments. COMMAND ARGS: -c Add a command or quoted list of commands and args to list of commands to be executed on each host. May be used more than once. -C [file ..] Add file contents to list of commands to be executed on each host. May be used more than once. At least one host is required. Arguments are in no particular order. Tips ==== - To pipe commands to mussh, you can get the commands from standard input by using '-C -'. - To pipe hosts to mussh, you can get the hosts from standard input by using '-H -'. - When you already have an ssh-agent loaded, you can avoid loading sensitive keys into that agent by using '-a' to force mussh to load (and dispose of) it's own agent. - When mussh is running you can use CTRL-\ to view which hosts are currently running and which hosts remain. - Blocking (-b) in asynchronous mode (-m) only makes the output look serial. The commands are still running at the same time on the remote host. mussh/mussh.10000664000076400007640000001260611651633575013745 0ustar doughnutdoughnut.\" $Id: mussh.1,v 1.5 2006-12-26 21:57:22 doughnut Exp $ .\" .TH MUSSH 1 "August 2005" Doughnut "MUltihost SSH" .SH NAME mussh \- MUltihost SSH .SH SYNOPSIS .B mussh [ .I OPTIONS .B ] <\-h .I host... .B | \-H .I hostfile .B > [\-c .I cmd .B ] [\-C .I scriptfile .B ] .SH DESCRIPTION .B mussh is a shell script that allows you to execute a command or script over .BR ssh (1) on multiple hosts with one command. When possible mussh will use .BR ssh\-agent (1) and RSA/DSA keys to minimize the need to enter your password more than once. .SH OPTIONS .IP \-\-help Prints full help text. .IP \-d Same as \-d 1 .IP "\-d 0" Turns debug mode off. .IP "\-d 1" On STDERR prints out basic actions and ssh\-agent activity and which host is being connected to. .IP "\-d 2" Includes all of the output from \-d1, the list of hosts, the command/script as it will be executed on each host, and a lot more. .IP \-v Same as \-v 1 .IP "\-v 1" Sets ssh in debug1 mode by passing "\-v" to ssh. .IP "\-v 2" Sets ssh in debug2 mode by passing "\-v \-v" to ssh. .IP "\-v 3" Sets ssh in debug3 mode by passing "\-v \-v \-v" to ssh. .IP "\-m [n]" Run concurrently on 'n' hosts at a time (asynchronous). Use '0' (zero) for infinite. (default) .IP \-q No output unless necessary. This will cancel \-d and \-v if after them on the command line. It also suppresses the output of each host. This will NOT suppress the password/passphrase prompts required to log into each host. .IP "\-i [identity ..]" Load an identity file. When \-i is used, the specified identity file(s) is loaded instead of the default identity. You can load as many RSA/DSA identities as you'd like. .IP "\-o " Args to pass to ssh with \-o option. See the .BR ssh (1) man page for more info on the \-o option. .IP \-a Force loading ssh\-agent. Without this flag, mussh will not load another agent when one is already loaded. .IP \-A Do NOT load ssh\-agent. If no agent is loaded you will be prompted for a password or passphrase by ssh for each host. If you do not have RSA/DSA keys for the destination hosts, this will save you some hassle. .IP \-b Print each hosts' output in a block without mingling with other hosts' output. .IP \-B Allow hosts' output to mingle. (default) .IP \-u Unique. Eliminate duplicate hosts. (default) If you a host or user@host occurs more than once across files specified with \-H or hosts specified with \-h, the host or user@host is used only once. .IP \-U Do NOT make host list unique. This simply overrides the \-u flag. This will cause scripts to be executed on duplicate hosts once per listing. .IP \-P Do NOT fall back to passwords on any host. This will skip hosts where keys fail. If you use this with '\-d' you'll still see which hosts failed. .IP "\-l " Use 'login' when no other is specified with hostname. .IP "\-L " Force use of 'login' name on all hosts. These can be handy for adding 'root@' to hostnames kept in a file for \-H option. With \-h it means you get to type less. .IP "\-s " Path to shell on remote host. (Default: bash) .IP "\-t " Timeout setting for each session. (requires openssh 3.8 or newer) .IP \-V Print version info and exit. .SH PROXY ARGS .IP "\-p [user@]" Host to use as proxy. (Must have mussh installed) .IP "\-po " Args to pass to ssh on proxy with \-o option. .SH HOST ARGS .IP "\-h [user@] [[user@] ..]" Add a host to list of hosts. May be used more than once. .IP "\-H [file ..]" Add contents of file(s) to list of hosts. Files should have one host per line. Use "#" for comments. .SH COMMAND ARGS If neither is specified, commands will be read from standard input. .IP "\-c " Add a command or quoted list of commands and args to list of commands to be executed on each host. May be used more than once. .IP "\-C [file ..]" Add file contents to list of commands to be executed on each host. May be used more than once. .SH PROXY MODE When proxying, mussh can use a single remote server to as a bastion host. All hosts will be connected to from the central host rather than from the computer where you are initially running mussh. This can be handy when you only have access to one machine behind a firewall. The proxy host must have OpenSSH 2.3 or greater, or an sshd that works with ForwardAgent under ssh2. Proxy server must also have mussh installed in your PATH. To verify that it is in your path use "ssh user@proxy 'which mussh'". Use "ssh user@proxy 'echo $PATH'" to determine what your path is. .SH SSH\-AGENT INTERACTION Assuming that you're not turning off the agent with '\-A' mussh will attempt to use .BR ssh\-agent (1). Normally mussh will get rid of the agent when it exits. See EXAMPLES for examples. .SH EXAMPLES There is an EXAMPLES file with detailed examples. .IP "The basic command:" .B $ mussh \-h foo bar baz .IP "A simple command:" .B $ mussh \-h foo bar baz \-c 'rpm \-e emacs' .IP "A simple command asynchronously:" .B $ mussh \-h foo bar baz \-c 'rpm \-e emacs' \-m .IP "Using a specific key:" .B $ mussh \-h foo bar baz \-c 'rpm \-e emacs' \-i ~/.ssh/my_other.key .IP "Loading a list of hosts from a file:" .B $ mussh \-H /tmp/hostlist.txt \-c 'rpm \-e emacs' .IP "Loading a script from a file:" .B $ mussh \-h foo bar baz \-C /tmp/scriptfile.sh .SH BUGS Please report any bugs at http://sourceforge.net/projects/mussh/ .SH AUTHOR Dave Fogarty .SH "SEE ALSO" .BR ssh (1), .BR ssh\-agent (1) mussh/EXAMPLES0000664000076400007640000000764411234163024013653 0ustar doughnutdoughnut SITUATION A (using ssh-agent): You run mussh and ssh-agent loads your default rsa/dsa key. You are prompted for your passphrase, you enter it, mussh executes the commands, kills ssh-agent, and exits. [doughnut@extradot doughnut]$ mussh -h swing slide junglegym -c 'rpm -q emacs' Need passphrase for /home/doughnut/.ssh/identity Enter passphrase for doughnut@extradot.yourdomain.com: Identity added: /home/doughnut/.ssh/identity (doughnut@extradot.yourdomain.com) junglegym: package emacs is not installed slide: package emacs is not installed swing: package emacs is not installed SITUATION B (using passwords): You run mussh but don't have RSA/DSA keys set up. When mussh connects to each host, it prompts you for the password. After completing the last host, mussh exits. [doughnut@extradot doughnut]$ mussh -h swing slide junglegym -c 'rpm -q emacs' /home/doughnut/.ssh/identity: No such file or directory doughnut@junglegym's password: junglegym: package emacs is not installed doughnut@slide's password: slide: package emacs is not installed doughnut@swing's password: swing: package emacs is not installed SITUATION C (ssh-agent already running): If you are already running ssh-agent with the keys loaded, mussh will not load keys unless you tell it to with the -i flag. [doughnut@extradot doughnut]$ mussh -h swing slide junglegym -c 'rpm -q emacs' junglegym: package emacs is not installed slide: package emacs is not installed swing: package emacs is not installed SITUATION D (everything but a goat): This example uses almost everything you could. Specifying hosts on the command line AND in a file with comments and blank lines, executing commands from the command line and from a file, forcing a unique agent to be loaded, using multiple keys (both RSA and DSA), and turing debug mode on for verbose output. [doughnut@extradot doughnut]$ cat dev/spfiles/testhosts dave@hobo #test@testhost merrygoround [doughnut@extradot doughnut]$ cat dev/spfiles/testscript date uptime [doughnut@extradot doughnut]$ mussh -a -i $HOME/.ssh/identity $HOME/.ssh/id_dsa -d -h swing slide -H dev/spfiles/testhosts -h junglegym -c 'rpm -q emacs' -C dev/spfiles/testscript DEBUG: Starting Agent DEBUG: Forcing SSH Agent DEBUG: Adding Keys Need passphrase for /home/doughnut/.ssh/identity Enter passphrase for doughnut@extradot.yourdomain.com: Identity added: /home/doughnut/.ssh/identity (doughnut@extradot.yourdomain.com) Need passphrase for /home/doughnut/.ssh/id_dsa Enter passphrase for /home/doughnut/.ssh/id_dsa: Bad passphrase, try again: Identity added: /home/doughnut/.ssh/id_dsa (/home/doughnut/.ssh/id_dsa) DEBUG: CONNECT junglegym junglegym: Wed Jun 27 18:34:37 PDT 2001 junglegym: 6:34pm up 19 days, 18:46, 1 user, load average: 0.00, 0.00, 0.00 junglegym: package emacs is not installed DEBUG: CONNECT dave@hobo dave@hobo: Wed Jun 27 18:35:35 PDT 2001 dave@hobo: 6:35pm up 20 days, 19:54, 11 users, load average: 0.00, 0.00, 0.00 dave@hobo: package emacs is not installed DEBUG: CONNECT merrygoround merrygoround: Wed Jun 27 18:35:27 PDT 2001 merrygoround: 6:35pm up 20 days, 19:53, 1 user, load average: 0.22, 0.11, 0.03 merrygoround: emacs-20.5-7 DEBUG: CONNECT slide slide: Wed Jun 27 18:35:32 PDT 2001 slide: 6:35pm up 19 days, 17:40, 4 users, load average: 0.00, 0.00, 0.00 slide: package emacs is not installed DEBUG: CONNECT swing swing: Wed Jun 27 18:36:15 PDT 2001 swing: 6:36pm up 20 days, 19:54, 3 users, load average: 0.00, 0.00, 0.00 swing: package emacs is not installed DEBUG: Removing keys from agent DEBUG: Stopping Agent SITUATION E (asynchronous mode with ssh-agent already running): Asynchronous mode (-m) allows you to get done a lot quicker by not having to wait for one host to finnish before moving on to the next. [doughnut@shoe mussh]$ mussh -h swing slide junglegym -c 'echo hello ; sleep 2 ; echo bye' -m2 junglegym: hello slide: hello junglegym: bye swing: hello slide: bye swing: bye mussh/.mussh.1.swp0000644000076400007640000005000011651633575014617 0ustar doughnutdoughnutb0VIM 7.3 7N *Bdoughnutshoe.unival.com~doughnut/sandbox/it/AdminProjects/trunk/DaveTools/mussh/mussh.1utf-8 3210#"! Utp*M,6xad _ *obWLA6, C0 { o =   y k _ _ 2 u N F * u e B + S F   V%vnE=xp"pE3G5h5}b8"#" for comments.Files should have one host per line. UseAdd contents of file(s) to list of hosts..IP "\-H [file ..]"used more than once.Add a host to list of hosts. May be.IP "\-h [user@] [[user@] ..]".SH HOST ARGSArgs to pass to ssh on proxy with \-o option..IP "\-po "Host to use as proxy. (Must have mussh installed).IP "\-p [user@]".SH PROXY ARGSPrint version info and exit..IP \-V(requires openssh 3.8 or newer)Timeout setting for each session..IP "\-t "Path to shell on remote host. (Default: bash).IP "\-s "for \-H option. With \-h it means you get to type less.handy for adding 'root@' to hostnames kept in a fileForce use of 'login' name on all hosts. These can be.IP "\-L "Use 'login' when no other is specified with hostname..IP "\-l "'\-d' you'll still see which hosts failed.skip hosts where keys fail. If you use this withDo NOT fall back to passwords on any host. This will.IP \-Ponce per listing.cause scripts to be executed on duplicate hostsDo NOT make host list unique. This simply overrides the \-u flag. This will.IP \-Uonly once.specified with \-h, the host or user@host is usedonce across files specified with \-H or hostsIf you a host or user@host occurs more thanUnique. Eliminate duplicate hosts. (default).IP \-uAllow hosts' output to mingle. (default).IP \-Bwith other hosts' output.Print each hosts' output in a block without mingling.IP \-bhosts, this will save you some hassle.you do not have RSA/DSA keys for the destinationpassword or passphrase by ssh for each host. IfDo NOT load ssh\-agent. If no agent is loaded you will be prompted for a.IP \-Aagent when one is already loaded.Force loading ssh\-agent. Without this flag, mussh will not load another.IP \-aman page for more info on the \-o option..BR ssh (1) Args to pass to ssh with \-o option. See the.IP "\-o "load as many RSA/DSA identities as you'd like.loaded instead of the default identity. You canWhen \-i is used, the specified identity file(s) isLoad an identity file..IP "\-i [identity ..]"into each host.password/passphrase prompts required to logof each host. This will NOT suppress thethe command line. It also suppresses the outputThis will cancel \-d and \-v if after them onNo output unless necessary..IP \-qUse '0' (zero) for infinite. (default)Run concurrently on 'n' hosts at a time (asynchronous)..IP \-m [n]Sets ssh in debug3 mode by passing "\-v \-v \-v" to ssh..IP "\-v 3Sets ssh in debug2 mode by passing "\-v \-v" to ssh..IP "\-v 2Sets ssh in debug1 mode by passin..IP "\-v 1"Same as \-v 1.IP \-vexecuted on each host, and a lot more.hosts, the command/script as it will beIncludes all of the output from \-d1, the list of.IP "\-d 2"activity and which host is being connected to.On STDERR prints out basic actions and ssh\-agent.IP "\-d 1"Turns debug mode off..IP "\-d 0"Same as \-d 1.IP \-dPrints full help text..IP \-\-help.SH OPTIONSmore than once.and RSA/DSA keys to minimize the need to enter your password.BR ssh\-agent (1)on multiple hosts with one command. When possible mussh will use.BR ssh (1) is a shell script that allows you to execute a command or script over .B mussh.SH DESCRIPTION.B ].I scriptfile.B ] [\-C .I cmd.B > [\-c.I hostfile.B | \-H .I host....B ] <\-h .I OPTIONS.B mussh [.SH SYNOPSISmussh \- MUltihost SSH.SH NAME.TH MUSSH 1 "August 2005" Doughnut "MUltihost SSH".\".\" $Id: mussh.1,v 1.5 2006-12-26 21:57:22 doughnut Exp $ad6j?7 w v * M 2 v i 7 6   ^ ] A t B A 8 .BR ssh\-agent (1).BR ssh (1),.SH "SEE ALSO"Dave Fogarty .SH AUTHORPlease report any bugs at http://sourceforge.net/projects/mussh/.SH BUGS.B $ mussh \-h foo bar baz \-C /tmp/scriptfile.sh.IP "Loading a script from a file:".B $ mussh \-H /tmp/hostlist.txt \-c 'rpm \-e emacs'.IP "Loading a list of hosts from a file:".B $ mussh \-h foo bar baz \-c 'rpm \-e emacs' \-i ~/.ssh/my_other.key.IP "Using a specific key:".B $ mussh \-h foo bar baz \-c 'rpm \-e emacs' \-m.IP "A simple command asynchronously:".B $ mussh \-h foo bar baz \-c 'rpm \-e emacs'.IP "A simple command:".B $ mussh \-h foo bar baz .IP "The basic command:"There is an EXAMPLES file with detailed examples..SH EXAMPLESSee EXAMPLES for examples.Normally mussh will get rid of the agent when it exits..BR ssh\-agent (1).to use Assuming that you're not turning off the agent with '\-A' mussh will attempt.SH SSH\-AGENT INTERACTIONUse "ssh user@proxy 'echo $PATH'" to determine what your path is.in your PATH. To verify that it is in your path use "ssh user@proxy 'which mussh'".ForwardAgent under ssh2. Proxy server must also have mussh installedThe proxy host must have OpenSSH 2.3 or greater, or an sshd that works withmachine behind a firewall.are initially running mussh. This can be handy when you only have access to onewill be connected to from the central host rather than from the computer where you When proxying, mussh can use a single remote server to as a bastion host. All hosts .SH PROXY MODEthan once.executed on each host. May be used moreAdd file contents to list of commands to be.IP "\-C [file ..]"each host. May be used more than once.args to list of commands to be executed onAdd a command or quoted list of commands and.IP "\-c "If neither is specified, commands will be read from standard input..SH COMMAND ARGSad:MI;Y/ T %  X 6 . [ S   q C   n f 0 yCbB:~pE  "#" for comments.Files should have one host per line. UseAdd contents of file(s) to list of hosts..IP "\-H [file ..]"used more than once.Add a host to list of hosts. May be.IP "\-h [user@] [[user@] ..]".SH HOST ARGSArgs to pass to ssh on proxy with \-o option..IP "\-po "Host to use as proxy. (Must have mussh installed).IP "\-p [user@]".SH PROXY ARGSPrint version info and exit..IP \-V(requires openssh 3.8 or newer)Timeout setting for each session..IP "\-t "Path to shell on remote host. (Default: bash).IP "\-s "for \-H option. With \-h it means you get to type less.handy for adding 'root@' to hostnames kept in a fileForce use of 'login' name on all hosts. These can be.IP "\-L "Use 'login' when no other is specified with hostname..IP "\-l "'\-d' you'll still see which hosts failed.skip hosts where keys fail. If you use this withDo NOT fall back to passwords on any host. This will.IP \-Ponce per listing.cause scripts to be executed on duplicate hostsDo NOT make host list unique. This simply overrides the \-u flag. This will.IP \-Uonly once.specified with \-h, the host or user@host is usedonce across files specified with \-H or hostsIf you a host or user@host occurs more thanUnique. Eliminate duplicate hosts. (default).IP \-uAllow hosts' output to mingle. (default).IP \-Bwith other hosts' output.Print each hosts' output in a block without mingling.IP \-bhosts, this will save you some hassle.you do not have RSA/DSA keys for the destinationpassword or passphrase by ssh for each host. IfDo NOT load ssh\-agent. If no agent is loaded you will be prompted for a.IP \-Aagent when one is already loaded.Force loading ssh\-agent. Without this flag, mussh will not load another.IP \-aman page for more info on the \-o option..BR ssh (1) Args to pass to ssh with \-o option. See the.IP "\-o "load as many RSA/DSA identities as you'd like.loaded instead of the default identity. You canWhen \-i is used, the specified identity file(s) isLoad an identity file..IP "\-i [identity ..]"into each host.password/passphrase prompts required to logof each host. This will NOT suppress thethe command line. It also suppresses the outputThis will cancel \-d and \-v if after them onNo output unless necessary..IP \-qUse '0' (zero) for infinite. (default)Run concurrently on 'n' hosts at a time (asynchronous)..IP "\-m [n]"Sets ssh in debug3 mode by passing "\-v \-v \-v" to ssh..IP "\-v 3"Sets ssh in debug2 mode by passing "\-v \-v" to ssh..IP "\-v 2"Sets ssh in debug1 mode by passing "\-v" to ssh.mussh/CHANGES0000664000076400007640000000460311651627534013513 0ustar doughnutdoughnutv1.0 2011-10-25 - Another fix to CTRL-\. "HOSTS RUNNING:" was not working. - Increased efficiency when async. Thanks, Jacob Lundberg. - No more need for 'seq'. Thanks, Jacob Lundberg. - Spelling correction and other minor fixes. Thanks, Jacob Lundberg. - Support for netgroups added. Thanks, Scott Bigelow. - Debug mode fix when using proxy. Thanks, Stephane Alnet. - Numeric args more intuitive but backwards compatible. - Verbose ssh now works. v0.7 2006-12-26 - Minor bug fix avoids error when someone hits CTRL-\ and there are no .active files. - Added man page. - Added ssh timeout v0.6 (BETA) 2005-06-11: - added '-s' to ssh-agent calls so that they will work for people with non-bash shells. (thanks to Jacob) - Asynchronous mode works. - Asynchronous mode required temp files so we use mktemp to make it safer. - Blocking option in async mode. - All debug messages go to STDERR. - Invoke remote shell (default bash) explicitely. - SIGQUIT (ctrl-\) prints current/remaining host(s). - Modernized all ``'s to $()'s. v0.5 2002-07-30: - Removed need for temp files. ssh-agent is now evaled and not sourced. 2002-02-11: - Created rpm spec file. v0.4 2002-01-22: - Added Feature: -l and -L allow you to specify login name from the command line. - Malcolm found a bug where mussh would hang with "StrictHostKeyChecking=ask". Added "BatchMode=yes" and changed how mussh handles STDIN/STDERR so that those hosts will fail immediately. - Removed '-q' flag to ssh since above change makes it hide errors from ssh. - Fixed broken -U mussh flag. v0.3 2001-11-24: - Separated mussh debug (-d) from ssh debug (-v). - Added '-P' flag to never use passwords. 2001-07-20: - Greater control of debug levels. - Debug (-D) now ties into ssh -v. v0.2beta3 2001-07-19: - Modified -i to accept multiple args like '-H' or '-C'. 2001-07-12: - Fixed bug introduced in v0.2beta2 where '-' could not be used with '-C' or '-H' to get values from STDIN. 2001-06-05: - Got rid of blank line produced by hosts with no output. v0.2beta2 2001-06-03: - Fixed bug with -q. Quiet mode was turning on -d debug. - STDIN is now default for commands. The -c and -C are now optional. - Scripts and commands are now executed in the order they were given NOT reverse order. - Updated "Usage" and documentation to reflect above changes. v0.2beta 2001-06-02: - Initial Release mussh/BUGS0000664000076400007640000000010111234163024013152 0ustar doughnutdoughnutPlease report any bugs at http://sourceforge.net/projects/mussh/