/usr/sbin/cabal
1 #!/bin/bash
2 #---------------------------------------------------------------------
3 ## @Synopsis A set of functions for the Cabal distributed administration system.
4 ## @Copyright Copyright 2002 by the Source Mage Team. Some parts Copyright 2001 by Kyle Sallee
5 ## Control a set of SourceMage boxes on a network. This documentation was
6 ## written by someone who never used cabal or looked at this code before.
7 ##
8 ## @Globals CABAL_DIRECTORY CABAL_NAMES MAX_CABALS
9 #---------------------------------------------------------------------
10
11 #------------------
12 ## Get the names of the machines in the cabal and store in CABAL
13 ## @Stdout The names of the machines in the cabal
14 ## @Globals CABAL_NAMES CABAL
15 #------------------
16 function read_cabal_names() {
17
18 mkdir -p $CABAL_DIRECTORY
19
20 [ -f $CABAL_NAMES ] || return
21
22
23 eval `awk '{ print "CABALS[" NR-1 "]=\"" $0 "\""; }' "$CABAL_NAMES"`
24
25 }
26
27 #--------------------
28 ## Overwrite the cabal name file with new member names
29 ## @Globals CABAL_NAMES MAX_CABALS
30 #--------------------
31 function write_cabal_names() {
32
33 rm -f $CABAL_NAMES
34 for (( COUNT=0; COUNT != ${#CABALS[@]}; COUNT++ )); do
35 echo "${CABALS[$COUNT]}" >> $CABAL_NAMES
36 done
37
38 }
39
40 #---------------------
41 ## Print out the member of the cabal (assuming the names have been read)
42 ## @Globals MAX_CABALS
43 ## @Stdout Names of each cabal member
44 ## @NOTE MAX_CABAL can be eliminated
45 #---------------------
46 function print_cabal_names() {
47
48 eval ${DIALOG} '--textbox ${CABAL_NAMES} 0 0'
49
50 #for (( COUNT = 0; COUNT != MAX_CABALS; COUNT++ )); do
51 # echo "${CABALS[$COUNT]}"
52 #done
53
54 }
55
56 #---------------------
57 ## Outputs the number and name of each cabal member
58 ## @Stdout <number>\n<cabal name or "Undefined"> for all cabal members
59 ## @NOTE Dito about MAX_CABALS
60 #---------------------
61 function print_cabal_numbers_names() {
62
63 for (( COUNT = 0; COUNT != MAX_CABALS; COUNT++ )); do
64 echo "$COUNT"
65 if [ -n "${CABALS[$COUNT]}" ]
66 then echo "${CABALS[$COUNT]}"
67 else echo "Undefined"
68 fi
69 done
70
71 }
72
73 #-----------------------
74 ## Pops up a dialog box for selecting a specific cabal member
75 ## @Stdout dialog output
76 ## @Stderr Cabal member selection
77 #-----------------------
78 function select_cabal() {
79
80 eval $DIALOG '--title "Cabal Selection Menu" \
81 --ok-label "Select" \
82 --cancel-label "Exit" \
83 --menu \
84 "Please Select a Cabal" \
85 0 0 0 \
86 $( print_cabal_numbers_names )'
87
88 }
89
90 #----------------------
91 ## Edit the file about selected members
92 #----------------------
93 function edit_cabals() {
94
95 while SELECTED=`select_cabal`
96 do edit_file "$CABAL_DIRECTORY/$SELECTED"
97 done
98
99 }
100
101 #---------------------
102 ## Show contents of a member's info file
103 #---------------------
104 function show_cabals() {
105
106 while SELECTED=`select_cabal`
107 do show_file "$CABAL_DIRECTORY/$SELECTED"
108 done
109
110 }
111
112 #--------------------
113 ## Generate SSH key for a cabal member
114 #--------------------
115 function generate_cabal_key() {
116
117 while SELECTED=`select_cabal`
118 do
119
120 mkdir -p $CABAL_KEYS
121 chmod 600 $CABAL_KEYS
122
123 ssh-keygen -t dsa \
124 -b 1024 \
125 -N "" \
126 -f $CABAL_KEYS/$SELECTED
127
128 done
129
130 }
131
132
133 function all_cabal_keys() {
134
135 for KEY in `ls $CABAL_KEYS/* |
136 grep -v "\.pub"`
137 do echo "-i"
138 echo "$KEY"
139 done
140
141 }
142
143
144 function distribute_cabal_key() {
145
146 while SELECTED=`select_cabal`
147 do
148
149 mkdir -p $CABAL_KEYS
150 chmod 600 $CABAL_KEYS
151
152 if ! [ -e $CABAL_KEYS/$SELECTED.pub ]; then
153 eval $DIALOG --msgbox "Generate the key first!"
154 return
155 fi
156
157 AK2="/root/.ssh/authorized_keys2"
158 # TODO check that the following works, since it is reading and writing to
159 # the same file - $AK2
160 cat $CABAL_DIRECTORY/$SELECTED |
161 while read BOX
162 do cat $CABAL_KEYS/$SELECTED.pub |
163 ssh `all_cabal_keys` \
164 root@$BOX \
165 "cat >> $AK2; \
166 sort $AK2 | \
167 uniq > $AK2"
168 done
169 done
170
171 }
172
173
174 function revoke_cabal_key() {
175
176 while SELECTED=`select_cabal`
177 do
178
179 mkdir -p $CABAL_KEYS
180 chmod 600 $CABAL_KEYS
181
182 if ! [ -e $CABAL_KEYS/$SELECTED.pub ]; then
183 eval $DIALOG --msgbox "No key to revoke!"
184 return
185 fi
186
187 AK2="/root/.ssh/authorized_keys2"
188
189 cat $CABAL_DIRECTORY/$SELECTED |
190 while read BOX
191 do REVOKE=KEY="`cat $CABAL_KEYS/$SELECTED.pub`"
192 ssh `all_cabal_keys` \
193 root@$BOX \
194 "grep -v "$REVOKE_KEY" $AK2 | \
195 sort > $AK2"
196 done
197 done
198
199 }
200
201
202 name_cabals() {
203
204 TITLE="Name Cabals Menu"
205 HELP="Please enter cabal's name"
206
207 while SELECTED=`select_cabal`
208 do
209
210 if NAME=`$DIALOG --title "$TITLE" \
211 --ok-label "Enter" \
212 --cancel-label "Cancel" \
213 --inputbox \
214 "$HELP" \
215 0 0 ${CABALS[$SELECTED]}`
216 then
217 CABALS[$SELECTED]="$NAME"
218 fi
219 done
220
221 write_cabal_names
222
223 }
224
225
226 function set_max_cabals() {
227
228 PROMPT="Please enter the maximum number of cabals"
229
230 if NEW_MAX=`eval $DIALOG '--ok-label "Commit" \
231 --inputbox \
232 "$PROMPT" \
233 0 0 "$MAX_CABALS"'`
234 then
235 MAX_CABALS=$NEW_MAX
236 modify_local_config "MAX_CABALS" $MAX_CABALS
237 fi
238
239 }
240
241
242 function cabal_name_menu() {
243
244 while
245
246 HELP=""
247 S_HELP="Show the names of the cabals"
248 E_HELP="Edit the names of the cabals"
249 TITLE="Cabal Name Menu"
250
251 COMMAND=`eval $DIALOG '--title "$TITLE" \
252 --item-help \
253 --ok-label "Select" \
254 --cancel-label "Exit" \
255 --menu \
256 "$HELP" \
257 0 0 0 \
258 "S" "Show Cabal Names" "$S_HELP" \
259 "E" "Edit Cabal Names" "$E_HELP"'`
260 do
261
262 case $COMMAND in
263 S) print_cabal_names ;;
264 E) name_cabals ;;
265 esac
266
267 done
268
269 }
270
271
272 function cabal_key_menu() {
273
274 while
275
276 HELP="Generate, distribute, and revoke ssh2 keys to cabal computers to
277 enable root login without password prompts"
278 G_HELP="Create 2048 bit ssh2 dsa public/private keys for a cabal."
279 D_HELP="Install the public key on all computer members of cabal."
280 R_HELP="Remove a previously installed cabal key"
281 TITLE="Cabal Key Menu"
282
283 COMMAND=`eval $DIALOG '--title "$TITLE" \
284 --item-help \
285 --ok-label "Select" \
286 --cancel-label "Exit" \
287 --menu \
288 "$HELP" \
289 0 0 0 \
290 "G" "Generate Cabal Key" "$G_HELP" \
291 "D" "Distribute Cabal Key" "$D_HELP" \
292 "R" "Revoke Cabal Key" "$R_HELP"'`
293 do
294
295 case $COMMAND in
296 G) generate_cabal_key ;;
297 D) distribute_cabal_key ;;
298 R) revoke_cabal_key ;;
299 esac
300
301 done
302
303 }
304
305
306 function cabal_content_menu() {
307
308 while
309
310 HELP=""
311 S_HELP="Show the computers which belong to a cabal"
312 E_HELP="Edit the computers which belong to a cabal"
313 TITLE="Cabal Content Menu"
314
315 COMMAND=`eval $DIALOG '--title "$TITLE" \
316 --item-help \
317 --ok-label "Select" \
318 --cancel-label "Exit" \
319 --menu \
320 "$HELP" \
321 0 0 0 \
322 "S" "Show Cabal" "$S_HELP" \
323 "E" "Edit Cabal" "$E_HELP"'`
324 do
325
326 case $COMMAND in
327 S) show_cabals ;;
328 E) edit_cabals ;;
329 esac
330
331 done
332
333 }
334
335
336 function select_order() {
337
338 eval $DIALOG '--title "Order Selection Menu" \
339 --menu "" 0 0 0 \
340 "S" "Sequentially" \
341 "C" "Concurrently"'
342
343 }
344
345
346 function cabal_admin_menu() {
347
348 while
349
350 HELP=""
351 M_HELP="Define maximum amount of cabals."
352 N_HELP="Define and display the names of cabals"
353 C_HELP="Define and display the computers belong to a cabal"
354 K_HELP="Generate and distribute keys for cabals"
355 TITLE="Cabal Admin Menu"
356
357 COMMAND=`eval $DIALOG '--title "$TITLE" \
358 --item-help \
359 --ok-label "Select" \
360 --cancel-label "Exit" \
361 --menu \
362 "$HELP" \
363 0 0 0 \
364 "N" "Cabal Name Menu" "$N_HELP" \
365 "C" "Cabal Content Menu" "$C_HELP" \
366 "K" "Cabal Key Menu" "$K_HELP" \
367 "M" "Maximum Cabals: $MAX_CABALS" "$M_HELP"'`
368
369 do
370
371 case $COMMAND in
372 N) cabal_name_menu ;;
373 C) cabal_content_menu ;;
374 K) cabal_key_menu ;;
375 M) set_max_cabals ;;
376 esac
377
378 done
379
380 }
381
382
383 function cabal_copy() {
384
385 SOURCE_MESSAGE='"Please enter source files and directories."'
386 DEST_MESSAGE='"Please enter destination directory."'
387
388 mkdir -p $CABAL_OUTPUT || {
389 messasge "Failed to make CABAL_OUTPUT directory $CABAL_OUTPUT"
390 exit 1
391 }
392
393 SOURCE=`eval $DIALOG --inputbox $SOURCE_MESSAGE 0 0` &&
394 DESTINATION=`eval $DIALOG --inputbox $DEST_MESSAGE 0 0` &&
395 CABAL=`select_cabal` &&
396 ORDER=`select_order` &&
397
398 case $ORDER in
399 C) cat $CABAL_DIRECTORY/$CABAL |
400 while read BOX; do
401 scp -i $CABAL_KEYS/$CABAL \
402 -r $SOURCE \
403 root@${BOX}:$DESTINATION \
404 > $CABAL_OUTPUT/$BOX &
405 done
406 ;;
407 S) cat $CABAL_DIRECTORY/$CABAL |
408 while read BOX; do
409 scp -i $CABAL_KEYS/$CABAL \
410 -r $SOURCE \
411 root@${BOX}:$DESTINATION
412 done
413 read -n 1 -p "Press any key to continue"
414 ;;
415 esac
416
417 }
418
419
420 function cabal_execute() {
421
422 COMMAND_MESSAGE="Please enter command to be execute remotely on cabal."
423
424 mkdir -p $CABAL_OUTPUT || {
425 messasge "Failed to make CABAL_OUTPUT directory $CABAL_OUTPUT"
426 exit 1
427 }
428
429 COMMAND=`eval $DIALOG '--inputbox "$COMMAND_MESSAGE" 0 0'` &&
430 CABAL=`select_cabal` &&
431 ORDER=`select_order` &&
432
433 case $ORDER in
434 C) cat $CABAL_DIRECTORY/$CABAL |
435 while read BOX; do
436 echo "execing box $BOX" && ssh -n -i $CABAL_KEYS/$CABAL \
437 root@${BOX} \
438 "$COMMAND" \
439 > $CABAL_OUTPUT/$BOX &
440 done
441 ;;
442
443 S) cat $CABAL_DIRECTORY/$CABAL |
444 while read BOX; do
445 echo "execing box $BOX" && ssh -n -i $CABAL_KEYS/$CABAL \
446 root@${BOX} \
447 "$COMMAND"
448 done
449 read -n 1 -p "Press any key to continue"
450 ;;
451 esac
452
453 #sleep 10 <--not sure why that's there.. --dave
454
455 }
456
457
458 function cabal_output() {
459
460 if [ -d $CABAL_OUTPUT ]; then
461 for FILE in `ls $CABAL_OUTPUT`; do
462 echo "*------- $FILE Beginning Output -------*"
463 cat $CABAL_OUTPUT/$FILE
464 echo "*------- $FILE Ending Output -------*"
465 echo
466 done
467 fi
468 }
469
470
471 function cabal_enchantment_menu() {
472
473 while
474
475 HELP=""
476 C_HELP="Copy files and directories to cabals via scp"
477 E_HELP="Execute commands on cabals"
478 S_HELP="Show the output of previously execute concurrent commands and
479 copies"
480 TITLE="Enchantment Menu"
481
482 COMMAND=`eval $DIALOG '--title "$TITLE" \
483 --item-help \
484 --ok-label "Select" \
485 --cancel-label "Exit" \
486 --menu \
487 "$HELP" \
488 0 0 0 \
489 "C" "Copy" "$A_HELP" \
490 "E" "Execute" "$E_HELP" \
491 "S" "Show" "$S_HELP"'`
492
493 do
494
495 case $COMMAND in
496 C) cabal_copy ;;
497 E) cabal_execute ;;
498 S) cabal_output | ${PAGER} ;;
499 esac
500
501 done
502
503 }
504
505
506 function cabal_menu() {
507
508 while
509
510 read_cabal_names
511
512 HELP="Administrate Multiple Boxes Simultaneously With Cabal"
513 A_HELP="Set up Cabals"
514 E_HELP="Execute scripts and copy files."
515 TITLE="Cabal Menu"
516
517 COMMAND=`eval $DIALOG '--title "$TITLE" \
518 --item-help \
519 --ok-label "Select" \
520 --cancel-label "Exit" \
521 --menu \
522 "$HELP" \
523 0 0 0 \
524 "A" "Administration" "$A_HELP" \
525 "E" "Enchantment" "$E_HELP"'`
526
527 do
528
529 case $COMMAND in
530 A) cabal_admin_menu ;;
531 E) cabal_enchantment_menu ;;
532 esac
533
534 done
535
536 }
537
538 . /etc/sorcery/config
539
540 if [ -n "${PAGER}" ]
541 then
542 PAGER=`which less`
543 fi
544
545 DIALOG="${DIALOGPROG} --stdout"
546
547 cabal_menu
548
549 ##---------------------------------------------------------------------
550 ##=back
551 ##
552 ##=head1 LICENSE
553 ##
554 ## This software is free software; you can redistribute it and/or modify
555 ## it under the terms of the GNU General Public License as published by
556 ## the Free Software Foundation; either version 2 of the License, or
557 ## (at your option) any later version.
558 ##
559 ## This software is distributed in the hope that it will be useful,
560 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
561 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
562 ## GNU General Public License for more details.
563 ##
564 ## You should have received a copy of the GNU General Public License
565 ## along with this software; if not, write to the Free Software
566 ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
567 ##
568 ##---------------------------------------------------------------------