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