1aa245bbfSMichael Walsh#!/usr/bin/expect 2aa245bbfSMichael Walsh 3*410b1787SMichael Walsh# This file provides many valuable expect procedures like handle_timeout and handle_eof. 4aa245bbfSMichael Walsh 5aa245bbfSMichael Walshmy_source [list print.tcl] 6aa245bbfSMichael Walsh 7aa245bbfSMichael Walsh 8aa245bbfSMichael Walshproc handle_timeout { description } { 9aa245bbfSMichael Walsh 10aa245bbfSMichael Walsh # Print timeout error message to stderr and exit 1. 11aa245bbfSMichael Walsh 12aa245bbfSMichael Walsh # Description of argument(s): 13*410b1787SMichael Walsh # description A description of what was being expected (e.g. "an SOL login prompt"). 14aa245bbfSMichael Walsh 15a0a42403SMichael Walsh global spawn_id 16a0a42403SMichael Walsh global expect_out 17a0a42403SMichael Walsh 18aa245bbfSMichael Walsh set timeout [get_stack_var timeout {} 2] 19aa245bbfSMichael Walsh 20aa245bbfSMichael Walsh if { $timeout == 1 } { 21aa245bbfSMichael Walsh set seconds "second" 22aa245bbfSMichael Walsh } else { 23aa245bbfSMichael Walsh set seconds "seconds" 24aa245bbfSMichael Walsh } 25a0a42403SMichael Walsh 26aa245bbfSMichael Walsh puts stderr "" 27aa245bbfSMichael Walsh print_error "Did not get ${description} after $timeout ${seconds}.\n" 28a0a42403SMichael Walsh # Using uplevel to be able to access expect_out. 29a0a42403SMichael Walsh if { [ catch {uplevel { puts stderr [sprint_var expect_out]}} result ] } { 30a0a42403SMichael Walsh puts stderr [sprint_varx expect_out "<not set>"] 31a0a42403SMichael Walsh } 32aa245bbfSMichael Walsh # If caller has exit_proc defined, call it. Otherwise, just call exit. 33aa245bbfSMichael Walsh if { [info procs "exit_proc"] != "" } { 34aa245bbfSMichael Walsh exit_proc 1 35aa245bbfSMichael Walsh } 36aa245bbfSMichael Walsh exit 1 37aa245bbfSMichael Walsh 38aa245bbfSMichael Walsh} 39aa245bbfSMichael Walsh 40aa245bbfSMichael Walsh 41aa245bbfSMichael Walshproc handle_eof { description } { 42aa245bbfSMichael Walsh 43aa245bbfSMichael Walsh # Print end-of-file error message to stderr and exit 1. 44aa245bbfSMichael Walsh 45aa245bbfSMichael Walsh # Description of argument(s): 46*410b1787SMichael Walsh # description A description of what was being expected (e.g. "an SOL login prompt"). 47aa245bbfSMichael Walsh 48a0a42403SMichael Walsh global spawn_id 49a0a42403SMichael Walsh 50aa245bbfSMichael Walsh puts stderr "" 51aa245bbfSMichael Walsh print_error "Reached end of file before getting $description.\n" 52a0a42403SMichael Walsh # Using uplevel to be able to access expect_out. 53a0a42403SMichael Walsh if { [ catch {uplevel { puts stderr [sprint_var expect_out]}} result ] } { 54a0a42403SMichael Walsh puts stderr [sprint_varx expect_out "<not set>"] 55a0a42403SMichael Walsh } 56aa245bbfSMichael Walsh # If caller has exit_proc defined, call it. Otherwise, just call exit. 57aa245bbfSMichael Walsh if { [info procs "exit_proc"] != "" } { 58aa245bbfSMichael Walsh exit_proc 1 59aa245bbfSMichael Walsh } 60aa245bbfSMichael Walsh exit 1 61aa245bbfSMichael Walsh 62aa245bbfSMichael Walsh} 63a0a42403SMichael Walsh 64a0a42403SMichael Walsh 6519d33f5dSJoy Onyerikwuproc expect_wrap {pattern_list message {timeout 15} {fail_on_timeout 1}} { 66a0a42403SMichael Walsh 67*410b1787SMichael Walsh # Run the expect command for the caller and return the list index of the matching pattern. 68a0a42403SMichael Walsh 69*410b1787SMichael Walsh # This function offers the following benefits over calling the expect command directly: 70*410b1787SMichael Walsh # - It makes program debug easier. When the program is run with --debug=1, this function prints useful 71*410b1787SMichael Walsh # debug output. 72a0a42403SMichael Walsh # - It will do standardized timeout and eof handling. 73a0a42403SMichael Walsh 74a0a42403SMichael Walsh # Description of argument(s): 75*410b1787SMichael Walsh # pattern_list A list of patterns to be matched. If one of the patterns matches, the 76*410b1787SMichael Walsh # list index of the matching item will be returned. By default, each 77*410b1787SMichael Walsh # pattern is presumed to be a regex. If the caller wishes to, they may 78*410b1787SMichael Walsh # precede each pattern with either of the following: "-re ", "-gl " or "-ex 79*410b1787SMichael Walsh # " in order to explicitly choose the kind of match to be done.. 80*410b1787SMichael Walsh # message A message explaining what is being expected (e.g. "an SOL login prompt"). 81a0a42403SMichael Walsh # This will be included in output messages. 82a0a42403SMichael Walsh # timeout The expect timeout value. 83*410b1787SMichael Walsh # fail_on_timeout A flag governing the behavior when the expect command results in a 84*410b1787SMichael Walsh # timeout. If set to 1, this procedure will print an error message to 85*410b1787SMichael Walsh # standard error and exit the program with a non-zero return code. If set 86*410b1787SMichael Walsh # to 0, it will return [expect_wrap_timeout]. 87a0a42403SMichael Walsh 88a0a42403SMichael Walsh # Example usage: 89a0a42403SMichael Walsh # set result [expect_wrap\ 90a0a42403SMichael Walsh # [list $bad_user_pw_regex "sh: xauth: command not found"]\ 91a0a42403SMichael Walsh # "an SOL prompt" 10] 92a0a42403SMichael Walsh # 93a0a42403SMichael Walsh # switch $result { 94a0a42403SMichael Walsh # 0 { 95a0a42403SMichael Walsh # puts stderr "" ; print_error "Invalid username or password.\n" 96a0a42403SMichael Walsh # exit_proc 1 97a0a42403SMichael Walsh # } 98a0a42403SMichael Walsh # 1 { 99a0a42403SMichael Walsh # dict set state ssh_logged_in 1 100a0a42403SMichael Walsh # } 101a0a42403SMichael Walsh # } 102a0a42403SMichael Walsh 103a0a42403SMichael Walsh global spawn_id 104a0a42403SMichael Walsh global expect_out 105a0a42403SMichael Walsh 106a0a42403SMichael Walsh # Recognized flags. 107a0a42403SMichael Walsh set flags [list "-re" "-ex" "-gl"] 108a0a42403SMichael Walsh 109a0a42403SMichael Walsh # This helps debug efforts by removing leftover, stale entries. 110a0a42403SMichael Walsh array unset expect_out \[1-9\],string 111a0a42403SMichael Walsh 112a0a42403SMichael Walsh # Prepare the expect statement. 113a0a42403SMichael Walsh append cmd_buf "global spawn_id\n" 114a0a42403SMichael Walsh append cmd_buf "global expect_out\n" 115a0a42403SMichael Walsh append cmd_buf "expect {\n" 116a0a42403SMichael Walsh set ix 0 117a0a42403SMichael Walsh foreach pattern $pattern_list { 118*410b1787SMichael Walsh # Check to see whether the caller has specified a flag (e.g. "-re", "-ex", etc.) at the beginning of the 119*410b1787SMichael Walsh # pattern. 120a0a42403SMichael Walsh set tokens [split $pattern " "] 121a0a42403SMichael Walsh if { [lsearch $flags [lindex $tokens 0]] != -1 } { 122a0a42403SMichael Walsh # Caller specified a flag. 123a0a42403SMichael Walsh set flag [lindex $tokens 0] 124a0a42403SMichael Walsh # Strip the flag from the pattern. 125a0a42403SMichael Walsh set pattern [string range $pattern 4 end] 126a0a42403SMichael Walsh } else { 127a0a42403SMichael Walsh set flag "-re" 128a0a42403SMichael Walsh } 129a0a42403SMichael Walsh append cmd_buf " ${flag} {$pattern} {set expect_result $ix}\n" 130a0a42403SMichael Walsh incr ix 131a0a42403SMichael Walsh } 13219d33f5dSJoy Onyerikwu if { $fail_on_timeout } { 133a0a42403SMichael Walsh append cmd_buf " timeout {handle_timeout \$message}\n" 13419d33f5dSJoy Onyerikwu } else { 13519d33f5dSJoy Onyerikwu append cmd_buf " timeout {set expect_result \[expect_wrap_timeout\]}\n" 13619d33f5dSJoy Onyerikwu } 137a0a42403SMichael Walsh append cmd_buf " eof {handle_eof \$message}\n" 138a0a42403SMichael Walsh append cmd_buf "}\n" 139a0a42403SMichael Walsh 140a0a42403SMichael Walsh dprint_timen "Expecting $message." 141a0a42403SMichael Walsh dprint_issuing "\n${cmd_buf}" 142a0a42403SMichael Walsh eval ${cmd_buf} 143a0a42403SMichael Walsh 144a0a42403SMichael Walsh dprintn ; dprint_vars expect_out expect_result 145a0a42403SMichael Walsh 146a0a42403SMichael Walsh return $expect_result 147a0a42403SMichael Walsh 148a0a42403SMichael Walsh} 149a0a42403SMichael Walsh 150e8899320SMichael Walsh 15119d33f5dSJoy Onyerikwuproc expect_wrap_timeout {} { 15219d33f5dSJoy Onyerikwu 15319d33f5dSJoy Onyerikwu # Return constant value of 1000. 15419d33f5dSJoy Onyerikwu 15519d33f5dSJoy Onyerikwu return 1000 15619d33f5dSJoy Onyerikwu 15719d33f5dSJoy Onyerikwu} 15819d33f5dSJoy Onyerikwu 159a0a42403SMichael Walsh 160a0a42403SMichael Walshproc send_wrap {buffer {add_lf 1}} { 161a0a42403SMichael Walsh 162a0a42403SMichael Walsh # Send the buffer to the spawned process. 163a0a42403SMichael Walsh 164*410b1787SMichael Walsh # This function offers the following benefits over calling the send command directly: 165*410b1787SMichael Walsh # - It makes program debug easier. When the program is run with --debug=1, this function prints useful 166*410b1787SMichael Walsh # debug output. 167a0a42403SMichael Walsh 168a0a42403SMichael Walsh # Description of argument(s): 169*410b1787SMichael Walsh # buffer The string to be sent to the spawned process. 170a0a42403SMichael Walsh # add_lf Send a line feed after sending the buffer. 171a0a42403SMichael Walsh 172a0a42403SMichael Walsh # Example usage. 173a0a42403SMichael Walsh # Close the ssh session. 174a0a42403SMichael Walsh # send_wrap "~." 175a0a42403SMichael Walsh # 176a0a42403SMichael Walsh # set expect_result [expect_wrap\ 177a0a42403SMichael Walsh # [list "Connection to $host closed"]\ 178a0a42403SMichael Walsh # "a connection closed message" 5] 179a0a42403SMichael Walsh 180a0a42403SMichael Walsh global spawn_id 181a0a42403SMichael Walsh global expect_out 182a0a42403SMichael Walsh 183a0a42403SMichael Walsh set cmd_buf "send -- {${buffer}}" 184a0a42403SMichael Walsh dprint_issuing 185a0a42403SMichael Walsh eval ${cmd_buf} 186a0a42403SMichael Walsh 187a0a42403SMichael Walsh if { $add_lf } { 188a0a42403SMichael Walsh send -- "\n" 189a0a42403SMichael Walsh set cmd_buf "send -- \"\\n\"" 190a0a42403SMichael Walsh dprint_issuing 191a0a42403SMichael Walsh eval ${cmd_buf} 192a0a42403SMichael Walsh } 193a0a42403SMichael Walsh 194a0a42403SMichael Walsh} 19556fd36a4SJoy Onyerikwu 19656fd36a4SJoy Onyerikwu 19756fd36a4SJoy Onyerikwuproc shell_command {command_string {prompt_regex} { quiet {} } \ 19856fd36a4SJoy Onyerikwu { test_mode {} } { show_err {} } { ignore_err {} } {trim_cr_lf 1}} { 19956fd36a4SJoy Onyerikwu 200*410b1787SMichael Walsh # Execute the command_string on the shell command line and return a list consisting of 1) the return code 201*410b1787SMichael Walsh # of the command 2) the stdout/stderr. 20256fd36a4SJoy Onyerikwu 203*410b1787SMichael Walsh # It is the caller's responsibility to spawn the appropriate process (ssh,telnet) and to get the process 204*410b1787SMichael Walsh # to a shell command line (by logging in, etc.). 20556fd36a4SJoy Onyerikwu 20656fd36a4SJoy Onyerikwu # Description of argument(s): 207*410b1787SMichael Walsh # command_string The command string which is to be run on the shell (e.g. "hostname" or 208*410b1787SMichael Walsh # "grep this that"). 209*410b1787SMichael Walsh # prompt_regex A regular expression to match the prompt for current shell to run on (e.g 210*410b1787SMichael Walsh # "/ #"). 211*410b1787SMichael Walsh # quiet Indicates whether this procedure should run the print_issuing() procedure 212*410b1787SMichael Walsh # which prints "Issuing: <cmd string>" to stdout. The default value is 0. 213*410b1787SMichael Walsh # test_mode If test_mode is set, this procedure will not actually run the command. 214*410b1787SMichael Walsh # If print_output is set, it will print "(test_mode) Issuing: <cmd string>" 215*410b1787SMichael Walsh # to stdout. The default value is 0. 216*410b1787SMichael Walsh # show_err If show_err is set, this procedure will print a standardized error report 217*410b1787SMichael Walsh # if the shell command returns non-zero. The default value is 1. 218*410b1787SMichael Walsh # ignore_err If ignore_err is set, this procedure will not fail if the shell command 219*410b1787SMichael Walsh # fails. However, if ignore_err is not set, this procedure will exit 1 if 220*410b1787SMichael Walsh # the shell command fails. The default value is 1. 221*410b1787SMichael Walsh # trim_cr_lf Trim any trailing carriage return or line feed from the result. 22256fd36a4SJoy Onyerikwu 223*410b1787SMichael Walsh # Set defaults (this section allows users to pass blank values for certain args). 22456fd36a4SJoy Onyerikwu set_var_default quiet [get_stack_var quiet 0 2] 22556fd36a4SJoy Onyerikwu set_var_default test_mode 0 22656fd36a4SJoy Onyerikwu set_var_default show_err 1 22756fd36a4SJoy Onyerikwu set_var_default ignore_err 0 22856fd36a4SJoy Onyerikwu set_var_default acceptable_shell_rcs 0 22956fd36a4SJoy Onyerikwu 23056fd36a4SJoy Onyerikwu global spawn_id 23156fd36a4SJoy Onyerikwu global expect_out 23256fd36a4SJoy Onyerikwu 23356fd36a4SJoy Onyerikwu qprintn ; qprint_issuing ${command_string} ${test_mode} 23456fd36a4SJoy Onyerikwu 23556fd36a4SJoy Onyerikwu if { $test_mode } { 23656fd36a4SJoy Onyerikwu return [list 0 ""] 23756fd36a4SJoy Onyerikwu } 23856fd36a4SJoy Onyerikwu 23956fd36a4SJoy Onyerikwu send_wrap "${command_string}" 24056fd36a4SJoy Onyerikwu 24156fd36a4SJoy Onyerikwu set expect_result [expect_wrap\ 24256fd36a4SJoy Onyerikwu [list "-ex $command_string"]\ 24356fd36a4SJoy Onyerikwu "the echoed command" 5] 24456fd36a4SJoy Onyerikwu set expect_result [expect_wrap\ 24556fd36a4SJoy Onyerikwu [list {[\n\r]{1,2}}]\ 24656fd36a4SJoy Onyerikwu "one or two line feeds" 5] 24756fd36a4SJoy Onyerikwu # Note the non-greedy specification in the regex below (the "?"). 24856fd36a4SJoy Onyerikwu set expect_result [expect_wrap\ 24956fd36a4SJoy Onyerikwu [list "(.*?)$prompt_regex"]\ 25056fd36a4SJoy Onyerikwu "command output plus prompt" -1] 25156fd36a4SJoy Onyerikwu # The command's stdout/stderr should be captured as match #1. 25256fd36a4SJoy Onyerikwu set out_buf $expect_out(1,string) 25356fd36a4SJoy Onyerikwu 25456fd36a4SJoy Onyerikwu if { $trim_cr_lf } { 25556fd36a4SJoy Onyerikwu set out_buf [ string trimright $out_buf "\r\n" ] 25656fd36a4SJoy Onyerikwu } 25756fd36a4SJoy Onyerikwu 25856fd36a4SJoy Onyerikwu # Get rc via recursive call to this function. 25956fd36a4SJoy Onyerikwu set rc 0 26056fd36a4SJoy Onyerikwu set proc_name [get_stack_proc_name] 26156fd36a4SJoy Onyerikwu set calling_proc_name [get_stack_proc_name -2] 26256fd36a4SJoy Onyerikwu if { $calling_proc_name != $proc_name } { 26356fd36a4SJoy Onyerikwu set sub_result [shell_command {echo ${?}} $prompt_regex 1] 26456fd36a4SJoy Onyerikwu dprintn ; dprint_list sub_result 26556fd36a4SJoy Onyerikwu set rc [lindex $sub_result 1] 26656fd36a4SJoy Onyerikwu } 26756fd36a4SJoy Onyerikwu 26856fd36a4SJoy Onyerikwu if { $rc != 0 } { 26956fd36a4SJoy Onyerikwu if { $show_err } { 27056fd36a4SJoy Onyerikwu puts stderr "" ; print_error_report "The prior shell command failed.\n" 27156fd36a4SJoy Onyerikwu } 27256fd36a4SJoy Onyerikwu if { ! $ignore_err } { 27356fd36a4SJoy Onyerikwu if { [info procs "exit_proc"] != "" } { 27456fd36a4SJoy Onyerikwu exit_proc 1 27556fd36a4SJoy Onyerikwu } 27656fd36a4SJoy Onyerikwu } 27756fd36a4SJoy Onyerikwu } 27856fd36a4SJoy Onyerikwu 27956fd36a4SJoy Onyerikwu return [list $rc $out_buf] 28056fd36a4SJoy Onyerikwu 28156fd36a4SJoy Onyerikwu} 282