source: trunk/tssh/tssh @ 390

Last change on this file since 390 was 390, checked in by g7moreau, 5 years ago
  • Add -o 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             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 fast='yes'
94    #export verbose='yes'
95
96AUTHOR
97   Gabriel Moreau
98
99COPYRIGHT
100   Copyright (C) 2014-2019, LEGI UMR 5519 / CNRS UGA G-INP, Grenoble, France
101   Licence : GNU GPL version 2 or later
102END_USAGE
103   }
104
105export remote_command=''
106export ssh_option=''
107export split_number=16
108if which tput > /dev/null
109then
110   export split_number=$(( ($(tput  lines)/ 10) * ($(tput  cols)/ 40) ))
111fi
112
113export dyn_domain=''
114if [ -e "${HOME}/.tsshrc" ]
115then
116   . "${HOME}/.tsshrc"
117fi
118
119# get options
120if [ $# -eq 0 ]; then usage; exit 1; fi 
121while getopts "w:c:fvh" options
122do
123   case ${options} in
124      w)
125         if echo ${OPTARG} | egrep -q '^[[:digit:]]+$' && [ ${OPTARG} -gt 0 ]
126         then
127            export split_number=${OPTARG}
128         else
129            usage
130            exit 2
131         fi
132         ;;
133      c)
134         if echo ${OPTARG} | egrep -q '[[:alpha:]]'
135         then
136            export remote_command=${OPTARG}
137         else
138            usage
139            exit 2
140         fi
141         ;;
142      o)
143         if echo ${OPTARG} | egrep -q '[[:alpha:][:digit:]]'
144         then
145            export ssh_option=${OPTARG}
146         else
147            usage
148            exit 2
149         fi
150         ;;
151      f)
152         export fast='yes'
153         ;;
154      v)
155         export verbose='yes'
156         ;;
157      h|*)
158         usage
159         exit 3
160         ;;
161   esac
162done
163shift $((OPTIND - 1))
164[[ $1 = "--" ]] && shift
165
166cd /tmp/
167export base_path=$(mktemp -d tssh.XXXXXX)
168touch "/tmp/${base_path}/master"
169touch "/tmp/${base_path}/master-"
170touch "/tmp/${base_path}/master+"
171touch "/tmp/${base_path}/master--"
172touch "/tmp/${base_path}/master++"
173
174get_host_list () {
175   local cluster
176   local default_mode=''
177
178   # set local mode
179   if echo $1 | grep -- ^--mode=
180   then
181      default_mode=$(echo $1 | grep -- ^--mode= | cut -f 2 -d '=')
182      shift
183   fi
184
185   for host in $*
186   do
187      local mode=${default_mode}
188      local last_char="${host: -1}"
189      if [ "${last_char}" == "-" -o "${last_char}" == "+" ]
190      then
191         mode="${last_char}"
192         host="${host:0:${#host}-1}"
193      fi
194
195      # short host without login part if any
196      local justhost=${host#*@}
197     
198      cluster=$(grep "^${justhost}\b" ${HOME}/.csshrc | cut -f 2 -d '=' | sed -e 's/^[[:space:]]*//;')
199      if [ "${cluster}" == "" ]
200      then
201         # just a host to scan and add
202         if [ "${fast}" != 'yes' -a "${mode}" != '-' ]
203         then
204            # test if exists host
205            if host ${justhost} | grep -q 'not found'
206            then
207               [ "${verbose}" == 'yes' ] && echo "Warning: ${justhost} does not exists"
208               continue
209            fi
210            if ! nmap -p 22 -sT -PN ${justhost} | grep -q '\bopen\b'
211            then
212               if host ${justhost}.${dyn_domain} | grep -q 'not found' || ! nmap -p 22 -sT -PN ${justhost}.${dyn_domain} | grep -q '\bopen\b'
213               then
214                  [ "${verbose}" == 'yes' ] && echo "Warning: ${justhost} is down"
215                  continue
216               else
217                  [ "${verbose}" == 'yes' ] && echo "Warning: remove ssh key of ${justhost}.${dyn_domain}"
218                  host=${justhost}.${dyn_domain}
219                  ssh-keygen -q -R $(LANG=C host ${justhost} | awk '{print $4}')
220               fi
221            fi
222         fi
223         [ "${verbose}" == 'yes' ] && echo "Warning: add ${host} on list with mode ${mode}"
224         echo "${host}" >> "/tmp/${base_path}/master${mode}"
225      else
226         # cluster, jump in a recursive mode
227         [ "${verbose}" == 'yes' ] && echo "Warning: recursive call for cluster ${justhost} (${cluster}), with mode ${mode}"
228         cluster=$(get_host_list --mode=${mode} "${cluster}")
229      fi
230   done
231   }
232declare -fx get_host_list
233
234get_host_list $@
235cat "/tmp/${base_path}/master+" >> "/tmp/${base_path}/master"
236for f in $(grep . "/tmp/${base_path}/master-")
237do
238   egrep "^${f}$" "/tmp/${base_path}/master+" && continue
239   echo "${f}" >> "/tmp/${base_path}/master--"
240done
241for f in $(grep . "/tmp/${base_path}/master")
242do
243   egrep "^${f}$" "/tmp/${base_path}/master--" && continue
244   echo "${f}" >> "/tmp/${base_path}/master++"
245done
246
247# split master list in paquet of split_number computer
248sort -u "/tmp/${base_path}/master++" | split -l ${split_number} - /tmp/${base_path}/__splitted_
249
250# wait is needed by time tmux session open and ssh time connection
251tempo=0.8
252first_tempo=0
253other_tempo=0
254if [ -n "${remote_command}" ]
255then
256   # add tempo after remote command
257   other_tempo=${tempo}
258   first_tempo="${tempo} ${other_tempo}"
259fi
260
261# loop on each split windows
262for f in $(ls -1 /tmp/${base_path}/ | grep ^__splitted_)
263do
264   if [ "${verbose}" == 'yes' ]
265   then
266      echo "Info: next hosts to be splitted"
267      cat "/tmp/${base_path}/${f}" | sed -e 's/^/  /;'
268      sleep 5
269   fi
270
271   session=$(shuf -n 1 /usr/share/dict/words | tr -cd "[:alpha:]")
272
273   IFS=$'\n' host=($(cat "/tmp/${base_path}/${f}"))
274
275   tmux -2 new-session -d -s $session "ssh ${ssh_option} ${host[0]} ${remote_command}; sleep ${first_tempo}"
276   # wait ${tempo} second to let new session start...
277   sleep ${tempo}
278
279   for (( i=1 ; i < ${#host[@]} ; i++))
280   do
281      # wait ${tempo} needed in case of ${remote_command}
282      tmux splitw -t $session "ssh ${ssh_option}${host[$i]} ${remote_command}; sleep ${other_tempo}"
283      tmux select-layout tiled
284   done
285
286   tmux set-window-option synchronize-panes on  > /dev/null
287   tmux set-window-option -g utf8 on            > /dev/null
288   tmux set -g default-terminal screen-256color > /dev/null
289   #tmux set-option -g set-clipboard on
290 
291   # Sane scrolling
292   #tmux set -g mode-mouse on
293   #tmux set -g mouse-resize-pane on
294   #tmux set -g mouse-select-pane on
295   #tmux set -g mouse-select-window on
296 
297   #set -g terminal-overrides 'xterm*:smcup@:rmcup@'
298 
299   # toggle mouse mode to allow mouse copy/paste
300   # set mouse on with prefix m
301   tmux bind m \
302      set -g mode-mouse on \; \
303      set -g mouse-select-pane on \; \
304      display 'Mouse: ON' > /dev/null
305      # set -g mouse-resize-pane on \; \
306      #set -g mouse-select-window on \; \
307   # set mouse off with prefix M
308   tmux bind M \
309      set -g mode-mouse off \; \
310      set -g mouse-select-pane off \; \
311      display 'Mouse: OFF' > /dev/null
312      #set -g mouse-resize-pane off \; \
313      #set -g mouse-select-window off \; \
314   # toggle Broadcast
315   tmux bind b set-window-option synchronize-panes
316
317   tmux attach -t $session
318done
319
320# Clean temporary folder
321[ -d "/tmp/${base_path}" ] && rm -rf "/tmp/${base_path}"
Note: See TracBrowser for help on using the repository browser.