blob: f8e876d74a629f3563f9efd57962f5267dfc3a54 [file] [log] [blame]
Michael Walshaa245bb2018-02-20 11:30:00 -06001#!/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
Michael Walsha0a42402018-02-27 10:50:40 -060017 global spawn_id
18 global expect_out
19
Michael Walshaa245bb2018-02-20 11:30:00 -060020 set timeout [get_stack_var timeout {} 2]
21
22 if { $timeout == 1 } {
23 set seconds "second"
24 } else {
25 set seconds "seconds"
26 }
Michael Walsha0a42402018-02-27 10:50:40 -060027
Michael Walshaa245bb2018-02-20 11:30:00 -060028 puts stderr ""
29 print_error "Did not get ${description} after $timeout ${seconds}.\n"
Michael Walsha0a42402018-02-27 10:50:40 -060030 # 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 }
Michael Walshaa245bb2018-02-20 11:30:00 -060034 # 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
Michael Walsha0a42402018-02-27 10:50:40 -060051 global spawn_id
52
Michael Walshaa245bb2018-02-20 11:30:00 -060053 puts stderr ""
54 print_error "Reached end of file before getting $description.\n"
Michael Walsha0a42402018-02-27 10:50:40 -060055 # 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 }
Michael Walshaa245bb2018-02-20 11:30:00 -060059 # 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}
Michael Walsha0a42402018-02-27 10:50:40 -060066
67
Joy Onyerikwu19d33f52018-03-05 15:30:56 -060068proc expect_wrap {pattern_list message {timeout 15} {fail_on_timeout 1}} {
Michael Walsha0a42402018-02-27 10:50:40 -060069
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.
Joy Onyerikwu19d33f52018-03-05 15:30:56 -060093 # fail_on_timeout A flag governing the behavior when the
Michael Walshe8899322018-04-18 11:45:24 -050094 # expect command results in a timeout. If
95 # set to 1, this procedure will print an
Joy Onyerikwu19d33f52018-03-05 15:30:56 -060096 # 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].
Michael Walsha0a42402018-02-27 10:50:40 -0600100
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 }
Joy Onyerikwu19d33f52018-03-05 15:30:56 -0600145 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 }
Michael Walsha0a42402018-02-27 10:50:40 -0600150 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
Michael Walshe8899322018-04-18 11:45:24 -0500163
Joy Onyerikwu19d33f52018-03-05 15:30:56 -0600164proc expect_wrap_timeout {} {
165
166 # Return constant value of 1000.
167
168 return 1000
169
170}
171
Michael Walsha0a42402018-02-27 10:50:40 -0600172
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}
Joy Onyerikwu56fd36a2018-04-10 10:09:07 -0500210
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
Michael Walshe8899322018-04-18 11:45:24 -0500216 # consisting of 1) the return code of the command 2) the stdout/stderr.
Joy Onyerikwu56fd36a2018-04-10 10:09:07 -0500217
218 # It is the caller's responsibility to spawn the appropriate process
Michael Walshe8899322018-04-18 11:45:24 -0500219 # (ssh,telnet) and to get the process to a shell command line (by logging
220 # in, etc.).
Joy Onyerikwu56fd36a2018-04-10 10:09:07 -0500221
222 # Description of argument(s):
Michael Walshe8899322018-04-18 11:45:24 -0500223 # 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.
Joy Onyerikwu56fd36a2018-04-10 10:09:07 -0500248
249 # Set defaults (this section allows users to pass blank values for certain
Michael Walshe8899322018-04-18 11:45:24 -0500250 # args).
Joy Onyerikwu56fd36a2018-04-10 10:09:07 -0500251 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
Michael Walshe8899322018-04-18 11:45:24 -0500308}