1#!/usr/bin/expect 2 3# This file provides many valuable expect procedures like handle_timeout and handle_eof. 4 5my_source [list print.tcl] 6 7 8proc handle_timeout { description } { 9 10 # Print timeout error message to stderr and exit 1. 11 12 # Description of argument(s): 13 # description A description of what was being expected (e.g. "an SOL login prompt"). 14 15 global spawn_id 16 global expect_out 17 18 set timeout [get_stack_var timeout {} 2] 19 20 if { $timeout == 1 } { 21 set seconds "second" 22 } else { 23 set seconds "seconds" 24 } 25 26 puts stderr "" 27 print_error "Did not get ${description} after $timeout ${seconds}.\n" 28 # Using uplevel to be able to access expect_out. 29 if { [ catch {uplevel { puts stderr [sprint_var expect_out]}} result ] } { 30 puts stderr [sprint_varx expect_out "<not set>"] 31 } 32 # If caller has exit_proc defined, call it. Otherwise, just call exit. 33 if { [info procs "exit_proc"] != "" } { 34 exit_proc 1 35 } 36 exit 1 37 38} 39 40 41proc handle_eof { description } { 42 43 # Print end-of-file error message to stderr and exit 1. 44 45 # Description of argument(s): 46 # description A description of what was being expected (e.g. "an SOL login prompt"). 47 48 global spawn_id 49 50 puts stderr "" 51 print_error "Reached end of file before getting $description.\n" 52 # Using uplevel to be able to access expect_out. 53 if { [ catch {uplevel { puts stderr [sprint_var expect_out]}} result ] } { 54 puts stderr [sprint_varx expect_out "<not set>"] 55 } 56 # If caller has exit_proc defined, call it. Otherwise, just call exit. 57 if { [info procs "exit_proc"] != "" } { 58 exit_proc 1 59 } 60 exit 1 61 62} 63 64 65proc expect_wrap {pattern_list message {timeout 15} {fail_on_timeout 1}} { 66 67 # Run the expect command for the caller and return the list index of the matching pattern. 68 69 # This function offers the following benefits over calling the expect command directly: 70 # - It makes program debug easier. When the program is run with --debug=1, this function prints useful 71 # debug output. 72 # - It will do standardized timeout and eof handling. 73 74 # Description of argument(s): 75 # pattern_list A list of patterns to be matched. If one of the patterns matches, the 76 # list index of the matching item will be returned. By default, each 77 # pattern is presumed to be a regex. If the caller wishes to, they may 78 # precede each pattern with either of the following: "-re ", "-gl " or "-ex 79 # " in order to explicitly choose the kind of match to be done.. 80 # message A message explaining what is being expected (e.g. "an SOL login prompt"). 81 # This will be included in output messages. 82 # timeout The expect timeout value. 83 # fail_on_timeout A flag governing the behavior when the expect command results in a 84 # timeout. If set to 1, this procedure will print an error message to 85 # standard error and exit the program with a non-zero return code. If set 86 # to 0, it will return [expect_wrap_timeout]. 87 88 # Example usage: 89 # set result [expect_wrap\ 90 # [list $bad_user_pw_regex "sh: xauth: command not found"]\ 91 # "an SOL prompt" 10] 92 # 93 # switch $result { 94 # 0 { 95 # puts stderr "" ; print_error "Invalid username or password.\n" 96 # exit_proc 1 97 # } 98 # 1 { 99 # dict set state ssh_logged_in 1 100 # } 101 # } 102 103 global spawn_id 104 global expect_out 105 106 # Recognized flags. 107 set flags [list "-re" "-ex" "-gl"] 108 109 # This helps debug efforts by removing leftover, stale entries. 110 array unset expect_out \[1-9\],string 111 112 # Prepare the expect statement. 113 append cmd_buf "global spawn_id\n" 114 append cmd_buf "global expect_out\n" 115 append cmd_buf "expect {\n" 116 set ix 0 117 foreach pattern $pattern_list { 118 # Check to see whether the caller has specified a flag (e.g. "-re", "-ex", etc.) at the beginning of the 119 # pattern. 120 set tokens [split $pattern " "] 121 if { [lsearch $flags [lindex $tokens 0]] != -1 } { 122 # Caller specified a flag. 123 set flag [lindex $tokens 0] 124 # Strip the flag from the pattern. 125 set pattern [string range $pattern 4 end] 126 } else { 127 set flag "-re" 128 } 129 append cmd_buf " ${flag} {$pattern} {set expect_result $ix}\n" 130 incr ix 131 } 132 if { $fail_on_timeout } { 133 append cmd_buf " timeout {handle_timeout \$message}\n" 134 } else { 135 append cmd_buf " timeout {set expect_result \[expect_wrap_timeout\]}\n" 136 } 137 append cmd_buf " eof {handle_eof \$message}\n" 138 append cmd_buf "}\n" 139 140 dprint_timen "Expecting $message." 141 dprint_issuing "\n${cmd_buf}" 142 eval ${cmd_buf} 143 144 dprintn ; dprint_vars expect_out expect_result 145 146 return $expect_result 147 148} 149 150 151proc expect_wrap_timeout {} { 152 153 # Return constant value of 1000. 154 155 return 1000 156 157} 158 159 160proc send_wrap {buffer {add_lf 1}} { 161 162 # Send the buffer to the spawned process. 163 164 # This function offers the following benefits over calling the send command directly: 165 # - It makes program debug easier. When the program is run with --debug=1, this function prints useful 166 # debug output. 167 168 # Description of argument(s): 169 # buffer The string to be sent to the spawned process. 170 # add_lf Send a line feed after sending the buffer. 171 172 # Example usage. 173 # Close the ssh session. 174 # send_wrap "~." 175 # 176 # set expect_result [expect_wrap\ 177 # [list "Connection to $host closed"]\ 178 # "a connection closed message" 5] 179 180 global spawn_id 181 global expect_out 182 183 set cmd_buf "send -- {${buffer}}" 184 dprint_issuing 185 eval ${cmd_buf} 186 187 if { $add_lf } { 188 send -- "\n" 189 set cmd_buf "send -- \"\\n\"" 190 dprint_issuing 191 eval ${cmd_buf} 192 } 193 194} 195 196 197proc shell_command {command_string {prompt_regex} { quiet {} } \ 198 { test_mode {} } { show_err {} } { ignore_err {} } {trim_cr_lf 1}} { 199 200 # Execute the command_string on the shell command line and return a list consisting of 1) the return code 201 # of the command 2) the stdout/stderr. 202 203 # It is the caller's responsibility to spawn the appropriate process (ssh,telnet) and to get the process 204 # to a shell command line (by logging in, etc.). 205 206 # Description of argument(s): 207 # command_string The command string which is to be run on the shell (e.g. "hostname" or 208 # "grep this that"). 209 # prompt_regex A regular expression to match the prompt for current shell to run on (e.g 210 # "/ #"). 211 # quiet Indicates whether this procedure should run the print_issuing() procedure 212 # which prints "Issuing: <cmd string>" to stdout. The default value is 0. 213 # test_mode If test_mode is set, this procedure will not actually run the command. 214 # If print_output is set, it will print "(test_mode) Issuing: <cmd string>" 215 # to stdout. The default value is 0. 216 # show_err If show_err is set, this procedure will print a standardized error report 217 # if the shell command returns non-zero. The default value is 1. 218 # ignore_err If ignore_err is set, this procedure will not fail if the shell command 219 # fails. However, if ignore_err is not set, this procedure will exit 1 if 220 # the shell command fails. The default value is 1. 221 # trim_cr_lf Trim any trailing carriage return or line feed from the result. 222 223 # Set defaults (this section allows users to pass blank values for certain args). 224 set_var_default quiet [get_stack_var quiet 0 2] 225 set_var_default test_mode 0 226 set_var_default show_err 1 227 set_var_default ignore_err 0 228 set_var_default acceptable_shell_rcs 0 229 230 global spawn_id 231 global expect_out 232 233 qprintn ; qprint_issuing ${command_string} ${test_mode} 234 235 if { $test_mode } { 236 return [list 0 ""] 237 } 238 239 send_wrap "${command_string}" 240 241 set expect_result [expect_wrap\ 242 [list "-ex $command_string"]\ 243 "the echoed command" 5] 244 set expect_result [expect_wrap\ 245 [list {[\n\r]{1,2}}]\ 246 "one or two line feeds" 5] 247 # Note the non-greedy specification in the regex below (the "?"). 248 set expect_result [expect_wrap\ 249 [list "(.*?)$prompt_regex"]\ 250 "command output plus prompt" -1] 251 # The command's stdout/stderr should be captured as match #1. 252 set out_buf $expect_out(1,string) 253 254 if { $trim_cr_lf } { 255 set out_buf [ string trimright $out_buf "\r\n" ] 256 } 257 258 # Get rc via recursive call to this function. 259 set rc 0 260 set proc_name [get_stack_proc_name] 261 set calling_proc_name [get_stack_proc_name -2] 262 if { $calling_proc_name != $proc_name } { 263 set sub_result [shell_command {echo ${?}} $prompt_regex 1] 264 dprintn ; dprint_list sub_result 265 set rc [lindex $sub_result 1] 266 } 267 268 if { $rc != 0 } { 269 if { $show_err } { 270 puts stderr "" ; print_error_report "The prior shell command failed.\n" 271 } 272 if { ! $ignore_err } { 273 if { [info procs "exit_proc"] != "" } { 274 exit_proc 1 275 } 276 } 277 } 278 279 return [list $rc $out_buf] 280 281} 282