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