1 #!/usr/bin/env expect 2 #this may it a bit more portable 3 # 4 #a script to (partially) simulate secureCRT funtions 5 # 6 #main features available currently: # 7 # anti-idle # 8 # auto-login (with retry) # 9 # name-to-host resolving # 10 # quick keystroke cmds under interact mode(after a successful login) # 11 # loggings # 12 # change log file in real time, # 13 # email current log file to user (or update case),as attachement # 14 # attachment can be either plain text or optionally in zip format # 15 # execution of user-defined cmds,in desired sequence # 16 # cmds can be grouped # 17 # cmds can be executed repeatedly in user-defined number of loops # 18 # cmd lists can be modified/updated w/o disconnecting current session # 19 # clock(timestamp of the cmd execution) can be prefixed for each cmd # 20 # send logs as email attachment, right away, or at a later time # 21 # execution of user-defined pattern-action list # 22 # dealing with changing/arbitrary prompts # 23 # one example work done by this feature is the coredump analysis # 24 # (file search,upload,telnet,decompress,decode,reformat, # 25 # email to user/case, etc) # 26 # misc. # 27 # ... # 28 # 29 # 30 #work done: 31 # some basic features are done 32 # 33 # 34 #issues/concerns: 35 # this script was mostly done using the corner/spare time, extended from 36 # a temporary script which was not designed to do the current work, that said 37 # here are the limitations/issues: 38 # 1) no good design from start, user commands (!NNN) are not consistent 39 # too much duplicate codes 40 # 2) too much dirty code to save time, make it not quite efficient 41 # (global var, test/temporary/arbitrary/shortcut codes) 42 # 3) no strong error detections/preventions, the script will only work 43 # with the way it was supposed to... 44 # 4) some features are not fully tested, might be buggy 45 # 46 # 47 #more time is needed to rewrite with cleaner codes... 48 # 49 # 50 # Sun Mar 13 16:31:55 EDT 2011 51 # 52 #the script is running good with simple jobs based on daily usage 53 #decision was made to stop coding unless REAL necessary 53 # 53 #send your suggestion/comments to 794707557@qq.com (try) 53 # 54 # Fri Apr 22 13:41:48 EDT 2011 55 56 57 #global vars 58 global cmds login_info CGS 59 global initlog_file host 60 61 #get script basename 62 set scriptbasename [exec basename $argv0] 63 #get the prefix before "." 64 regexp {(.*)\..*} $scriptbasename -> scriptbasename_pref 65 66 #some tests 67 #puts "script full name:-$argv0-" 68 #puts "script base name:-$scriptbasename-" 69 #puts "script base name prefix:-$scriptbasename_pref-" 70 71 #config file by def is located under the folder named by the script name 72 set configfile "~/.$scriptbasename_pref/$scriptbasename_pref.conf" 73 #set configfile "~/.mylogin/mylogin.conf" 74 75 76 #modifed puts: add timestamp and procedure name for every user message printed on the terminal 77 proc myputs {msg} { 78 puts "\[[exec date]:[lindex [info level 1] 0]:..$msg..\]" 79 } 80 81 #function to execute given system CLI(s) 82 #previously report file/dir doesn't exists. tcl exec really sucks! 83 #looks need that {expand} or eval exec story to make it work 84 proc myexec {cmd} { 85 if { \ 86 [catch \ 87 {eval exec \ 88 $cmd \ 89 } \ 90 msg \ 91 ] \ 92 } { 93 myputs "Something seems to have gone wrong:" 94 myputs "Information about it: $::errorInfo" 95 return 1 96 } else { 97 return 0 98 } 99 } 100 101 #add some error detection to log_file func 102 proc mylog_file {cmd} { 103 if { \ 104 [catch \ 105 { \ 106 log_file $cmd \ 107 } \ 108 msg \ 109 ] \ 110 } { 111 myputs "log name is probably not valid: $::errorInfo" 112 return 1 113 } else { 114 return 0 115 } 116 } 117 118 #send an email,with caseid as title and log files as attachment 119 #if use zip, multiple files can be included, otherwise only 1 file is supported 120 #0) use {expand} to make the globbed staff as individual parameters!! 121 # otherwise when glob get 2 file name there will be some strange errors! 122 #1) glob is required here to expand unix ~ or * in exec 123 #2) at least "zip"/"uuencode" and "mail" tools need to be a/v locally 124 #3) -q(quiet mode) in zip is required, 125 # otherwise some error will be catched, email get sent smoothly though 126 #use catch to catch/skip the error and continue 127 #procedure with default value,simpler than varible-length params precedure 128 proc sendanemail {caseid file emailto {emailcc "pings@.net"}} { 129 130 global DEBUG 131 global zip 132 #get file from glob (like unix) 133 set files [glob $file] 134 if $DEBUG {myputs "get file lists: $files"} 135 #if {[file exists [glob file]]} { 136 if $zip { 137 if $DEBUG {myputs "zip set, will send zipped file"} 138 set attachname "$caseid.zip" 139 set execcmd "zip -jq - {expand} [glob $file] | uuencode $attachname | mail -s \"log of case:$caseid\" -c $emailcc $emailto" 140 } else { 141 set attachname [exec basename $files] 142 if $DEBUG {myputs "zip not set, will send plain text file"} 143 #looks only 1 file is support at a time with uuencode 144 #this broke after upgrading to 12.04LTS 145 #set execcmd "uuencode [glob $file] $attachname | mail -s \"log of case:$caseid\" -c $emailcc $emailto" 146 #set execcmd "sendemail -s pod51010.outlook.com:587 -f pings@.net -t pings@.net -u \"log of case:$caseid\" -m \"this is the log file: $file\" -xu pings@.net -xp \"EMAILPASSWORD\" -o tls=auto -a [glob $file]" 147 set execcmd "sendthisfile.sh [glob $file] pings@.net \"log of case:$caseid\" " 148 } 149 150 #funny tcl rule, non-matching {} in comments seems also hit an error 151 # if { \ 152 # [catch \ 153 # {exec \ 154 # zip -jq - {expand} [glob $file] \ 155 # | uuencode $caseid.zip \ 156 # | mail -s "log of case:$caseid" \ 157 # -b $emailcc $emailto \ 158 # } \ 159 # msg \ 160 # ] \ 161 # } { 162 # myputs "Something seems to have gone wrong:" 163 # myputs "Information about it: $::errorInfo" 164 # } 165 if {[myexec $execcmd]} { 166 } else { 167 myputs "send email to $emailto (and bcc $emailcc) with logfile:$files as attachment:$attachname" 168 } 169 # } 170 # else { 171 # myputs "log file $file doesn't exist!" 172 # } 173 } 174 175 176 proc usage {} { 177 #$argv0 as script name become a private var here in proc w/o global 178 global argv0 179 myputs "Usage:$argv0 ssh|telnet|ftp|... PARAMS_LISTS" 180 } 181 182 #use this as a more informative expect function 183 #return 0 if got successful map. useful for result checking purpose 184 proc myexpect {usermsg pattern datasent timeout} { 185 #if pattern match, send data 186 global DEBUG 187 global quitontimeout quitkey 188 if $DEBUG {myputs "-$usermsg-,start expecting pattern:-$pattern- to proceed with sending -$datasent-"} 189 expect { 190 -re "$pattern" { 191 #looks very important. look \r is more reliable across OS.as suggested in expect books 192 #\n is ok for my linux, but not work for win goodtech telnetd 193 send "$datasent\r" 194 if $DEBUG {myputs "get good match for -$pattern- with -$expect_out(0,string)-,sent -$datasent\\\r-"} 195 return 0 196 } 197 198 timeout { 199 if $DEBUG {myputs "timeout in $timeout seconds:no match for -$pattern-,data -$datasent- not sent"} 200 #this is useful when the last cmd get stuck there and could be exited out of 201 #using some key, like "q" 202 if $quitontimeout { 203 if $DEBUG {myputs "quit last suspended cmd before preceeding"} 204 send "$quitkey" 205 } 206 207 return 1 208 } 209 } 210 } 211 212 #use only when there are logon info for large amount of remote host, 213 #when they can be put into a sperate/dedicated logon file. 214 # 215 #retrieve login info from a file and get it structured for use 216 #upvar (ref in tcl) is used here to pass value back from proc 217 #login_info is a global multi-dimensional array 218 # login_info(hostname1 pattern1) = data1 219 # login_info(hostname1 pattern2) = data2 220 # . 221 proc get_login_info {loginfile login_info} { 222 #ref it 223 global DEBUG 224 upvar $login_info a 225 myputs "grab login info from file $loginfile-" 226 myputs "open file $loginfile" 227 set file [open $loginfile r] 228 set cmd_no 0 229 while {[gets $file line] != -1} { 230 if $DEBUG {myputs "get a line from file:-$line-"} 231 #save the splitted line into a list 232 set l1 [split $line " "] 233 if $DEBUG { 234 myputs "this line is splitted into:" 235 myputs " -$l1-" 236 } 237 #get the 1st member out of the line,as hostname 238 set hostname [lindex $l1 0] 239 #get the remainder as login info 240 set pa_pair [lrange $l1 1 end] 241 #convert the login list to an array (tcl, hash in perl) 242 set a($hostname) $pa_pair 243 } 244 close $file 245 # return $cmd_no 246 } 247 248 #auto login script 249 #here global array is used directly for simplicity, 250 #saving one proc param and upvar stuff 251 proc do_patterns_actions {host pattern_timeout dataarray pa_intv} { 252 global DEBUG addclock send_initial_cr 253 upvar $dataarray da 254 # source ~/.mylogin/nofwd1211.conf 255 if $DEBUG {myputs "start pattern-action sequence"} 256 if $DEBUG {parray da} 257 if $send_initial_cr {send "\r"} 258 if {[info exists da($host)]} { 259 if $DEBUG {myputs "pattern-action data for $host now looks:"} 260 if $DEBUG {myputs " -$da($host)-"} 261 } else { 262 myputs "pattern-action data for $host doesn't exist, check your config!" 263 return 1 264 } 265 266 # this won't work for duplicate patterns 267 # set i 1 268 #array set pa_pair $login_info($host) 269 # foreach pattern [array names pa_pair] { 270 # set datasent $pa_pair($pattern) 271 # myexpect "pattern-action item $i\n" $pattern $datasent 272 # incr i 273 # } 274 #get a data list from data array 275 set l $da($host) 276 set j 0 277 #go through this data list 278 for {set i 0} {$i<=[expr [llength $l]-1]} {incr i 2} { 279 #get pattern/data 280 set pattern [lindex $l $i] 281 set datasent [lindex $l [expr $i+1]] 282 #execute the pattern-data pairs 283 if $addclock { 284 if $DEBUG { myputs "send a clock" } 285 send "$clockcmd\r" 286 } 287 myexpect "pattern-action item $j" $pattern $datasent $pattern_timeout 288 #if $DEBUG {myputs "pattern-action item $j"} 289 #do_cmd $pattern $datasent $pattern_timeout 290 incr j 291 #optionally pause between each step 292 sleep $pa_intv 293 } 294 295 #this is to garrantee we can get the prompt for the last cmd to finish 296 #otherwise the output of it will be held unless next cmd was inputted 297 #this works in most cases: 298 #myexpect "extra return" $pattern "\r" $pattern_timeout 299 #but this is better: 300 myexpect "extra return" ".*" "\r" $pattern_timeout 301 } 302 303 proc repeat_patterns_actions {maxrounds host pattern_timeout dataarray pa_intv} { 304 myputs "$maxrounds rounds of patterns_actions_list will be executed" 305 set i 1 306 upvar $dataarray ref 307 while {$i<=$maxrounds} { 308 do_patterns_actions $host $pattern_timeout ref $pa_intv 309 myputs "\n\n..#####################################..\n" 310 myputs "this is rounds $i of patterns_actions_list execution..\n" 311 myputs "..#####################################..\n\n" 312 313 #this doesn't work 314 # trap {send_user "bye"; exit} SIGINT 315 incr i 316 } 317 } 318 319 #set PAGS(e320-1) {pa_list1 pa_list2} 320 #set pa_list1(HRNDVA-FIOS-2) {# "config t" config "do show clock" config exit} 321 #set pa_list2(HRNDVA-FIOS-2) {# "config t" config "do show red" config exit} 322 #set pa_list1(e320-1) {# "config t" config "do show clock" config exit} 323 #set pa_list2(e320-1) {# "config t" config "do show red" config exit} 324 #this function is depressed by its new version and hence obsolete 325 proc do_pags_original_obsolete {pags host pattern_timeout pa_intv} { 326 327 global DEBUG NEWFEATURE 328 #pass the array via upvar (ref) 329 upvar $pags PAGS 330 if $DEBUG {myputs "get pattern action groups from list(PAGS):-$PAGS($host)-"} 331 332 foreach pa_group $PAGS($host) { 333 #this worked, but ugly, in terms of using global var like this 334 if $DEBUG {myputs "get a pa_group $pa_group"} 335 if $DEBUG {myputs "executed eval global $pa_group"} 336 eval global $pa_group 337 338 do_patterns_actions $host $pattern_timeout $pa_group $pa_intv 339 } 340 } 341 342 343 proc do_pags {pags host pattern_timeout pa_intv} { 344 345 global DEBUG NEWFEATURE configfile 346 #pass the array via upvar (ref) 347 upvar $pags PAGS 348 if $DEBUG {myputs "get pattern action groups from :-$PAGS($host)-"} 349 350 #source $configfile 351 352 if {[regexp "^E_" $pags]} { 353 if $DEBUG {myputs "this pa_group $pa_group is end 'leaf' node,execute it..."} 354 do_patterns_actions $host $pattern_timeout $PAGS $pa_intv 355 } else { 356 foreach pa_group $PAGS($host) { 357 if {[regexp "^E_" $pa_group]} { 358 #this worked, but ugly, in terms of using global var like this 359 # 360 if {[info exists pa_group($host)] == -1} { 361 myputs "the pattern action group $pa_group is not configured,check your config!" 362 } else { 363 if $DEBUG {myputs "get a pa_group $pa_group"} 364 if $DEBUG {myputs "executed eval global $pa_group"} 365 eval global $pa_group 366 if $DEBUG {myputs "this pa_group $pa_group is end 'leaf' node,execute it..."} 367 do_patterns_actions $host $pattern_timeout $pa_group $pa_intv 368 #unset $pa_group 369 } 370 } else { 371 if {[info exists pa_group($host)] == -1} { 372 myputs "the pattern action group $pa_group is not configured,check your config!" 373 } else { 374 if $DEBUG {myputs "get a pa_group $pa_group"} 375 if $DEBUG {myputs "executed eval global $pa_group"} 376 eval global $pa_group 377 if $DEBUG {myputs "this pa_group $pa_group contains more sub-groups,resolve further..."} 378 do_pags $pa_group $host $pattern_timeout $pa_intv 379 } 380 } 381 } 382 } 383 } 384 385 proc repeat_pags {maxrounds pags host pattern_timeout pa_intv pags_intv} { 386 myputs "$maxrounds rounds of pattern action groups will be executed" 387 upvar $pags PAGS 388 set i 1 389 while {$i<=$maxrounds} { 390 do_pags PAGS $host $pattern_timeout $pa_intv 391 puts "\n\n..#####################################..\n" 392 puts "this is rounds $i of pattern action group..\n" 393 puts "..#####################################..\n\n" 394 395 sleep $pags_intv 396 397 #wanted to stop the loop anytime,this doesn't work yet 398 # trap {send_user "bye"; exit} SIGINT 399 incr i 400 } 401 } 402 403 proc do_autologin_retry {max_login_retry success_login_pattern login_timeout pa_intv} { 404 global login_info 405 set i 1 406 while {$i<=$max_login_retry} { 407 #check the login result to see if a retry is needed 408 #again, tcl syntax: \\~ for ~ ; \\$ for $, \\\\ for \, etc.. to match ping@640g-laptop:~$ 409 #set success_pattern "laptop:\\~*\\$" 410 set autologin_fail [myexpect "check if login success after retry $i" $success_login_pattern "\r" $login_timeout] 411 #if failed, but still within retry limit, retry login 412 if $autologin_fail { 413 puts "..last login failed..will retry $i/$max_login_retry time(s) \n" 414 do_patterns_actions $hostname $login_timeout login_info $pa_intv 415 } else { 416 #if get through, go out of loop and continue 417 set login_retry_fail 0 418 break 419 } 420 #if max retry is reached,go interact 421 if {$ieq$max_login_retry} { 422 puts "..max login retry times($max_login_retry) reached..\n" 423 set login_retry_fail 1 424 } 425 incr i 426 } 427 428 return $login_retry_fail 429 430 } 431 432 #execute a single command, with patience of a given time 433 proc do_cmd {pattern cmd timeout1} { 434 #if use do_cmd {...timeout}, then 435 #no need to set explicitly due to the sepcialty of var timeout in proc param 436 # set timeout $timeout1 - this is no need 437 #but for simplicity we can bypass this machnism and use another var name like timeout1 438 439 global DEBUG NEWFEATURE addclock clockcmd prefix_cr_for_each_cmd 440 #this is to garantee we got the right prompt before proceed 441 #don't know why, but the clockcmd lose 1st CHs from time to time 442 #use these non-sense stuff to feed that 443 #send "!!!!" 444 # 445 if $prefix_cr_for_each_cmd {send "\r"} 446 447 448 if $addclock { 449 if $DEBUG { myputs "send a clock" } 450 send "$clockcmd\r" 451 } 452 453 if $DEBUG { 454 myputs "next cmd:-$cmd-" 455 myputs "will check prompt for cmd -$cmd-" 456 } 457 458 set result [myexpect "checking prompt for -$cmd-" $pattern $cmd $timeout1] 459 return result 460 } 461 462 463 #get cmds out of cmds file, use global var(no ref) to pass value back 464 proc get_cmds {cmdsfile cmds} { 465 global DEBUG 466 upvar $cmds a 467 myputs "grab cmds from file $cmdsfile-" 468 myputs "open file $cmdsfile" 469 set file [open $cmdsfile r] 470 set cmd_no 0 471 while {[gets $file line] != -1} { 472 if ($DEBUG) {myputs "get a line from file:-$line-"} 473 set a($cmd_no) $line 474 incr cmd_no 475 } 476 close $file 477 return $cmd_no 478 } 479 480 481 #get cmds from either config file,or n/a, from another file, 482 #here use global var to get value from proc 483 proc loaddata {datafile data_type data} { 484 global DEBUG cmds login_info 485 upvar $data a 486 487 if {[array size a] eq 0} { 488 if {[catch {source $datafile} msg]} { 489 #file is not in tcl syntax 490 puts "file $datafile is not with correct syntax" 491 puts "..try to read each line as pure $data_type" 492 493 if {$data_type eq "cmds"} { 494 get_cmds $datafile cmds 495 } else { 496 get_logininfo $datafile login_info 497 } 498 499 } else { 500 puts "file $datafile is with good syntax, well loaded" 501 } 502 } else { 503 if $DEBUG {puts "got data already(from config file),no need to read/source file $datafile"} 504 } 505 if $DEBUG { 506 myputs "got following $data_type" 507 parray a 508 } 509 } 510 511 512 513 #executes commands in batch, with given interval bet each cmd 514 #use upvar(ref) to pass array as a parameter 515 #w/o ref seems not working 516 proc do_cmds {pattern cmds cmd_interval waittime} { 517 global DEBUG send_initial_cr 518 upvar $cmds ref 519 if $DEBUG { 520 myputs "start to do_following batch cmds:" 521 parray ref 522 } 523 524 set i 0 525 if $send_initial_cr {send "\r"} 526 #by def tcl will use ascii sequence to sort the list 527 #resort the array index/key w/ numerically increasing order is more convenient to control the cmd orders 528 foreach cmd_no [lsort -integer [array name ref]] { 529 incr i 530 set onecmd $ref($cmd_no) 531 #if no value, just skip and do nothing, otherwise send it 532 #it looks these compare agaist empty is not necessary in tcl 533 if {[string compare $onecmd ""] eq 0} { 534 if $DEBUG {myputs "NO$i:ID$cmd_no:get an empty cmd,skip it and do nothing"} 535 } else { 536 if $DEBUG {myputs "NO$i:ID$cmd_no:get an cmd:$onecmd,send it"} 537 do_cmd $pattern $onecmd $waittime 538 sleep $cmd_interval 539 } 540 } 541 #this is to garrantee we can get the prompt for the last cmd to finishe 542 #otherwise the output of it will be delayed! 543 do_cmd $pattern "\r" $waittime 544 } 545 546 #execute a list of cmds groups, recursively 547 proc do_cmds_groups {pattern cmds_group_list cmds_groups_intv cmd_interval waittime} { 548 global DEBUG NEWFEATURE configfile 549 #since CGS is a list, not an array, so passing it like a var 550 #no need upvar(for array this is needed) 551 #upvar $CGS ref 552 553 if $DEBUG {myputs "start to get cmds groups from cmds_groups_list:-$cmds_group_list-"} 554 #set CGS {ospf isis} 555 #dirty, but working method, to get exact update of each cmd_group(ospf, isis) 556 #may introduce too much unuseful vars into this proc 557 source $configfile 558 559 foreach cmds_group $cmds_group_list { 560 561 if $DEBUG {myputs "get a group $cmds_group"} 562 563 #this worked, but ugly, in terms of using global var like this 564 #to get cmds_group, eg. ospf, isis 565 #if $DEBUG {myputs "executed eval global $cmds_group"} 566 #eval global $cmds_group 567 if {[regexp "^E_" $cmds_group]} { 568 if $DEBUG {myputs "$cmds_group is an end leaf node"} 569 do_cmds $pattern $cmds_group $cmd_interval $waittime 570 } else { 571 if $DEBUG {myputs "$cmds_group is not an end node,resolve further..."} 572 do_cmds_groups $pattern [set $cmds_group] $cmds_groups_intv $cmd_interval $waittime 573 } 574 575 if $NEWFEATURE { 576 foreach cmd_no [lsort -integer [array name [set cmds_group]]] { 577 #set ospf(0) "show ip ospf" 578 #set ospf(1) "show ip ospf nei" 579 #use [set var] to do some eval-like work inside some code 580 if $DEBUG {myputs "get #$cmd_no cmd:[set [set cmds_group]($cmd_no)] from the group"} 581 do_cmd $pattern [set [set cmds_group]($cmd_no)] $waittime 582 sleep $cmd_interval 583 } 584 585 sleep $cmds_groups_intv 586 587 } 588 } 589 590 } 591 592 proc repeat_cmds {maxrounds pattern4cmd cmds cmds_intv cmd_timeout} { 593 myputs "$maxrounds rounds of cmds will be executed" 594 set i 1 595 upvar $cmds ref 596 while {$i<=$maxrounds} { 597 do_cmds $pattern4cmd ref $cmds_intv $cmd_timeout 598 myputs "\n\n..#####################################..\n" 599 myputs "this is rounds $i of cmds set..\n" 600 myputs "..#####################################..\n\n" 601 602 #this doesn't work 603 # trap {send_user "bye"; exit} SIGINT 604 incr i 605 } 606 } 607 608 proc repeat_cmds_groups {maxrounds pattern4cmd \ 609 CGS round_intv cmds_groups_intv \ 610 cmds_intv cmd_timeout} { 611 myputs "$maxrounds rounds of cmds groups will be executed" 612 set i 1 613 #upvar $cmds ref 614 while {$i<=$maxrounds} { 615 do_cmds_groups $pattern4cmd $CGS $cmds_groups_intv $cmds_intv $cmd_timeout 616 myputs "\n\n....\n" 617 myputs "rounds $i/$maxrounds..will continue after $round_intv seconds...\n" 618 myputs "....\n\n" 619 620 #wanted to stop the loop anytime,this doesn't work yet 621 # trap {send_user "bye"; exit} SIGINT 622 incr i 623 sleep $round_intv 624 } 625 } 626 627 proc do_confirm {} { 628 myputs "you sure you want to do that?(y/n)" 629 stty raw 630 expect_user { 631 "y" {return 1} 632 "n" {return 0} 633 } 634 } 635 636 proc do_sel {} { 637 myputs "start sel data collections" 638 # if $NEWFEATURE { 639 if {[do_confirm]} { 640 send "date\n" 641 } else { 642 myputs "sel data collections cancelled" 643 } 644 # } 645 } 646 647 proc do_showtech {} { 648 } 649 650 proc diag_on_error {} { 651 } 652 653 #interact mode,return control to user if all work done/failed 654 proc do_interact {code} { 655 656 657 #hostname here is only needed when invoke script with local shell 658 #zip need to be global here, otherwise "global" in sendanemail won't 659 #take effect 660 global configfile hostname host initlog_file zip 661 global CGS 662 663 #global stuff real sucks, I know, but just for simplicity for now.. 664 #global NEWFEATURE DEBUG CGS caseid_pattern 665 #global mylog_file configfile redosource hostname pa_intv caselog_file 666 #global pattern4cmd precmds cmds cmds_intv cmd_timeout maxrounds 667 668 #most cases re-"source" config file reduces global vars 669 #but it won't garentee all vars in configfile be updated correctly 670 #example: in config file: 671 #1)set ospf(0) abc;set ospf(1) 123 672 #change ospf(0) to def, and delete ospf(1) in config file, 673 #now re-"source" here only update ospf(0), but won't delete ospf(1) as expected 674 source $configfile 675 set n 0 676 677 # code: 678 # enter from point0: spawn a local shell 679 # enter from point1: autologin fail and force into 680 # enter from point2: autologin fail, user confirm to 681 # enter from point3: login ok,and before batch cmds 682 # enter from point4: all done 683 # enter from point5: no autologin 684 # 685 686 switch -- $code \ 687 0 { 688 set reason "spawn a local shell" 689 } 1 { 690 set reason "autologin failed,force flag set to go interact" 691 } 2 { 692 set reason "autologin failed,user confirmed to go" 693 } 3 { 694 set reason "autologin succeed,flag set to go interact" 695 } 4 { 696 set reason "all work done, flag set to go interact" 697 } 5 { 698 set reason "no autologin flag set to go interact" 699 } 700 701 if $DEBUG {myputs "go to interact mode on reason:$reason"} 702 if $welcome_msg { 703 myputs " 704 welcome to mylogin shell! it's an clone(spawn) of bash plus some new customized shortcut commands 705 the intention is to help JTAC day-to-day work. But it can also be used for ANY other usual tasks 706 wherever the origin shell(e.g bash) was used, plus now we have logging, anti-timeout, and other features. 707 type !i for currently available cmds, !h for most frequently used cmds, and !T for a mini tutorial 708 enjoy it! 709 710 -v0.2 ping@.net" 711 } 712 713 #start currlog_file value from initlog_file, which, was acquired in the very beginning 714 #moment of the config file reading when the script was just running 715 #this is to avoid the dynamic nature of the config file, especially the timebased file naming 716 #so source config file again won't change currlog_file anymore 717 set currlog_file $initlog_file 718 719 set log_started $log_when_start 720 721 #initially newcaseid is same as caseid in config file, but it will change per user cmds 722 set newcaseid $caseid 723 #initial caseid and caselog were preserved for some use:mostly to generate full name 724 #of a new log file by replacing initcaseid with a new caseid, based on 725 #full name of the initial caselog file 726 set initcaseid $caseid 727 set initcaselog_file $caselog_file 728 729 set newdmpfilebasename $dmpfilebasename 730 731 stty -raw 732 interact { 733 #wait for match from user, if timeout then send a blank then delete it 734 #this bring an anti-idle feature 735 timeout $anti_idle_timeout { 736 send $anti_idle_string 737 } 738 # 739 #under interact mode, use some keystroke cmds to instruct 740 #script to do some other automations 741 #-echo make keystokes matching listed patterns also display 742 #side effect is duplicate display if partial pattern were being inputted 743 #use ! to make these patterns unlikely to be accidently duplicated with other cmds for spawned app 744 745 -echo -re "!i" { 746 puts "\navailable cmds under interact mode:" 747 puts " 748 EXCUTIONS: ATTACHING (logs/coredump decode): 749 --execute precmds-- !alx(x:m/c/b) (a)ttach curr (l)og:$currlog_file 750 !eP (e)xcute actions in '(p)rework' with caseid $caseid (via email) 751 pattern-action-list !alm email to me($emailme) only 752 !ep (e)xecute cmds in (p)recmds list !alc email to case($emailcase) only 753 (term len/width/etc) !alb email to both $emailme and $emailcase 754 !alt a@b email to a@b.com, t.b.c 755 --execute/repeat(and email logs of) commands-- !adx (A)ttach decoded coredump 756 --in 'cmds','CGS'-- files under /mnt/coredumps/$caseid (via email) 757 !ec (e)xecute whatever in '(c)mds' 758 !eCx (x:m/c/b) same,also e(m)ail logs ABBREVIATIONS: 759 with caseid $caseid !b<KEY>. send the corresponding long 760 !rc same,but (r)epeatedly cmd strings based on the key defined in 761 !eg (e)xecute whatever in 'CGS',recursively ABB(KEY) array 762 !rg same, but repeatedly 763 LOGGINGS: 764 --execute/repeat(and email logs of) cmds in-- !lf change (l)og file based on 765 ---'pattern-action-list','PAGS'-- caseid specified in config (f)ile:$caselog_file 766 !ea (e)xecute 'pattern_action_list' !lc CASEID ..based on (c)aseid , 767 !eAx (x:m/c/b) same, plus emails e,g: 1111-1111-1111.log 768 !eG (e)xecute PAGS,recursive !lC CASEID ..based on (c)aseid plus 769 !ra<CR> same as !ea, (r)epeat $maxrounds host name $caseid-$host.log 770 !ra N<CR> same, N rounds !li stop current (l)og, 771 !rG<CR> (r)epeat PAGS for $maxrounds return to (i)nitial log:$initlog_file 772 !rG N (r)epeat PAGS for N rounds !la NAME<CR> change (l)og file to an 773 (a)rbitrary name (!la mylog<CR>) under $log_dir 774 --execute or execute recursively cmds in !lA FULLPATH<CR> change (l)og file to another 775 --user-defined cmd groups or PA groups-- full path name logfile:!lA /a/b/c.txt 776 !mlx(x:c/p/s) (l)list currently a/v groups !ls (s)top (l)og recording on 777 (cmd,pa,pa group for SHELL),from CGL,PAGL current logfile:$currlog_file 778 !mg CMD_GROUP<CR> (e)xecute one cmd grp,no recursive !lr same, resume it 779 !mG PA_GROUP<CR> (e)xecute 1 PA group,recursively !lS (S)how current (l)ogfile 780 !mS PA_GROUP<CR> same,as host SHELL,recursively name:$currlog_file 781 782 !Hl (h)ost (l)ist MISC: 783 !h this (h)elp 784 --execute/email coredump-- !dd (d)efine a new (d)mp file 785 !ed (e)xecute core(d)ump analysis name:$dmpfilebasename,obsolete 786 !eDx (x:m/c/b) same, plus sending emails !da set arbitary values, 787 not generalized yet, only a/v in my PC (set caseid 2222-2222-2222),obsolete 788 !T a mini tutorial 789 !h a list of most frequently used commands 790 791 --not finished features-- 792 !t HOSTNAME<CR> !s HOSTNAME<CR>: telnet/ssh login to HOSTNAME, which will be resolved to HOST(IP addres) 793 the intention here is to remove dependency on /etc/hosts, which is not changable without root priv. 794 ver 0.2 795 " 796 } 797 798 -echo -re "!h" { 799 puts "\nfrequently used cmds under interact mode:" 800 puts " 801 --frequently used cmds-- 802 !lc CASEID (=>log name: $caseid.log) or !da set host xo<CR> !lC 1111-1111-1111(=>log name: $caseid$host.log) 803 !ls !lr (s)top/(r)esume (l)og recording on current logfile:$currlog_file 804 !mS pre<CR> or !ep then !eg or !eCb (=>exec CLIes) !eDm (=> execute dmp decode, currently only a/v on my own pc) 805 !mS scott_check<CR> (=>run scott script) !mS cpucheck<CR> (=>run KA34030 high CPU check) 806 807 type !T for a mini tutorial 808 type !i for more info/cmds 809 ver 0.2 810 " 811 } 812 813 -echo -re "!T" { 814 puts "\na quick tutorial for most frequently used commdands:" 815 puts " 816 :by default, once started, everything will be logged in a file under \$log_dir(current:$log_dir), 817 :in a filename defined in \$mylogfile (current:$mylog_file), to change the log file name 818 :to current case number, type following command. 819 820 :this will change the logfile to a filename defined in \$caselog_file (current:$caselog_file) 821 :under a folder \$caselog_dir (current:$caselog_dir) these VARs are define in file:~/.mylogin/coredump.conf 822 !lc 2011-0511-0012 823 824 :this is normal telnet(ssh) command to login remote system 825 telnet 1.1.1.1 826 ...//..username,password,jumpstations,token,etc..//... 827 :assume we are now in privilidge mode of E320 828 ABC-VFTTP-120# 829 830 :type following, then hit enter (<CR>), this will adjust the term width/len/acquire a shell session and exit 831 !mS pre 832 :type following, no need enter, this will start some frequently used CLIes (not vxShell), defined under ~/.mylogin/cmds-data.conf 833 :it will also send an email after it finished (read below) 834 !eg 835 836 :wait until it finished, type following to send another email anytime you want the logs, 837 :to whoever defined in \$emailme(currently is $emailme), $emailcase (currently $emailcase) in file ~/.mylogin/mylogin.conf 838 :this is convenient to (a)ttach (l)og to (me), to (c)ase, or to (b)oth 839 !alm (or !alc) (or !alb) 840 841 :sometimes its unavoidable to run some script, !mS <SCRIPT> is the command, SCRIPT need to be defined in a file under ~/.mylogin/ 842 :here is an example to run scott's data-collection CLIes and attach the log in an email, 843 :the SCRIPT file was ~/.mylogin/scott_check.conf, you can check that file and 844 :just follow its simply syntax you can define your own new script file, 845 :type following and hit enter 846 !mS scott_check 847 848 :there are some other misc commands/features available, in some cases they are useful 849 :to stop the logging 850 !ls 851 :to resume 852 !lr 853 854 type !h for a brief list of most frequently used commands 855 type !i for more info/cmds 856 " 857 } 858 859 #log stop 860 -echo "!ls" { 861 if $log_started { 862 myputs "stopped log recording on file $currlog_file" 863 log_file 864 set log_started 0 865 } else { 866 myputs "nothing to stop, log not started yet" 867 } 868 } 869 870 #log show 871 -echo "!lS" { 872 if $log_started { 873 myputs "current log file:$currlog_file" 874 } else { 875 myputs "currently no log has been started yet" 876 } 877 } 878 879 #log view, not done yet 880 -echo "!lv" { 881 if $log_started { 882 myputs "viewing current log file:$currlog_file" 883 myexec "less [glob $currlog_file]" 884 } else { 885 myputs "currently no log has been started yet" 886 } 887 } 888 889 #log resume/start 890 -echo "!lr" { 891 #expect:need to stop first in order to "resume" it 892 log_file 893 log_file $currlog_file 894 myputs "resume log recording to file $currlog_file" 895 set log_started 1 896 } 897 898 #log change per config file 899 -echo "!lf" { 900 #get new caselog_file name from config(which is based on caseid) 901 if $redosource {source $configfile} 902 #expect:need to stop first in order to "resume" it 903 log_file 904 log_file $caselog_file 905 set currlog_file $caselog_file 906 myputs "resume log recording to file $currlog_file" 907 set log_started 1 908 } 909 910 #log return to initial file 911 -echo "!li" { 912 #get new caselog_file name from config(which is based on caseid) 913 if $redosource {source $configfile} 914 #expect:need to stop first in order to "resume" it 915 log_file 916 log_file $initlog_file 917 myputs "stop log on $currlog_file, resume on initial log file $initlog_file" 918 set currlog_file $initlog_file 919 set log_started 1 920 } 921 922 #log change to caseid 923 -echo -re "!lc $caseid_pattern|!lC $caseid_pattern" { 924 #if $redosource {source $configfile} 925 #get user input 926 set a $interact_out(0,string) 927 #scan the input and find what followed "!l " and use it as newcaseid 928 if {[regexp {^(!l.) (.*)} $a -> cmd_pref newcaseid] eq 1} { 929 #if {[scan $a "!lc %s" newcaseid] eq 1} 930 if $DEBUG {myputs "get new caseid $newcaseid"} 931 #stop old log and start on the new file name 932 log_file 933 if {$cmd_pref == "!lc"} { 934 #replace caseid in the caselog_file full name and get new caselogfile name 935 set newcaselog_file [string map [list $caseid $newcaseid] $caselog_file] 936 } elseif {$cmd_pref == "!lC"} { 937 set newcaselog_file [string map [list $caseid "$newcaseid-$host"] $caselog_file] 938 } else { 939 #place holder 940 } 941 log_file $newcaselog_file 942 myputs "stop logging on:$currlog_file,continue on:$newcaselog_file" 943 set currlog_file $newcaselog_file 944 #because other cmd also my do re-source beforehand, following may not work 945 #myputs "use newcaseid:$newcaseid instead of old caseid:$caseid" 946 #set caseid newcaseid 947 set log_started 1 948 } else { 949 myputs "nothing found in inputted string" 950 } 951 } 952 953 954 #dummy codes, for test purpose only 955 -echo -re "!zl." { 956 set a $interact_out(0,string) 957 if {[string compare $a "!zl1"] eq 0} { 958 myputs "tests:ch followed !zl is 1" 959 } elseif {[string compare $a "!zl2"] eq 0} { 960 myputs "tests:ch followed !zl is 2" 961 } else { 962 myputs "tests:ch followed !zl is not 1 or 2" 963 } 964 } 965 966 #log attach 967 -echo -re "!al." { 968 #get new caselog_file name from config(which is based on caseid) 969 if $redosource {source $configfile} 970 #update the caseid,if ever changed (via !lc) 971 set caseid $newcaseid 972 973 974 # if $DEBUG {myputs "value of zip is $zip"} 975 if $log_started { 976 set a $interact_out(0,string) 977 if $DEBUG {myputs "newcaseid:$newcaseid;currlog:$currlog_file"} 978 979 if {[string compare $a "!alm"] eq 0} { 980 sendanemail $newcaseid $currlog_file $emailme 981 } elseif {[string compare $a "!alc"] eq 0} { 982 sendanemail $newcaseid $currlog_file $emailcase 983 } elseif {[string compare $a "!alb"] eq 0} { 984 sendanemail $newcaseid $currlog_file $emailme $emailcase 985 } else { 986 myputs "currently only support !alm(me) !alc(case) !alb(both)" 987 } 988 989 #t.b.d: send curr log to any other emailaddress 990 if $NEWFEATURE { 991 -echo -re "!alt .*@.*\r" { 992 #if $redosource {source $configfile} 993 #get user input 994 set a $interact_out(0,string) 995 #scan the input and find what followed "!v " and use it as newcaseid 996 set tclcmd [string range $a 4 end] 997 #if {[string compare $tclcmd ""] eq 0} 998 if {[string match {*[a-zA-Z]*} $tclcmd]} { 999 if $DEBUG {myputs "\nget tclcmd $tclcmd"} 1000 if {[catch {eval $tclcmd} msg]} { 1001 myputs "wrong syntax with the inputed command!" 1002 myputs "the error is:$::errorInfo" 1003 } 1004 1005 if $DEBUG {myputs "caseid is now $caseid"} 1006 #replace caseid in the caselog_file full name and get new caselogfile name 1007 } else { 1008 myputs "nothing found in inputted string" 1009 } 1010 } 1011 } 1012 } else { 1013 myputs "log has not been started yet, nothing to send!" 1014 } 1015 } 1016 1017 #log(decoded dump file) attach 1018 -echo -re "!ad." { 1019 #get new caselog_file name from config(which is based on caseid) 1020 if $redosource {source $configfile} 1021 #update the caseid,if ever changed (via !L) 1022 set caseid $newcaseid 1023 1024 set a $interact_out(0,string) 1025 if {$a eq "!adm"} { 1026 sendanemail $newcaseid "$dmp_upload_dir1*decode*" $emailme 1027 } elseif {$a eq "!adc"} { 1028 sendanemail $newcaseid "$dmp_upload_dir1*decode*" $emailcase 1029 } elseif {$a eq "!adb"} { 1030 sendanemail $newcaseid "$dmp_upload_dir1*decode*" $emailme $emailcase 1031 } else { 1032 myputs "currently only support !adm(me) !adc(case) !adb(both)" 1033 } 1034 } 1035 1036 -echo "!q" { 1037 myputs " uit interact mode" 1038 return 1039 } 1040 1041 1042 #dumpfile define 1043 -echo -re "!dd .*\r" { 1044 #if $redosource {source $configfile} 1045 #get user input 1046 set a $interact_out(0,string) 1047 #scan the input and find what followed "!d " and use it as newcaseid 1048 if {[scan $a "!dd %s" newdmpfilebasename] eq 1} { 1049 if $DEBUG {myputs "\nget newdmpfilebasename $newdmpfilebasename"} 1050 1051 } else { 1052 myputs "nothing found in inputted string" 1053 } 1054 } 1055 1056 #define arbitrary things (using tcl systax) 1057 -echo -re "!da set.*\r" { 1058 #if $redosource {source $configfile} 1059 #get user input 1060 set a $interact_out(0,string) 1061 #scan the input and find what followed "!v " and use it as newcaseid 1062 set tclcmd [string range $a 4 end] 1063 #if {[string compare $tclcmd ""] eq 0} 1064 if {[string match {*[a-zA-Z]*} $tclcmd]} { 1065 if $DEBUG {myputs "\nget tclcmd $tclcmd"} 1066 if {[catch {eval $tclcmd} msg]} { 1067 myputs "wrong syntax with the inputed command!" 1068 myputs "the error is:$::errorInfo" 1069 } 1070 1071 if $DEBUG {myputs "caseid is now $caseid"} 1072 #replace caseid in the caselog_file full name and get new caselogfile name 1073 } else { 1074 myputs "nothing found in inputted string" 1075 } 1076 } 1077 1078 1079 #log with arbitrary name, and... 1080 #clean the log (remove escape, backspace, etc) doesn't work well yet 1081 -echo -re "!la .*\r|!lA .*\r" { 1082 #if $redosource {source $configfile} 1083 #get user input 1084 set a $interact_out(0,string) 1085 set newlogbasename 1 1086 set cmd_pref 1 1087 #extract cmd string,a blank,everything until (but excluding the end ^M--don't know why) 1088 if {[regexp {^(!l.) (.*).} $a -> cmd_pref newlogbasename] eq 1} { 1089 if $DEBUG { 1090 myputs "get new logfile:$newlogbasename" 1091 myputs "initcaseid:$initcaseid,initcaselog_file:$initcaselog_file" 1092 } 1093 if {$cmd_pref == "!la"} { 1094 #in initial caselog_file full name, replace caseid part with the newly 1095 #acquired newlogbasename and get new caselogfile full name 1096 set newcaselog_file [string map [list $initcaseid $newlogbasename] $initcaselog_file] 1097 } elseif {$cmd_pref == "!lA"} { 1098 set newcaselog_file $newlogbasename 1099 } else { 1100 myputs "currently only !la and !lA are supported!" 1101 } 1102 1103 #now change log to newcaselog_file 1104 log_file 1105 if {[mylog_file $newcaselog_file] == 0} { 1106 myputs "stop logging on:$currlog_file,continue on:$newcaselog_file" 1107 set currlog_file $newcaselog_file 1108 set log_started 1 1109 } else { 1110 myputs "command failed,restore old logs" 1111 log_file $currlog_file 1112 } 1113 } else { 1114 myputs "nothing found in inputted string" 1115 } 1116 1117 if $NEWFEATURE { 1118 incr n 1119 if { \ 1120 [catch \ 1121 {exec \ 1122 screen -X scrollback [exec wc -l [glob $currlog_file]]; \ 1123 } \ 1124 msg \ 1125 ] \ 1126 } { 1127 myputs "Something seems to have gone wrong:" 1128 myputs "Information about it: $::errorInfo" 1129 } 1130 1131 if { \ 1132 [catch \ 1133 {exec \ 1134 cat [glob $currlog_file]; \ 1135 } \ 1136 msg \ 1137 ] \ 1138 } { 1139 myputs "Something seems to have gone wrong:" 1140 myputs "Information about it: $::errorInfo" 1141 } 1142 if { \ 1143 [catch \ 1144 {exec \ 1145 screen -X hardcopy -h [glob $currlog_file]-clean$n.log \ 1146 } \ 1147 msg \ 1148 ] \ 1149 } { 1150 myputs "Something seems to have gone wrong:" 1151 myputs "Information about it: $::errorInfo" 1152 } 1153 myputs "log file $currlog_file is now cleaned as $currlog_file-clean$n.log" 1154 } 1155 } 1156 1157 #execute precmds 1158 -echo -re "!doprecmds|!ep" { 1159 if $redosource {source $configfile} 1160 do_cmds $pattern4cmd precmds $cmds_intv $cmd_timeout 1161 unset precmds 1162 } 1163 1164 #execute cmds 1165 -echo -re "!docmds|!ec" { 1166 if $DEBUG {myputs "resourcing config file $configfile"} 1167 if $redosource {source $configfile} 1168 do_cmds $pattern4cmd cmds $cmds_intv $cmd_timeout 1169 if $DEBUG {myputs "commands finished"} 1170 #clear the array after done, give next execution a fresh start 1171 #this is useful when you want to remove a command out of cmds 1172 unset cmds 1173 } 1174 1175 #same as !ec but send emails 1176 -echo -re "!eC." { 1177 set a $interact_out(0,string) 1178 if { ($a eq "!eCm") || ($a eq "!eCc") || ($a eq "!eCb") \ 1179 } { 1180 if $DEBUG {myputs "resourcing config file $configfile"} 1181 if $redosource {source $configfile} 1182 do_cmds $pattern4cmd cmds $cmds_intv $cmd_timeout 1183 if $DEBUG {myputs "commands finished,destruct cmds"} 1184 unset cmds 1185 } else { 1186 } 1187 1188 #send email 1189 set a $interact_out(0,string) 1190 if {[string compare $a "!eCm"] eq 0} { 1191 sendanemail $newcaseid $currlog_file $emailme 1192 } elseif {[string compare $a "!eCc"] eq 0} { 1193 sendanemail $newcaseid $currlog_file $emailcase 1194 } elseif {[string compare $a "!eCb"] eq 0} { 1195 sendanemail $newcaseid $currlog_file $emailme $emailcase 1196 } else { 1197 myputs "currently only support !eCm(me) !eCc(case) !eCb(both)" 1198 } 1199 } 1200 1201 #repeat cmds 1202 -echo -re "!repcmds|!rc" { 1203 if $redosource {source $configfile} 1204 repeat_cmds $maxrounds $pattern4cmd cmds $cmds_intv $cmd_timeout 1205 } 1206 1207 #execute cmds groups 1208 -echo -re "!docmdsgrps|!eg" { 1209 myputs "start cmds groups" 1210 if $redosource {source $configfile} 1211 do_cmds_groups $pattern4cmd $CGS $cmds_groups_intv $cmds_intv $cmd_timeout 1212 #send email 1213 sendanemail $newcaseid $currlog_file $emailme 1214 1215 #foreach cmds_group $CGS { 1216 # unset $cmds_group 1217 # } 1218 unset CGS 1219 } 1220 1221 #repeat cmds groups 1222 -echo -re "!rg" { 1223 myputs "repeat cmds groups" 1224 if $redosource {source $configfile} 1225 repeat_cmds_groups $maxrounds $pattern4cmd $CGS $round_intv $cmds_groups_intv $cmds_intv $cmd_timeout 1226 } 1227 1228 #execute pattern-action pairs and optionally send email 1229 -echo -re "!dopa|!ea|!eA." { 1230 if {$host eq "SHELL"} { 1231 myputs "host is $host" 1232 myputs "warning: dump analysis requirs special filename other than the currlog_file:$currlog_file" 1233 myputs " so better use !ed !eDx to do dump analysis under local shell" 1234 } 1235 1236 if $redosource {source $configfile} 1237 set a $interact_out(0,string) 1238 #it looks: 1239 #1) string compare can be as simple as $a eq "a", string compare is also ok 1240 #2) || looks doesn't work with '\' 1241 if { ($a eq "!ea") || ($a eq "!eAm") || ($a eq "!eAc") || ($a eq "!eAb") \ 1242 } { 1243 do_patterns_actions $host $pattern_action_timeout pattern_action_list $pattern_action_intv 1244 } else { 1245 myputs "invalid cmd $a,currently only support !eAm(me) !eAc(case) !eAb(both)" 1246 } 1247 1248 #also send emails with these cmds 1249 if { ($a eq "!eAm") || ($a eq "!eAc") || ($a eq "!eAb") \ 1250 } { 1251 1252 if {[string compare $a "!eAm"] eq 0} { 1253 sendanemail $newcaseid $currlog_file $emailme 1254 } elseif {[string compare $a "!eAc"] eq 0} { 1255 sendanemail $newcaseid $currlog_file $emailcase 1256 } elseif {[string compare $a "!eAb"] eq 0} { 1257 sendanemail $newcaseid $currlog_file $emailme $emailcase 1258 } else { 1259 myputs "invalid cmd $a,currently only support !eAm(me) !eAc(case) !eAb(both) " 1260 } 1261 1262 } 1263 1264 #destruct/fresh the data when done 1265 unset pattern_action_list 1266 } 1267 1268 #repeat pattern_action_list for $maxrounds 1269 #or repeat it for given rounds 1270 -echo -re {!ra\r|!ra [0-9]+\r} { 1271 if {$host eq "SHELL"} { 1272 myputs "host is $host" 1273 myputs "warning: better use !ed !eDx to do dump analysis under local shell" 1274 } 1275 1276 if $redosource {source $configfile} 1277 set rounds 1 1278 set a $interact_out(0,string) 1279 1280 if { $a eq "!ra\r" } { 1281 if $DEBUG {myputs "round not set, use maxrounds value in config file:$maxrounds} 1282 set rounds $maxrounds 1283 repeat_patterns_actions $rounds $host $pattern_action_timeout pattern_action_list $pattern_action_intv 1284 } else { 1285 #scan the input and find what followed "!ra " and use it as max_rounds 1286 if {[scan $a "!ra %d" rounds] eq 1} { 1287 if $DEBUG {myputs "round is set to $rounds} 1288 repeat_patterns_actions $rounds $host $pattern_action_timeout pattern_action_list $pattern_action_intv 1289 } else { 1290 myputs "rounds of actions are wrong!-$rounds/should be integer-" 1291 } 1292 } 1293 1294 #destruct/fresh the data when done 1295 unset pattern_action_list 1296 } 1297 1298 -echo -re {!rG\r|!rG [0-9]+\r} { 1299 if {$host eq "SHELL"} { 1300 myputs "host is $host" 1301 myputs "warning: better use !ed !eDx to do dump analysis under local shell" 1302 } 1303 1304 if $redosource {source $configfile} 1305 set rounds 1 1306 set a $interact_out(0,string) 1307 1308 if { $a eq "!rG\r" } { 1309 if $DEBUG {myputs "round not set, use maxrounds value in config file:$maxrounds} 1310 set rounds $maxrounds 1311 repeat_pags $rounds PAGS $host $pattern_action_timeout $pattern_action_intv $pattern_action_groups_intv 1312 sendanemail $newcaseid $currlog_file $emailme 1313 } else { 1314 #scan the input and find what followed "!rG " and use it as max_rounds 1315 if {[scan $a "!rG %d" rounds] eq 1} { 1316 if $DEBUG {myputs "round is set to $rounds} 1317 repeat_pags $rounds PAGS $host $pattern_action_timeout $pattern_action_intv $pattern_action_groups_intv 1318 sendanemail $newcaseid $currlog_file $emailme 1319 } else { 1320 myputs "rounds of actions are wrong!-$rounds/should be integer-" 1321 } 1322 } 1323 1324 #destruct/fresh the data when done 1325 unset PAGS 1326 } 1327 1328 -echo "!eG" { 1329 if {$host eq "SHELL"} { 1330 myputs "host is $host" 1331 myputs "warning: better use !ed !eDx to do dump analysis under local shell" 1332 } 1333 1334 set host1 $host 1335 set host "SHELL" 1336 if $redosource {source $configfile} 1337 do_pags PAGS $host $pattern_action_timeout $pattern_action_intv 1338 set host $host1 1339 } 1340 1341 1342 -echo "!mlc" { 1343 if $redosource {source $configfile} 1344 myputs "\ncurrently available command groups:\n$CGL\n" 1345 unset CGL 1346 } 1347 1348 -echo "!mlp" { 1349 if $redosource {source $configfile} 1350 myputs "\ncurrently available pattern action groups for $host:\n$PAGL($host)\n" 1351 unset CGL 1352 } 1353 1354 -echo "!mls" { 1355 if $redosource {source $configfile} 1356 set host1 $host 1357 set host SHELL 1358 myputs "\ncurrently available pattern action groups for SHELL:\n$PAGL($host)\n" 1359 set host $host1 1360 unset CGL 1361 } 1362 1363 -echo -re "!mg .*\r" { 1364 if $redosource {source $configfile} 1365 set a $interact_out(0,string) 1366 set cmd_group 1 1367 #scan the input and find what followed "!e " and use it as new caseid 1368 if {[scan $a "!mg %s" cmd_group] eq 1} { 1369 #eval global $pa_group 1370 if {[lsearch -exact $CGL $cmd_group] == -1} { 1371 myputs "the command group $cmd_group is not available in CGL!" 1372 myputs "\ncurrently available command groups:\n$CGL\n" 1373 } else { 1374 do_cmds $pattern4cmd $cmd_group $cmds_intv $cmd_timeout 1375 } 1376 } else { 1377 myputs "nothing found in inputted string" 1378 } 1379 } 1380 1381 1382 -echo -re "!mG .*\r|!mS .*\r" { 1383 if $redosource {source $configfile} 1384 1385 set a $interact_out(0,string) 1386 set pa_group 1 1387 #scan the input and find what followed "!mG " and use it as pa_group 1388 if {[scan $a "!mG %s" pa_group] eq 1} { 1389 #eval global $pa_group 1390 if {[lsearch -exact $PAGL($host) $pa_group] == -1} { 1391 myputs "the pattern action group $pa_group is not available in PAGL!" 1392 myputs "\ncurrently available pattern action groups:\n$PAGL($host)\n" 1393 } else { 1394 #do_patterns_actions $host $pattern_action_timeout $pa_group $pattern_action_intv 1395 do_pags $pa_group $host $pattern_action_timeout $pattern_action_intv 1396 #unset $pa_group 1397 } 1398 1399 } elseif {[scan $a "!mS %s" pa_group] eq 1} { 1400 #for !mR, execute what is configured for host "SHELL", regardless of hostname 1401 #backup curent hostname 1402 set host1 $host 1403 #treat host as if SHELL 1404 set host "SHELL" 1405 #do same as !mG 1406 if {[lsearch -exact $PAGL($host) $pa_group] == -1} { 1407 myputs "the pattern action group $pa_group is not available in PAGL!" 1408 myputs "\ncurrently available pattern action groups:\n$PAGL($host)\n" 1409 } else { 1410 #do_patterns_actions $host $pattern_action_timeout $pa_group $pattern_action_intv 1411 do_pags $pa_group $host $pattern_action_timeout $pattern_action_intv 1412 #unset $pa_group 1413 } 1414 1415 #send email 1416 sendanemail $newcaseid $currlog_file $emailme 1417 1418 #when done, recover hostname back 1419 set host $host1 1420 } else { 1421 myputs "nothing found in inputted string" 1422 } 1423 } 1424 1425 -echo "!Hl" { 1426 if $redosource {source $configfile} 1427 puts "\nhost table:\n" 1428 parray host2name 1429 puts "\nlogin info\n" 1430 parray login_info 1431 } 1432 1433 -echo -re "!b.*\\." { 1434 if $redosource {source $configfile} 1435 1436 set a $interact_out(0,string) 1437 set abbkey 1 1438 #two ways to extract string/CHs: it looks regexp is the best way! 1439 #regexp {c((.*)g)(.*)} "abcdefghi" matched sub1 sub2 sub3 1440 #if {[scan $a "!b%s\." abbkey] eq 1} 1441 #match string $a with a pattern, all matched part goes to special var "->" 1442 #then extract sub-string from matched part into $abbkey using () and regex 1443 if {[regexp {!b(.*)\.} $a -> abbkey] eq 1} { 1444 #eval global $pa_group 1445 if {[lsearch -exact [array names ABB] $abbkey] == -1} { 1446 myputs "abbreviation key:$abbkey is not configured in ABB, please double check!" 1447 myputs "\ncurrently available abbreviation keys are:\n" 1448 parray ABB 1449 } else { 1450 send_user "=>" 1451 send "$ABB($abbkey)" 1452 } 1453 } else { 1454 myputs "inputted $a is a wrong command!" 1455 } 1456 1457 unset ABB 1458 } 1459 1460 #new shell commands to auto-resolve the name to host 1461 #not finished, it doesn't work, for unknown reason 1462 -echo -re "!t .*\r|!s .*\r" { 1463 set a $interact_out(0,string) 1464 #scan the input and find what followed "!s " or "!t " and use it as hostname 1465 if {[regexp {^(!.) (.*)} $a -> protocol hostname] eq 1} { 1466 if $DEBUG {myputs "get protocol -$protocol- and hostname -$hostname-"} 1467 1468 if {[info exists host2name($hostname)]} { 1469 set host $host2name($hostname) 1470 if $DEBUG {myputs "get resolved for $hostname to $host"} 1471 1472 if {$protocol == "!t"} { 1473 send "\rtelnet $host" 1474 } elseif {$protocol == "!s"} { 1475 #this is not good supported, considering login name .. 1476 send "\rssh $host" 1477 } else { 1478 myputs "currently only resolve name for telnet/ssh..." 1479 } 1480 } else { 1481 myputs "\nhostname not resolved for -$hostname-, please use original telnet/ssh cmds" 1482 if $DEBUG { 1483 parray host2name 1484 } 1485 } 1486 } else { 1487 myputs "nothing found in inputted string" 1488 } 1489 } 1490 1491 1492 #coredump handling, one of the specialized action under local shell mode 1493 -echo -re "!dodump|!ed|!eD." { 1494 1495 if {$host ne "SHELL"} { 1496 myputs "host is $host" 1497 myputs "currently only support dump analysis under local shell" 1498 return 1 1499 } 1500 1501 if $redosource {source $configfile} 1502 set a $interact_out(0,string) 1503 1504 #speical handling for coredump analysis 1505 if {($a eq "!ed") || ($a eq "!eDm") || ($a eq "!eDc") || ($a eq "!eDb")} { 1506 if $DEBUG {myputs "caseid is now $caseid"} 1507 log_file 1508 #log to a seperated,fresh decode file(disable append) 1509 log_file -noappend $decodelog_file 1510 myputs "change log file to $decodelog_file" 1511 1512 #do_patterns_actions $host $pattern_action_timeout pattern_action_list $pattern_action_intv 1513 do_pags core $host $pattern_action_timeout $pattern_action_intv 1514 1515 log_file 1516 log_file $currlog_file 1517 myputs "change log file back to $currlog_file" 1518 1519 if {$a eq "!eDm"} { 1520 sendanemail $caseid "$dmp_upload_dir1*decode*" $emailme 1521 } elseif {$a eq "!eDc"} { 1522 sendanemail $caseid "$dmp_upload_dir1*decode*" $emailcase 1523 } elseif {$a eq "!eDb"} { 1524 sendanemail $caseid "$dmp_upload_dir1*decode*" $emailme $emailcase 1525 } else { 1526 1527 } 1528 1529 } else { 1530 myputs "invalid cmd $a,currently only support !ed !eDm !eDc !eDb" 1531 } 1532 1533 unset pattern_action_list 1534 } 1535 1536 #execute preworks 1537 -echo -re "!eP" { 1538 #some pre-work, might be useful for interactions 1539 if $redosource {source $configfile} 1540 if $DEBUG {myputs "do some interactions here"} 1541 do_patterns_actions $host $pattern_action_timeout prework $pattern_action_intv 1542 unset prework 1543 } 1544 1545 #execute a block of tcl code 1546 -echo -re "!eb" { 1547 if $redosource {source $configfile} 1548 myputs "going to eval code:-$tclblock-" 1549 set temp [eval $tclblock] 1550 } 1551 1552 1553 # -reset "\032" { 1554 # exec kill -STOP 0 1555 # } 1556 1557 # -o 1558 # "error" { 1559 # myputs "!some errors were found!want to run diag?(y/n)" 1560 # diag_on_error 1561 # } 1562 1563 } 1564 } 1565 1566 ##############################main program################################### 1567 #include('source' in tcl context) config and lib files 1568 #check if file is readable before hand 1569 #set files [ list $configfile $libfile ] 1570 set files [ list $configfile ] 1571 1572 foreach file $files { 1573 if {[file readable $file]} { 1574 puts "\[[exec date]: sourcing file $file\]" 1575 source $file 1576 } else { 1577 puts "\[[exec date]: file $file doesn't exists\n" 1578 exit 1 1579 } 1580 } 1581 if $DEBUG {myputs "checking log_dir $log_dir ...\n"} 1582 if {[file exists $log_dir]} { 1583 if $DEBUG {send_user "...existing!\n"} 1584 } else { 1585 send_user "dir $log_dir didn't exist, creating one...\n" 1586 if [catch {file mkdir $log_dir} failed_reason] { 1587 send_user "failed to creating dir $log_dir: $failed_reason\n" 1588 exit 1 1589 } else { 1590 send_user "...done!\n" 1591 } 1592 } 1593 1594 if $DEBUG {myputs "checking log_dir $caselog_dir ...\n"} 1595 if {[file exists $caselog_dir]} { 1596 if $DEBUG {send_user "...existing!\n"} 1597 } else { 1598 send_user "dir $caselog_dir didn't exist, creating one...\n" 1599 if [catch {file mkdir $caselog_dir} failed_reason] { 1600 send_user "failed to creating dir $caselog_dir: $failed_reason\n" 1601 exit 1 1602 } else { 1603 send_user "...done!\n" 1604 } 1605 } 1606 1607 set initlog_file $mylog_file 1608 #start logging 1609 if $log_when_start { 1610 log_file $mylog_file 1611 if $DEBUG {myputs "start logging on initial log_file:$initlog_file"} 1612 } else { 1613 if $DEBUG {myputs "log_when_start flag not set, log not enabled"} 1614 } 1615 1616 1617 if $do_log_user { 1618 1619 } else { 1620 myputs "depress the interaction details" 1621 log_user 0 1622 } 1623 1624 #this is not well designed yet 1625 set code 1 1626 1627 #retrieve logon info from another file,only if not in config file, to the global array 1628 loaddata $loginfile "logininfo" login_info 1629 1630 #simple paramaters handling,omit params for quick test 1631 if {$argc==0} { 1632 #to spawn another shell 1633 spawn $env(SHELL) 1634 set code 0 1635 #set hostname to "shell" since no remote host is involved here 1636 if $DEBUG {myputs "no parameter followed, set hostname to 'shell'"} 1637 set host SHELL 1638 1639 } elseif {$argc==1} { 1640 usage 1641 if $DEBUG {myputs "no parameter followed the protocol, set hostname to 'localhost'"} 1642 if $DEBUG {myputs "this is just for quick test"} 1643 set hostname "localhost" 1644 spawn [lindex $argv 0] $hostname 1645 1646 } else { 1647 #get hostname from command line 1648 set proto [lindex $argv 0] 1649 if $DEBUG {myputs "detected protocol:$proto"} 1650 1651 #for telnet,hostname can be retrieved from 2nd argv 1652 if {$proto=="telnet"} { 1653 set host [lindex $argv 1] 1654 if $DEBUG {myputs "detected host:$host in $proto session"} 1655 1656 } elseif {$proto=="ssh"} { 1657 #for ssh, just get the last param 1658 #this is safe only if host entry has been well configured in .ssh/config 1659 set lastparam [lindex $argv [expr $argc - 1] ] 1660 if $DEBUG {myputs "detected host:$lastparam in $proto session"} 1661 #in theory, need more work here to handle other ssh usage: 1662 # e.g.: ssh ping@host, ssh host -l ping, ... 1663 set host $lastparam 1664 } else { 1665 myputs "probably protocols excepts telnet/ssh are not tested at this moment!" 1666 } 1667 1668 #spawn telnet/ssh/ftp/rsync/whatever tools,followed by their native params 1669 #this is one of the way in tcl/expect to support more params 1670 #w/o 'eval' this won't work 1671 1672 #name resolving function: check if host can be resolved from config file 1673 #use the resolved info if possible 1674 if {[info exists host2name($host)]} { 1675 set hostname $host2name($host) 1676 if $DEBUG {myputs "get resolved for $host to $hostname"} 1677 eval spawn [lrange $argv 0 [expr $argc-2]] $hostname 1678 } else { 1679 1680 if $DEBUG { 1681 parray host2name 1682 myputs "hostname not resolved for $host,use original"} 1683 eval spawn [lrange $argv 0 end] 1684 } 1685 } 1686 1687 #use these to prefix all cmd output with a timestamp, not well done yet 1688 if $NEWFEATURE { 1689 expect_background -re "\[^ \]+\n" { 1690 send_user "following output was caught at [exec date]\n$expect_out(0,string)" 1691 } 1692 } 1693 1694 1695 #different branches based on where we are 1696 if {$code=="0"} { 1697 #if spawn a local shell, no need autologin, go interact 1698 if {$doprework=="0"} { 1699 do_interact 0 1700 } 1701 } else { 1702 #otherwise, check if autologin feature is enabled 1703 #if yes,try autologin 1704 if $autologin { 1705 if $DEBUG {myputs "autologin flag set, try autologin"} 1706 do_patterns_actions $host $login_timeout login_info $pattern_action_intv 1707 1708 1709 #autologin retry feature, not well done yet 1710 #if spawned process failed, looks the whole script exit 1711 1712 set autologin_fail [myexpect "check if login success for the 1st time" $success_login_pattern "\r" $login_timeout] 1713 1714 #if autologin failed, check if retry feature is enabled 1715 if $autologin_fail { 1716 #if yes, do retry 1717 set login_retry_fail [do_autologin_retry $max_login_retry $success_login_pattern $login_timeout $pattern_action_intv] 1718 1719 #if retry also failed, based on ... 1720 #not sure this part of logic is correct or not 1721 if $login_retry_fail { 1722 #if set, force(no need user confirm) interact mode when login not succeed 1723 if $interact_force_login_nok { 1724 puts "interact_force_login_nok set, force to interace mode" 1725 do_interact 1 1726 } else { 1727 #failover to manual retry feature: if auto logon failed, continue to do it manually 1728 #looks {} is not necessary here for if 1729 myputs "auto login failed..\n..want to retry manually?(y/n)" 1730 #raw mode for a while 1731 stty raw 1732 expect_user { 1733 "y" { 1734 myputs "go interact mode on user's request" 1735 do_interact 2 1736 #stty -raw 1737 } 1738 "n" { 1739 myputs "..exit on no..\n" 1740 exit 1 1741 } 1742 default { 1743 myputs "..exit on default..\n" 1744 exit 1745 } 1746 } 1747 1748 } 1749 } else { 1750 myputs "auto login succeed after retry" 1751 } 1752 } else { 1753 myputs "auto login succeed" 1754 } 1755 1756 1757 } else { 1758 #if autologin not enabled, go interact(manual login) 1759 myputs "autologin not set, go interact" 1760 do_interact 5 1761 } 1762 } 1763 1764 loaddata $cmdsfile "cmds" cmds 1765 1766 if $doprework { 1767 } 1768 1769 #base on config, go interact or auto mode after success login 1770 if $interact_after_login_ok { 1771 #go interact mode on login, if flag set 1772 do_interact 3 1773 } else { 1774 #otherwise proceed with automode 1775 #some pre-set commands for every login 1776 # show clock, term wid/length, etc 1777 puts "\nsome pre-checkings..\n" 1778 if $DEBUG {parray precmds} 1779 #executes all CLIes in precmds@2s interval, each wait 10s for outputs 1780 do_cmds $pattern4cmd precmds 2 5 1781 1782 #executes pre-defined cmds for configured rounds 1783 repeat_cmds $maxrounds $pattern4cmd cmds $cmds_intv $cmd_timeout 1784 1785 #if all done,handover control to user if configured 1786 if $interact_after_alldone { 1787 do_interact 4 1788 } else { 1789 exit 1790 } 1791 } 1792 1793 #capture eol from spawn when session exit 1794 #this feature doesn't work yet 1795 if $NEWFEATURE { 1796 1797 expect { 1798 eol {puts "eol received and exit"} 1799 } 1800 1801 } 1802 1803 # send "got <$expect_out(buffer)>" 1804 # send "but only <$expect_out(0,string)> was expected" 1805 1806