5094352 [rkeene@sledge /home/rkeene/tmp]$ cat -n netexec.sh
  1 #! /bin/bash
  2 
  3 # SSH Options to use when SSH'ing
  4 SSH_CMD="${SSH_CMD:-ssh}"
  5 SCP_CMD="${SCP_CMD:-scp}"
  6 SSHOPTS="-o ForwardAgent=no -o StrictHostKeyChecking=no -o CheckHostIP=yes -o
	UserKnownHostsFile=${HOME}/.ssh/netexec_known_hosts"
  7 SSH_CMDONLY_OPTS='-q'
  8 SSH_PTY='/dev/null'
  9 spawn_rate='1'
 10 
 11 if ! echo "${SSH_CMD}" | grep '^sshpass ' >/dev/null; then
 12     SSHOPTS="${SSHOPTS} -o PasswordAuthentication=no -o BatchMode=yes -o PreferredAuthentications=publickey"
 13 fi
 14 
 15 # These variabls are used by the "cleanup" function for sanity
 16 unset TMPLOGDIR TMPSCRIPTFILE
 17 
 18 # URLs
 19 ## Default host is a group called "default"
 20 DEFAULT_HOSTS="+default"
 21 
 22 ## URL to download groups
 23 GROUPS_URLS=(
 24     "${HOME}/.netexec/groups"
 25     "/usr/local/etc/netexec/groups"
 26 )
 27 
 28 function show_help() {
 29     echo "Usage: netexec [-hrs] [-g <groups> [-f <filename>] [-z <zonename>]" >&2
 30     echo "               [-c <file>] [-t <timeout>] [-o <directory>] [-l <limit>]" >&2
 31     echo "               [-D <delay> dest|commands..." >&2
 32     echo "  -h                This help" >&2
 33     echo "  -r                Perform operations as root." >&2
 34     echo "  -s                Treat commands as script to run rather than our own" >&2
 35     echo "  -S                Start netexec in shell mode" >&2
 36     echo "  -T                Allocate tty (passes \"-tt\" to ssh)" >&2
 37     echo "  -g <groups>       Operate on a group of systems" >&2
 38     echo "  -f <filename>     Filename containing white-space seperated list of hostnames" >&2
 39     echo "                    (default is to download list from specified URL)" >&2
 40     echo "  -z <zonename>     Zone to run the commands on (usually \"global\")" >&2
 41     echo "  -c <filename>     Specifies that a file should be copied, rather than commands" >&2
 42     echo "                    run" >&2
 43     echo "  -o <directory>    Save the output from each host in one file per host in the" >&2
 44     echo "                    specified directory" >&2
 45     echo "  -t <timeout>      Global timeout for commands on remote systems (in seconds)" >&2
 46     echo "  -l <limit>        Specify a limit on the number of child processes that run" >&2
 47     echo "                    simultaneously" >&2
 48     echo "  -D <delay>        Specify a delay to be used when spawning children" >&2
 49     echo "                    (default: ${spawn_rate})" >&2
 50     echo "  dest              Specifies the destination of the copy (if -c specified)" >&2
 51     echo "  commands...       Specifies the commands to run." >&2
 52 }
 53 
 54 unset join_pids
 55 unset run_pids
 56 function cleanup() {
 57     if [ -n "${TMPSCRIPTFILE}" ]; then
 58         rm -f "${TMPSCRIPTFILE}"
 59     fi
 60 
 61     if [ -n "${TMPLOGDIR}" ]; then
 62         rm -rf "${TMPLOGDIR}"
 63     fi
 64 
 65     if [ -f "${TMP_PTY_FILE}" ]; then
 66         rm -f "${TMP_PTY_FILE}"
 67     fi
 68 
 69     if [ -f "${TMP_PTY_SHELLFILE}" ]; then
 70         rm -f "${TMP_PTY_SHELLFILE}"
 71     fi
 72 
 73     if [ -n "${TMP_SCRIPT_PID}" ]; then
 74         kill "${TMP_SCRIPT_PID}"
 75     fi
 76 
 77     if [ "${opt_netexecshell}" = '1' ]; then
 78         if [ -n "${join_pids[*]}" ]; then
 79             kill -9 "${join_pids[@]}"
 80         fi
 81 
 82         if [ -n "${run_pids[*]}" ]; then
 83             kill -9 "${run_pids[@]}"
 84         fi
 85 
 86         if [ -n "${idxlist[*]}" ]; then
 87             local pids
 88 
 89             ## Close all file descriptors
 90             unset pids
 91             for idx in "${idxlist[@]}"; do
 92                 (
 93                     eval "exec ${cmdfd[$idx]}<&-"
 94                     eval "exec ${resfd[$idx]}<&-"
 95                 ) &
 96                 pids[${idx}]="$!"
 97             done
 98 
 99             ## Wait for closing to terminate
100             wait "${pids[@]}"
101         fi
102 
103         ## Save history buffer to disk
104         history -w ~/.netexec_history >/dev/null 2>/dev/null
105     fi
106 }
107 
108 function timeout() {
109     local timeout timein childpid
110 
111     timeout="$1"
112     shift
113 
114     "$@" &
115     childpid="$!"
116     
117     for ((timein=0; timein < $timeout; timein++)) {
118         kill -0 "${childpid}" >/dev/null 2>/dev/null || break
119 
120         sleep 1
121     }
122 
123     if kill -0 "${childpid}" >/dev/null 2>/dev/null; then
124         kill -9 "${childpid}"
125     fi
126 
127     wait "${childpid}"
128 }
129 
130 # 1. Register a cleanup handler should we be asked to terminate
131 trap "echo \"Cleaning up...\"; cleanup; exit 1" SIGINT
132 
133 # 2. Process user aguments
134 ## 2.b. Iterate over every option and check to see if we understand it
135 copyfile=""
136 usesudo="0"
137 usescript="0"
138 maxchildren='0'
139 outdir=""
140 groups=""
141 opt_netexecshell='0'
142 while getopts 'hrsSTz:f:g:c:o:t:l:D:' ch; do
143     case "${ch}" in
144         h)
145             show_help 2>&1
146             exit 0
147             ;;
148         r)
149             usesudo="1"
150             ;;
151         s)
152             usescript="1"
153             ;;
154         S)
155             opt_netexecshell='1'
156             ;;
157         T)
158             SSH_CMDONLY_OPTS="${SSH_CMDONLY_OPTS} -tt"
159 
160             if [ "$(uname -s)" = "SunOS" ]; then
161                 # On Solaris, we have to create a fake tty to use otherwise ssh produces an error:
162                 # tcgetattr: No such device or address
163                 TMP_PTY_FILE="${TMPDIR:-/tmp}/netexec-pty.$$${RANDOM}${RANDOM}${RANDOM}"
164                 TMP_PTY_SHELLFILE="${TMPDIR:-/tmp}/netexec-pty-shell.$$${RANDOM}${RANDOM}${RANDOM}"
165                 cat << \_EOF_ > "${TMP_PTY_SHELLFILE}"
166 #! /bin/bash
167 
168 echo "SSH_TTY=`tty`"
169 echo "TMP_SCRIPT_PID=$$"
170 
171 while true; do
172     sleep 86400
173 done
174 _EOF_
175                 chmod 700 "${TMP_PTY_SHELLFILE}"
176 
177                 SHELL="${TMP_PTY_SHELLFILE}" script "${TMP_PTY_FILE}" </dev/null >/dev/null 2>/dev/null &
178 
179                 for try in {1..10}; do
180                     SSH_PTY="$(cat "${TMP_PTY_FILE}" | sed "s|$(echo -e '\r')||" | grep '^SSH_TTY=' | cut -f 2- -d = |
	head -1)"
