source: trunk/tssh/tssh @ 392

Last change on this file since 392 was 392, checked in by g7moreau, 5 years ago
  • Help and small bug on ssh_option
File size: 9.5 KB
Line 
1#!/bin/bash
2#
3# 2014/03/26 Gabriel Moreau <Gabriel Moreau(A)univ-grenoble-alpes.fr> - Initial release
4#
5# From http://hd-recording.at/dokuwiki/doku.php?id=linux:tmux#tssh
6
7# Clean when Ctrl^C
8trap '[ -n "${base_path}" -a -d "/tmp/${base_path}" ] && rm -rf "/tmp/${base_path}"; exit 4;' QUIT INT TERM
9
10export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin
11export LANG=C
12
13
14function usage() {
15   cat <<END_USAGE
16NAME
17   $(basename $0) - tmux cluster ssh
18
19SYNOPSIS
20   $0 [-w number] [-f] [-v] [-c remote_cmd] [-o ssh_option] <host1> <host2> <clusterssh class>... <hostM>- <hostN>+
21
22OPTIONS
23   -w             windows to open (integer, default 16)
24   -o ssh_option  option to pass to ssh
25   -f             fast, no nmap scan to eliminate sleeping computer
26   -v             verbose
27   -c remote_cmd  launch the remote command on hosts and exit
28   -h             help
29
30DESCRIPTION
31   tssh can be use to launch terminal on many computer in parallel with tmux
32   multiplexer and ssh.
33   The tmux windows is splitted automatically.
34   If you need more computers on the same windows, you can zoom in and out
35   under gnome terminal with Ctrl- or Ctrl+.
36   This must be done before launching tssh.
37   
38   On the command line, you can put host, login@host, clusterssh class.
39   A host or a class can be remove from the list with a dash append
40   and force to be in this one with a plus append.
41   Example with the cluster ssh config below:
42   
43    tssh all team- node005 laptop04+
44
45   Is equivalent to:
46 
47    tssh srv-mail srv-dns srv-imap srv-web srv-proxy \\
48      node001 node002 node003 node004 \\
49      node101 node102 node103 node104 \\
50      node005 laptop04
51
52   The control command for tmux is Ctrl^b.
53   You can switch from broadcast to a local machine with Ctrl^b Ctrl^b
54   and move between machine with Ctrl^b ArrowKey.
55
56DEPENDS
57   On Debian, you need the package
58
59    apt-get install tmux ncurses-bin wamerican nmap
60
61   wamerican (or wfrench...) is used to choose a random word in the file /usr/share/dict/words
62   for each new tmux session.
63
64   ncurses-bin is required for the tput command
65   to automatically split your terminal into several small panels.
66   nmap is only used for dynamic DNS domain and dynamic scan.
67   This is not mandatory for general use.
68
69   By default, tssh use tput to know the number of columns and lines of your terminal.
70   It takes 10 lines and 40 columns for each windows by default.
71   If tput is not installed, the default is 16 windows...
72
73CONFIGURATION
74   The clusterssh config file ~/.csshrc is a key values file.
75   The "clusters" is mandatory for clusterssh (not tssh) and define the other keys.
76   Values could be computer list or other key...
77   
78    clusters = all server s1 s2 s3 node n1 n2 team switch
79    all = server node team
80    server = s1 s2
81    node = n1 n2
82    s1 = srv-mail srv-dns srv-imap
83    s2 = srv-web srv-proxy
84    n1 = node001 node002 node003 node004
85    n2 = node101 node102 node103 node104
86    team = pc01 pc06 laptop04 laptop05 laptop09
87    switch = root@switch01 root@switch05 root@switch17
88
89   The tssh config file (~/.tsshrc) can be use change the default parameters.
90   
91    #export split_number=16
92    export dyn_domain='mycompagny.local'
93    #export ssh_option=''
94    #export fast='yes'
95    #export verbose='yes'
96
97AUTHOR
98   Gabriel Moreau
99
100COPYRIGHT
101   Copyright (C) 2014-2019, LEGI UMR 5519 / CNRS UGA G-INP, Grenoble, France
102   Licence : GNU GPL version 2 or later
103END_USAGE
104   }
105
106export remote_command=''
107export ssh_option=''
108export split_number=16
109if which tput > /dev/null
110then
111   export split_number=$(( ($(tput  lines)/ 10) * ($(tput  cols)/ 40) ))
112fi
113
114export dyn_domain=''
115if [ -e "${HOME}/.tsshrc" ]
116then
117   . "${HOME}/.tsshrc"
118fi
119
120# get options
121if [ $# -eq 0 ]; then usage; exit 1; fi 
122while getopts "w:o:c:fvh" options
123do
124   case ${options} in
125      w)
126         if echo ${OPTARG} | egrep -q '^[[:digit:]]+$' && [ ${OPTARG} -gt 0 ]
127         then
128            export split_number=${OPTARG}
129         else
130            usage
131            exit 2
132         fi
133         ;;
134      c)
135         if echo ${OPTARG} | egrep -q '[[:alpha:]]'
136         then
137            export remote_command=${OPTARG}
138         else
139            usage
140            exit 2
141         fi
142         ;;
143      o)
144         if echo ${OPTARG} | egrep -q '[[:alpha:][:digit:]]'
145         then
146            export ssh_option=${OPTARG}
147         else
148            usage
149            exit 2
150         fi
151         ;;
152      f)
153         export fast='yes'
154         ;;
155      v)
156         export verbose='yes'
157         ;;
158      h|*)
159         usage
160         exit 3
161         ;;
162   esac
163done
164shift $((OPTIND - 1))
165[[ $1 = "--" ]] && shift
166
167cd /tmp/
168export base_path=$(mktemp -d tssh.XXXXXX)
169touch "/tmp/${base_path}/master"
170touch "/tmp/${base_path}/master-"
171touch "/tmp/${base_path}/master+"
172touch "/tmp/${base_path}/master--"
173touch "/tmp/${base_path}/master++"
174
175get_host_list () {
176   local cluster
177   local default_mode=''
178
179   # set local mode
180   if echo $1 | grep -- ^--mode=
181   then
182      default_mode=$(echo $1 | grep -- ^--mode= | cut -f 2 -d '=')
183      shift
184   fi
185
186   for host in $*
187   do
188      local mode=${default_mode}
189      local last_char="${host: -1}"
190      if [ "${last_char}" == "-" -o "${last_char}" == "+" ]
191      then
192         mode="${last_char}"
193         host="${host:0:${#host}-1}"
194      fi
195
196      # short host without login part if any
197      local justhost=${host#*@}
198     
199      cluster=$(grep "^${justhost}\b" ${HOME}/.csshrc | cut -f 2 -d '=' | sed -e 's/^[[:space:]]*//;')
200      if [ "${cluster}" == "" ]
201      then
202         # just a host to scan and add
203         if [ "${fast}" != 'yes' -a "${mode}" != '-' ]
204         then
205            # test if exists host
206            if host ${justhost} | grep -q 'not found'
207            then
208               [ "${verbose}" == 'yes' ] && echo "Warning: ${justhost} does not exists"
209               continue
210            fi
211            if ! nmap -p 22 -sT -PN ${justhost} | grep -q '\bopen\b'
212            then
213               if host ${justhost}.${dyn_domain} | grep -q 'not found' || ! nmap -p 22 -sT -PN ${justhost}.${dyn_domain} | grep -q '\bopen\b'
214               then
215                  [ "${verbose}" == 'yes' ] && echo "Warning: ${justhost} is down"
216                  continue
217               else
218                  [ "${verbose}" == 'yes' ] && echo "Warning: remove ssh key of ${justhost}.${dyn_domain}"
219                  host=${justhost}.${dyn_domain}
220                  ssh-keygen -q -R $(LANG=C host ${justhost} | awk '{print $4}')
221               fi
222            fi
223         fi
224         [ "${verbose}" == 'yes' ] && echo "Warning: add ${host} on list with mode ${mode}"
225         echo "${host}" >> "/tmp/${base_path}/master${mode}"
226      else
227         # cluster, jump in a recursive mode
228         [ "${verbose}" == 'yes' ] && echo "Warning: recursive call for cluster ${justhost} (${cluster}), with mode ${mode}"
229         cluster=$(get_host_list --mode=${mode} "${cluster}")
230      fi
231   done
232   }
233declare -fx get_host_list
234
235get_host_list $@
236cat "/tmp/${base_path}/master+" >> "/tmp/${base_path}/master"
237for f in $(grep . "/tmp/${base_path}/master-")
238do
239   egrep "^${f}$" "/tmp/${base_path}/master+" && continue
240   echo "${f}" >> "/tmp/${base_path}/master--"
241done
242for f in $(grep . "/tmp/${base_path}/master")
243do
244   egrep "^${f}$" "/tmp/${base_path}/master--" && continue
245   echo "${f}" >> "/tmp/${base_path}/master++"
246done
247
248# split master list in paquet of split_number computer
249sort -u "/tmp/${base_path}/master++" | split -l ${split_number} - /tmp/${base_path}/__splitted_
250
251# wait is needed by time tmux session open and ssh time connection
252tempo=0.8
253first_tempo=0
254other_tempo=0
255if [ -n "${remote_command}" ]
256then
257   # add tempo after remote command
258   other_tempo=${tempo}
259   first_tempo="${tempo} ${other_tempo}"
260fi
261
262# loop on each split windows
263for f in $(ls -1 /tmp/${base_path}/ | grep ^__splitted_)
264do
265   if [ "${verbose}" == 'yes' ]
266   then
267      echo "Info: next hosts to be splitted"
268      cat "/tmp/${base_path}/${f}" | sed -e 's/^/  /;'
269      sleep 5
270   fi
271
272   session=$(shuf -n 1 /usr/share/dict/words | tr -cd "[:alpha:]")
273
274   IFS=$'\n' host=($(cat "/tmp/${base_path}/${f}"))
275
276   tmux -2 new-session -d -s $session "ssh ${ssh_option} ${host[0]} ${remote_command}; sleep ${first_tempo}"
277   # wait ${tempo} second to let new session start...
278   sleep ${tempo}
279
280   for (( i=1 ; i < ${#host[@]} ; i++))
281   do
282      # wait ${tempo} needed in case of ${remote_command}
283      tmux splitw -t $session "ssh ${ssh_option} ${host[$i]} ${remote_command}; sleep ${other_tempo}"
284      tmux select-layout tiled
285   done
286
287   tmux set-window-option synchronize-panes on  > /dev/null
288   tmux set-window-option -g utf8 on            > /dev/null
289   tmux set -g default-terminal screen-256color > /dev/null
290   #tmux set-option -g set-clipboard on
291 
292   # Sane scrolling
293   #tmux set -g mode-mouse on
294   #tmux set -g mouse-resize-pane on
295   #tmux set -g mouse-select-pane on
296   #tmux set -g mouse-select-window on
297 
298   #set -g terminal-overrides 'xterm*:smcup@:rmcup@'
299 
300   # toggle mouse mode to allow mouse copy/paste
301   # set mouse on with prefix m
302   tmux bind m \
303      set -g mode-mouse on \; \
304      set -g mouse-select-pane on \; \
305      display 'Mouse: ON' > /dev/null
306      # set -g mouse-resize-pane on \; \
307      #set -g mouse-select-window on \; \
308   # set mouse off with prefix M
309   tmux bind M \
310      set -g mode-mouse off \; \
311      set -g mouse-select-pane off \; \
312      display 'Mouse: OFF' > /dev/null
313      #set -g mouse-resize-pane off \; \
314      #set -g mouse-select-window off \; \
315   # toggle Broadcast
316   tmux bind b set-window-option synchronize-panes
317
318   tmux attach -t $session
319done
320
321# Clean temporary folder
322[ -d "/tmp/${base_path}" ] && rm -rf "/tmp/${base_path}"
Note: See TracBrowser for help on using the repository browser.