181                     TMP_SCRIPT_PID="$(cat "${TMP_PTY_FILE}" | sed "s|$(echo -e '\r')||" | grep '^TMP_SCRIPT_PID=' | cut
	-f 2 -d = | head -1)"
182 
183                     if [ -n "${SSH_PTY}" -a -n "${TMP_SCRIPT_PID}" ]; then
184                         break
185                     fi
186 
187                     sleep "${try}"
188                 done
189 
190                 if [ -z "${SSH_PTY}" ]; then
191                     SSH_PTY='/dev/null'
192                 fi
193             fi
194 
195             ;;
196         z)
197             target_zonename="${OPTARG}"
198             ;;
199         f)
200             hostsfile="${OPTARG}"
201 
202             if [ -z "${hostsfile}" ]; then
203                 echo "Hosts file (-f) option may not be empty" >&2
204 
205                 exit 1
206             fi
207             ;;
208         g)
209             groups="${OPTARG}"
210             ;;
211         c)
212             copyfile="${OPTARG}"
213             ;;
214         o)
215             outdir="$(cd "${OPTARG}" 2>/dev/null && pwd)"
216             if [ -z "${outdir}" ]; then
217                 echo "Specified output directory \"$1\" does not exist, aborting." >&2
218 
219                 exit 1
220             fi
221             ;;
222         t)
223             globaltimeout="${OPTARG}"
224             ;;
225         l)
226             maxchildren="${OPTARG}"
227 
228             if [ "${maxchildren}" -lt '0' ]; then
229                 echo "Specified limit (${maxchildren}) must be integer greater than or" >&2
230                 echo "equal to 0. Aborting." >&2
231 
232                 exit 1
233             fi
234             ;;
235         D)
236             spawn_rate="${OPTARG}"
237             ;;
238         *)
239             echo "Unknown option (${ch})" >&2
240 
241             show_help
242 
243             exit 1
244             ;;
245     esac
246 done
247 
248 shift $[${OPTIND} - 1]
249 
250 ## 2.c. Verify argument sanity
251 ### 2.c.i. Verify that we were asked to do something productive
252 if [ -z "$*" ]; then
253     if [ "${opt_netexecshell}" != '1' ]; then
254         if [ -z "${copyfile}" ]; then
255             echo "No commands to run, exiting." >&2
256         else
257             echo "Destination not specified, exiting." >&2
258         fi
259 
260         show_help
261 
262         exit 1
263     fi
264 else
265     if [ "${opt_netexecshell}" = '1' ]; then
266         echo "No commands may be specified in shell mode" >&2
267 
268         exit 1
269     fi
270 fi
271 
272 ### 2.c.ii. Verify that we support the operation
273 #### 2.c.ii.A. No copy and root, currently
274 if [ "${usesudo}" = "1" -a -n "${copyfile}" ]; then
275     echo "Currently we do not support copying files as root." >&2
276 
277     exit 1
278 fi
279 
280 #### 2.c.ii.B. No copying with shell mode
281 if [ "${opt_netexecshell}" = '1' -a -n "${copyfile}" ]; then
282     echo "File copy is not supported in shell mode" >&2
283 
284     exit 1
285 fi
286 
287 #### 2.c.ii.C. Shell mode does not support limited children
288 if [ "${maxchildren}" != '0' -a "${opt_netexecshell}" = '1' ]; then
289     echo "Process limit not supported in shell mode" >&2
290 
291     exit 1
292 fi
293 
294 ### 2.c.iii. If arguments require copying, update destination with arguments
295 if [ -n "${copyfile}" ]; then
296     copyfiledest="$*"
297 fi
298 
299 ### 2.c.iv. If arguments require running a command, construct a script that we
300 ###         can copy to the host and run to ensure a consistent experience
301 if [ -z "${copyfile}" -a "${opt_netexecshell}" = '0' ]; then
302     #### 2.c.iv.(a). Create script
303     TMPSCRIPTFILE="${TMPDIR:-/tmp}/netexec-script-$$${RANDOM}${RANDOM}${RANDOM}.deleteme"
304     touch "${TMPSCRIPTFILE}" || exit 1
305     chmod 700 "${TMPSCRIPTFILE}" || exit 1
306 
307     #### 2.c.iv.(b). Create script header
308     cat << \__EOF__ > "${TMPSCRIPTFILE}"
309 #! /bin/bash
310 
311 # Abort on failure
312 set -e
313 
314 # Setup sane path
315 PATH="/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin:/usr/local/sbin:/usr/nsh/bin:/usr/sfw/bin:/opt/sfw/bin:/usr/ucb"
316 export PATH
317 
318 # Setup sane umask
319 umask 022
320 
321 __EOF__
322 
323     #### 2.c.iv.(c). Put user commands in the script
324     if [ "${usescript}" = '0' ]; then
325         echo "$*" >> "${TMPSCRIPTFILE}"
326     else
327         echo '' >> "${TMPSCRIPTFILE}"
328         echo '# Disable abort on error, most scripts do not expect it' >> "${TMPSCRIPTFILE}"
329         echo 'set +e' >> "${TMPSCRIPTFILE}"
330         echo '' >> "${TMPSCRIPTFILE}"
331         cat "$@" >> "${TMPSCRIPTFILE}"
332     fi
333 
334     #### 2.c.iv.(d). Update script to declare victory if nothing failed
335     cat << \__EOF__ >> "${TMPSCRIPTFILE}"
336 
337 exit 0
338 __EOF__
339 fi
340 
341 ## 2.d. If we are doing this as root, use sudo
342 if [ "${usesudo}" = "1" ]; then
343     call_sudo='sudo'
344 else
345     call_sudo=''
346 fi
347 
348 # 3. Determine which hosts to operate on
349 if [ -n "${hostsfile}" ]; then
350     ## 3.a. If a file was specified, load its contents into memory
351     if echo "${hostsfile}" | grep '/' >/dev/null || [ -e "${hostsfile}" ]; then
352         ### 3.a.i. If the file specified exists or has a '/' in it treat it as a filename..
353         HOSTS="$(cat "${hostsfile}")"
354     else
355         ### 3.a.ii. ... Otherwise treat it as a list of hosts
356         HOSTS="${hostsfile}"
357     fi
358 else
359     ## 3.b. Otherwise, pull in all hosts (unless a group file was specified, then pull in none)
360     if [ -z "${groups}" ]; then
361         HOSTS="${DEFAULT_HOSTS}"
362     fi
363 fi
364 
365 ## 3.c. Pull in groups
366 for level in 1 2 3 4 5 6 7 8 9 10; do
367     for group in ${groups}; do
368         for groups_url in "${GROUPS_URLS[@]}" __notfound__; do
369             if [ "${groups_url}" = '__notfound__' ]; then
370                 echo "Unable to download group for \"${group}\", aborting." >&2
371 
372                 exit 1
373             fi
374 
375             group_url="${groups_url}/${group}"
376 
377             if [ -f "${group_url}" ]; then
378                 HOSTS="${HOSTS} $(cat "${group_url}")"
379             else
380                 HOSTS="${HOSTS} $(wget --no-check-certificate -O - "${group_url}" 2>/dev/null)" || continue
381             fi
382 
383             break
384         done
385     done
386 
387     ## 3.d. Normalize list of hosts
388     HOSTS="$(echo "${HOSTS} " | sed 's@#.*@@' | tr ' ' "\n" | grep -v '^ *$' | dd conv=lcase 2>/dev/null | sort -u)"
389 
390     if ! echo "${HOSTS}" | grep '^+' >/dev/null; then
391         break
392     fi
393 
394     groups="$(echo "${HOSTS}" | grep '^+' | sed 's@^+@@')"
395     HOSTS="$(echo "${HOSTS}" | grep -v '^+')"
396 done
397 
398 # 4. Create a temporary directory to store our results
399 TMPLOGDIR="${TMPDIR:-/tmp}/netexec-logdir-$$${RANDOM}${RANDOM}${RANDOM}"
400 export TMPLOGDIR
401 mkdir "${TMPLOGDIR}" || exit 1
402 chmod 700 "${TMPLOGDIR}" || exit 1
403 
404 # 5. Start up a temporary SSH agent with our service account's private keys
405 ## NOT DONE
406 
407 # 6. Process each host
408 if [ -n "${TMPSCRIPTFILE}" ]; then
409     script="$(cat "${TMPSCRIPTFILE}")"
410     script_quoted="$(set | grep '^script=' | cut -f 2- -d '=' | sed "s@'@'\"'\"'@g")"
411 fi
412 
413 if [ "${opt_netexecshell}" = '1' ]; then
414     numhosts="$(HOSTS=(${HOSTS}); echo "${#HOSTS[@]}")"
415     idx=0
416 fi
417 
418 for host in ${HOSTS}; do
419     (
420         ## 6.c. Figure out the name of the zone, if specified
421         if [ -n "${target_zonename}" ]; then
422             zonename="$(timeout 120 ${SSH_CMD} ${SSHOPTS} ${SSH_CMDONLY_OPTS} "${host}" zonename 2>/dev/null | tail -1)"
423 
424             if [ "${zonename}" != "${target_zonename}" ]; then
425                 exit 0
426             fi
427         fi
428 
429         ## 6.d. Perform operation
430         if [ -n "${copyfile}" ]; then
431             mode='copy'
432         elif [ "${opt_netexecshell}" = '1' ]; then
433             mode="shell"
434         else
435             mode="ssh"
436         fi
437         case "${mode}" in
438             ssh)
439                 ### 6.d.ii. Run our script
440                 if [ -z "${globaltimeout}" ]; then
441                     ${SSH_CMD} ${SSHOPTS} ${SSH_CMDONLY_OPTS} "${host}" "/bin/bash -c 'script=${script_quoted};
	PATH=\"/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin:/usr/local/sbin:/usr/nsh/bin:/usr/sfw/bin:/opt/sfw/bin:/usr/ucb\";
	export PATH; ${call_sudo} /bin/bash -c \"\${script}\"'" < "${SSH_PTY}"
442                 else
443                     timeout "${globaltimeout}" ${SSH_CMD} ${SSHOPTS} ${SSH_CMDONLY_OPTS} "${host}" "/bin/bash -c
	'script=${script_quoted};
	PATH=\"/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin:/usr/local/sbin:/usr/nsh/bin:/usr/sfw/bin:/opt/sfw/bin:/usr/ucb\";
	export PATH; ${call_sudo} /bin/bash -c \"\${script}\"'" < "${SSH_PTY}"
444                 fi
445                 retval="$?"
446                 ;;
447             copy)
448                 if [ -z "${globaltimeout}" ]; then
449                     ${SCP_CMD} ${SSHOPTS} "${copyfile}" "${host}:${copyfiledest}"
450                 else
451                     timeout "${globaltimeout}" scp ${SSHOPTS} "${copyfile}" "${host}:${copyfiledest}"
452                 fi
453                 retval="$?"
454                 ;;
455             shell)
456                 infile="${TMPLOGDIR}/${host}@in"
457                 outfile="${TMPLOGDIR}/${host}@out"
458 
459                 mkfifo "${infile}" "${outfile}"
460 
461                 ${SSH_CMD} ${SSHOPTS} ${SSH_CMDONLY_OPTS} "${host}" "${call_sudo} /bin/sh -c 'test -x /bin/bash && exec
	/bin/bash -s; exec /bin/sh -s' 2>&1" < "${infile}" > "${outfile}"
462                 retval="$?"
463 
464                 rm -f "${infile}" "${outfile}"
465 
466                 touch "${infile}" "${outfile}"
467                 ;;
468         esac
469 
470         if [ "${retval}" != "0" ]; then
471             echo "[error] Exited with non-zero return value: ${retval}"
472 
473             exit 1
474         fi
475     ) >"${TMPLOGDIR}/${host}" 2>&1 &
476 
477     if [ "${opt_netexecshell}" = '1' ]; then
478         idx=$[$idx + 1]
479 
480         echo -ne "\rStarting SSH sessions: ${idx}/${numhosts}"
481     fi
482 
483     # If we are using an SSH agent then avoid overloading it by limiting
484     # rate of children spawned
485     if [ "${opt_netexecshell}" != '1' ]; then
486         if [ "${spawn_rate}" != '0' -a -n "${spawn_rate}" ]; then
487             sleep "${spawn_rate}"
488         fi
489     fi
490 
491     # If we have been asked to limit number of children, do so
492     if [ "${maxchildren}" -gt '0' ]; then
493         while true; do
494             numchildren="$(jobs -p | wc -l | awk '{ print $1 }')"
495             if [ "${numchildren}" -lt "${maxchildren}" ]; then
496                 break
497             fi
498 
499             sleep 5
500         done
501     fi
502 done
503 
504 # 7. Wait for SSH processes to complete
505 
506 if [ "${opt_netexecshell}" = '1' ]; then
507     function run_remote_command() {
508         local cmdfd resfd cmd_quoted
509         local marker result
510 
511         cmdfd="$1"
512         resfd="$2"
513         cmd_quoted="$3"
514 
515         marker="${RANDOM}${RANDOM}${RANDOM}${RANDOM}${RANDOM}${RANDOM}${RANDOM}${RANDOM}${RANDOM}"
516         echo "echo -n '${marker}_START'; eval ${cmd_quoted}; echo '${marker}_END'" >&${cmdfd}
517 
518         result="$(sed "s@${marker}_START@@;/${marker}_END\$/ {s@${marker}_END\$@@;q}" <&${resfd})"
519 
520         echo "${result}"
521     }
522 
523     function hostname_to_idx() {
524         local system
525         local idx name
526 
527         system="$1"
528 
529         for idx in "${idxlist[@]}"; do
530             name="${names[${idx}]}"
531             case "${system}" in
532                 ~*)
533                     system_re="$(echo "${system}" | cut -c 2-)"
534                     if echo "${name}" | grep "${system_re}" >/dev/null 2>/dev/null; then
535                         echo "${idx}"
536                     fi
537                     ;;
538                 *)
539                     if [ "${name}" = "${system}" ]; then
540                         echo "${idx}"
541                         return
542                     fi
543                     ;;
544             esac
545         done
546     }
547 
548     echo ""
549 
550     idx=-1
551     for host in ${HOSTS}; do
552         idx=$[$idx + 1]
553 
554         cmdfile="${TMPLOGDIR}/${host}@in"
555         resfile="${TMPLOGDIR}/${host}@out"
556 
557         echo -ne "\rConnecting to sockets: $[$idx + 1]/$numhosts"
558 
559         for try in {0..9} __failed__; do
560             if [ "${try}" = '__failed__' ]; then
561                 continue
562             fi
563 
564             if [ -e "${cmdfile}" -a -e "${resfile}" ]; then
565                 if [ -p "${cmdfile}" -a -p "${resfile}" ]; then
566                     break
567                 fi
568 
569                 continue
570             fi
571 
572             sleep $[${try} * 2]
573         done
574 
575         idxlist[$idx]="${idx}"
576         names[$idx]="${host}"
577         logfiles[$idx]="${TMPLOGDIR}/${host}@tmp"
578         cmdfiles[$idx]="${cmdfile}"
579         resfiles[$idx]="${resfile}"
580         cmdfd[$idx]=$[$idx + 32]
581         resfd[$idx]=$[$idx + 32 + ${numhosts}]
582 
583         eval "exec ${cmdfd[$idx]}<&-"
584         eval "exec ${resfd[$idx]}<&-"
585         eval "exec ${cmdfd[$idx]}>\"${cmdfile}\""
586         eval "exec ${resfd[$idx]}<\"${resfile}\""
587 
588         if [ "${spawn_rate}" != '0' -a -n "${spawn_rate}" ]; then
589             sleep "${spawn_rate}"
590         fi
591     done
592     echo ""
593 
594     echo -n "Verifying connectivity: "
595 
596     unset join_pids
597     for idx in "${idxlist[@]}"; do
598         (
599             cmdfile="${cmdfiles[$idx]}"
600             resfile="${resfiles[$idx]}"
601             marker="${RANDOM}${RANDOM}${RANDOM}${RANDOM}"
602             echo "PS1=''; echo; echo ${marker}" >&${cmdfd[$idx]}
603 
604             failed='0'
605             for try in {0..9} __failed__; do
606                 if [ "${try}" = '__failed__' ] || [ ! -p "${cmdfile}" -o ! -p "${resfile}" ]; then
607                     failed='1'
608 
609                     break
610                 fi
611 
612                 read -t 10 -r -u ${resfd[${idx}]} line 2>/dev/null
613 
614                 if [ "${line}" = "${marker}" ]; then
615                     break
616                 fi
617 
618                 sleep $[${try} * 2]
619             done
620 
621             if [ "${failed}" = '0' ]; then
622                 echo -n " ${names[$idx]}..."
623             else
624                 echo -n " ${names[$idx]}(FAILED)..."
625                 rm -f "${cmdfile}"
626                 rm -f "${resfile}"
627             fi
628         ) &
629         join_pids[${idx}]="$!"
630     done
631 
632     wait "${join_pids[@]}"
633     unset join_pids
634     echo ""
635 
636     history -r ~/.netexec_history >/dev/null 2>/dev/null
637 
638     working_idxlist=("${idxlist[@]}")
639     while true; do
640         IFS='' read -r -e -p 'netexec$ ' cmd || break
641 
642         # Normalize command a bit
643         cmd="$(echo "${cmd}" | sed 's@^ *@@')"
644 
645         # Ignore empty commands
646         if [ -z "${cmd}" ]; then
647             continue
648         fi
649 
650         # Pull command from history, if appropriate
651         if echo "${cmd}" | grep '^![0-9][0-9]*$' >/dev/null; then
652             cmd="$(history -p "${cmd}")"
653 
654             echo "netexec>> ${cmd}"
655         fi
656 
657         # Write command to history
658         history -s "${cmd}"
659 
660         # Handle local overrides
661         unset run_idxlist
662         run_idxlist=("${working_idxlist[@]}")
663         case "${cmd}" in
664             clear|clear\ *|history|history\ *)
665                 eval "${cmd}"
666 
667                 continue
668                 ;;
669             exit)
670                 break
671                 ;;
672             vi|vi\ *|sudo\ vi|sudo\ vi\ *)
673                 template_idx='0'
674 
675                 if echo "${cmd}" | grep '^sudo ' >/dev/null; then
676                     vi_sudo='1'
677                     vi_sudo_cmd='sudo '
678                 else
679                     vi_sudo='0'
680                     vi_sudo_cmd=''
681                 fi
682 
683                 remote_filename="$(echo "${cmd}" | sed 's@^\(sudo\)* *vi *@@;s@ *$@@')"
684 
685                 if [ -z "${remote_filename}" ]; then
686                     echo "Usage: vi <filename>" >&2
687 
688                     continue
689                 fi
690 
691                 contents="$(run_remote_command "${cmdfd[${template_idx}]}" "${resfd[${template_idx}]}"
	"${vi_sudo_cmd}cat ${remote_filename} 2>/dev/null")"
692 
693                 tmp_filename="${TMPDIR:-/tmp}/netexec-vi-$$-$(echo "${contents}" | openssl sha1 | sed 's@.*= *@@')"
694 
695                 if [ -e "${tmp_filename}" ]; then
696                     echo -n 'netexec>> Edit session for this file is already in progress, continue (YES/no) ? '
697                     read no
698                     if [ "${no}" = 'no' ]; then
699                         rm -f "${tmp_filename}"
700                     fi
701                 fi
702 
703                 if [ ! -e "${tmp_filename}" ]; then
704                     echo "${contents}" > "${tmp_filename}"
705                 fi
706 
707                 vi "${tmp_filename}"
708 
709                 echo -n 'netexec>> Ready to deploy (yes/NO) ? '
710                 read yes
711                 if [ "${yes}" != 'yes' ]; then
712                     echo "netexec>> NOT deploying file."
713 
714                     continue
715                 fi
716 
717                 contents="$(cat "${tmp_filename}")"
718                 rm -f "${tmp_filename}"
719 
720                 echo "netexec>> Deploying ${remote_filename}"
721 
722                 tmp_marker="$$${RANDOM}${RANDOM}${RANDOM}${RANDOM}"
723 
724                 if [ "${vi_sudo}" = '1' ]; then
725                     remote_filename_unquoted="$(eval echo "${remote_filename}")"
726                     remote_filename_quoted="$(set | grep '^remote_filename_unquoted=' | cut -f 2- -d '=')"
727 
728                     vi_redirect="| sudo bash -c \"cat > ${remote_filename_quoted}\""
729                 else
730                     vi_redirect="> ${remote_filename}"
731                 fi
732 
733                 cmd='cat << \__EOF__'"${tmp_marker}"'__  '"${vi_redirect}"'
734 '"${contents}"'
735 __EOF__'"${tmp_marker}"'__
736 '
737                 ;;
738             passwd|passwd\ *)
739                 echo "Not currently supported"
740                 continue
741                 ;;
742             !cp|!cp\ *)
743                 echo "Not currently supported"
744                 continue
745                 ;;
746             !remove\ *)
747                 system="$(echo "${cmd}" | awk '{ print $2 }')"
748                 for idx in "${idxlist[@]}"; do
749                     name="${names[${idx}]}"
750                     if [ "${name}" = "${system}" ]; then
751                         unset idxlist[$idx]
752                     fi
753                 done
754 
755                 continue
756                 ;;
757             !run\ *)
758                 system="$(echo "${cmd}" | cut -f 2 -d ' ')"
759                 cmd="$(echo "${cmd}" | cut -f 3- -d ' ')"
760 
761                 unset run_idxlist
762                 run_idxlist=()
763 
764                 tmpidx=-1
765                 for add_idx in $(hostname_to_idx "${system}"); do
766                     tmpidx=$[$tmpidx + 1]
767                     run_idxlist[$tmpidx]="${add_idx}"
768                 done
769                 ;;
770             !subset|!subset\ *)
771                 systems="$(echo "${cmd}" | sed 's@^!subset *@@')"
772 
773                 unset working_idxlist
774                 working_idxlist=()
775 
776                 if [ -z "${systems}" ]; then
777                     working_idxlist=("${idxlist[@]}")
778                 else
779                     tmpidx=-1
780                     for system in $systems; do
781                         for add_idx in $(hostname_to_idx "${system}"); do
782                             tmpidx=$[$tmpidx + 1]
783                             working_idxlist[$tmpidx]="${add_idx}"
784                         done
785                     done
786                 fi
787 
788                 continue
789                 ;;
790             !help)
791                 echo "NETEXEC SHELL MODE COMMANDS:"
792                 echo "      clear                Clear the screen"
793                 echo "      history              Display command history buffer"
794                 echo "      exit                 Terminate netexec"
795                 echo "      vi                   Edit a file and distribute it"
796                 echo "      passwd               Change a user's password"
797                 echo "      !subset <hosts...>   Work with a subets of hosts"
798                 echo "      !cp <src> <dest>     Copy a file from the local system to all systems"
799                 echo "      !remove <host>       Remove a host from list of hosts"
800                 echo "      !run <host> <cmd>    Run a command on a single host"
801                 echo "      !help                This help"
802                 continue
803                 ;;
804         esac
805 
806         # Ensure no "!" commands get transmitted
807         case "${cmd}" in
808             !*)
809                 echo "Invalid netexec command ${cmd}" >&2
810                 continue
811                 ;;
812         esac
813 
814         # Handle local pipe mode
815         if echo "${cmd}" | grep '!|' >/dev/null; then
816             local_cmd="$(echo "${cmd}" | sed 's@^.*!|@@')"
817             cmd="$(echo "${cmd}" | sed 's@!|.*$@@')"
818         else
819             local_cmd='cat'
820         fi
821 
822         # Quote our command string
823         cmd_quoted="$(set | grep '^cmd=' | cut -f 2- -d '=')"
824 
825         if [ -z "${run_idxlist[*]}" ]; then
826             continue
827         fi
828 
829         # Run and collect results
830         unset run_pids
831         for idx in "${run_idxlist[@]}"; do
832             (
833                 cmdfile="${cmdfiles[$idx]}"
834                 resfile="${resfiles[$idx]}"
835                 if [ ! -p "${cmdfile}" -o ! -p "${resfile}" ]; then
836                     echo "SSH DIED"
837 
838                     exit 1
839                 fi
840 
841                 run_remote_command "${cmdfd[${idx}]}" "${resfd[${idx}]}" "${cmd_quoted}"
842             ) > "${logfiles[${idx}]}" &
843             run_pids[${idx}]="$!"
844         done
845 
846         wait "${run_pids[@]}"
847         unset run_pids
848 
849         for idx in "${run_idxlist[@]}"; do
850             name="${names[${idx}]}"
851             logfile="${logfiles[${idx}]}"
852 
853             result="$(cat "${logfile}" 2>/dev/null)"
854 
855             if [ -z "${result}" ]; then
856                 result="(no output)"
857             fi
858 
859             echo "${result}" | sed 's|^|'"${name}"': |'
860         done | eval "${local_cmd}"
861     done
862 
863     # Cleanup
864     cleanup
865 
866     # Terminate
867     exit 0
868 fi
869 
870 wait
871 
872 # 8. Print the results, let globbing sort the hostnames
873 (
874     cd "${TMPLOGDIR}" || exit 1
875     for host in ${HOSTS}; do
876         if [ -f "${host}" ]; then
877             if [ -s "${host}" ]; then
878                 cat "${host}" | sed "s|^|${host}: |"
879             else
880                 echo "${host}: (no results)"
881             fi
882 
883             if [ -n "${outdir}" ]; then
884                 cat "${host}" > "${outdir}/${host}"
885             fi
886         else
887             echo "${host}: [error] No results found!"
888 
889             if [ -n "${outdir}" ]; then
890                 echo '[error] No results found!' > "${outdir}/${host}"
891             fi
892         fi
893     done
894 )
895 
896 # 9. Clean up
897 cleanup
898 
899 # 10. Declare victory
900 exit 0
5094353 [rkeene@sledge /home/rkeene/tmp]$

Click here to go back to the directory listing.
Click here to download this file.
last modified: 2019-03-29 16:04:18