I'm looking for a way in tcl expect to simulate pressing "enter" the script (for example after some outputs the script stops, and only after I manually press "enter" it goes further) It wait's for a "enter" key to be pressed from the user before continuing outputting the remaining script.
Here is my code where I have this problem:
set timeout 20
set f [open "password.txt" r]
set password [read $f]
close $f
foreach i $password {
puts "trying this as a pass : $i"
spawn ssh user#exemple.net -p 724
expect "user#exemple.net's password:"
send $i
interact
}
This code takes from the password.txt all the words it contains and trye's them as the password for the user#exemple.net;
The code works but after this line from the code above expect "user#example.net's password:" I need to press manually "enter" button and then the script goes with the next try.
How can I simulate this enter. Is there any command that simulates it?
I am new to tcl expect. Thank you for your time.
Try to change
send $i
to
send "$i\n"
Use this code for example:
#!/usr/bin/expect
# Set the time allowed to wait for a given response. SEt to 30 seconds because it can take a while
# for machines to respond to an ssh request.
set timeout 30000
proc connectToServer { user host password port } {
set address $user
append address "#"
append address $host
puts "Connecting to server: $host"
spawn ssh $address -p $port
expect {
{*password: } {
send "$password\n"
interact
}
{The authenticity of host*} {
send "yes\n"
expect {
{*password: } {
send "$password\n"
interact
}
}
}
"*No route to host" { puts "No route to host" }
eof {puts "Woops there was an eof"}
timeout {puts "Timed out"}
}
}
if {$argc < 3} {
puts "Error - you need to provide at least 3 arguments"
puts "* user"
puts "* host"
puts "* password"
puts "* port - optional"
} else {
set user [lindex $argv 0];
set host [lindex $argv 1];
set password [lindex $argv 2];
# setting port argument is optional
if {$argc > 3} {
set port [lindex $argv 3];
} else {
set port 22
}
connectToServer $user $host $password $port
}
Related
I'm using this expect file (keygen.exp) to generate SSH key, I'm using ssh-keygen just as example to test 'expect':
#!/usr/bin/expect
spawn ssh-keygen
expect "Enter file"
send "./id_rsa\r"
expect "Enter passphrase"
send "\r"
expect "Enter same passphrase"
send "\r"
interact
It works perfectly, however, what if the order of the prompts for inputs is not the same every run, and there might be different questions on different runs?
I want something like this:
#!/usr/bin/expect
spawn ssh-keygen
expect if-is "Enter file"
send "./id_rsa\r"
expect if-is "Enter passphrase"
send "\r"
expect if-is "Enter same passphrase"
send "\r"
interact
Is it possible?
You can have single expect statement which loops, processing different outputs differently, something like:
expect {
"Enter file" {
send "./id_rsa\r"
exp_continue
}
"Enter passphrase" {
send "\r"
exp_continue
}
"Enter same passphrase" {
send "\r"
exp_continue
}
$
}
But note that you need some way to break out of the loop. The last pattern here - $ has no exp_continue (or any other action) so it will break out of the loop if the prompt you get when logged in includes $.
See the full documentation at https://www.tcl.tk/man/expect5.31/expect.1.html
Expect in any order, with infinite timeout:
#!/usr/bin/expect
spawn /path/to/some-programme
expect {
"some string" {
set timeout -1
send "some input\r"
exp_continue
}
"some other string" {
set timeout -1
send "some other input\r"
exp_continue
}
...
}
#no 'interact'
#end of file
I'm writing an expect script which can log out in hundreds of routers and change their config.
My problem is, there is a bug on the routers firmware which causes them to close the connection after the password is send.
If I log in again, it works perfectly (so only the first log in after reboot causes the exception).
When the connection is closed the expect script is terminated.
I would like if i could gracefully catch the exception, and try again.
The code which fails is this part:
# go through each IP
for {set i $start} {$i <= $end} {incr i} {
set ip "10.$octet2.$i.x"
puts "\n\n\n#### doing $ip...\n" ; flush stdout
# log in to the IP
spawn ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -l $user $ip
expect {
"continue connecting (yes/no)?" { send "yes\r" ; exp_continue }
"login as: " { send "$user\r" ; exp_continue }
"Password: " { send "$pwd\r" }
"No route to host" { continue }
timeout { continue }
}
# execute commands from file
foreach c "$commands" { eval $c }
}
The error I get looks like this:
Password:
Connection to 10.x.x.x closed by remote host.
Connection to 10.x.x.x closed.
send: spawn id exp11 not open
while executing
"send "exit\r""
("eval" body line 1)
invoked from within
"eval $c "
("foreach" body line 1)
invoked from within
"foreach c "$commands" { eval $c }"
("for" body line 18)
invoked from within
"for {set i $start} {$i <= $end} {incr i} {
set ip "10.$octet2.$i.x"
puts "\n\n\n#### doing $ip...\n" ; flush stdout
# log in to the IP
spa..."
(file "./multido.exp" line 39)
Any help is really appreciated!
You can catch the exception using the tcl command catch to surround the command that might error. You would extend your code's inner loop to resemble this:
set tryrun 1
while {$tryrun} {
spawn ssh ...
expect ...
set tryrun 0
foreach c "$commands" {
if {[catch {eval $c} result]} {
puts "failed: $result"
set tryrun 1
}
}
}
Perhaps a simpler solution would be to look for the pattern "closed by remote host" in your expect, and using this to repeat a similar loop.
I intend to make a telnet connection with a network device using Expect, and it involves sending commands to the device multiple times and also rebooting the device as well. I thereby need to make the telnet connection again and again.
proc dputs {msg} {
if {[info exists ::debug] && $::debug} {
puts $msg
}
}
proc open_telnet_session {} {
set sResult FAIL
set prompt "(\r|\n|\r\n).*?(#|%|>|\\\$) $"
#set prompt "#|%|>|\\\$ $"
set timeout 60
if {$::tcl_platform(platform) eq "windows"} {
spawn {c:\Dinesh\telnet_32bit.exe} $::device_ip
} else {
spawn telnet $::device_ip
}
set ::device_ip $spawn_id
expect {
timeout {puts "Timeout happened while spawning telnet session";return $sResult}
eof {puts "EOF happened while spawning telnet session";return $sResult}
"login: $" {send "$::device_uname\r";exp_continue}
"password: $" {send "$::device_pwd\r";exp_continue}
-re $prompt
}
set sResult PASS
return $sResult
}
proc send_cmd_to_device {cmd} {
set timeout 180
dputs "cmd : $cmd"
set sResult FAIL
set prompt "(\r|\n|\r\n).*?(#|%|>|\\\$) $"
set ::spawn_id $::device_ip
if {[catch {send "$cmd\r"} errorMsg]} {
puts "Failed to send the commands..."
puts "Reason : $errorMsg"
return $sResult
}
expect {
timeout {puts "Timeout happened while sending commands to telnet session";return 0}
eof {puts "EOF happened while sending commands to telnet session";return 1}
"invalid token" {puts "Invalid token error from device";exp_continue}
"$cmd" { dputs "\n\n matching the cmd\n\n";set ::actual_cmd_match 1;exp_continue}
-re $prompt {
if {$::actual_cmd_match} {
dputs "\n\n final prompt match \n\n"
set ::actual_cmd_match 0
set sResult PASS
} else {
dputs "\n\n still waiting for prompt match \n\n"
exp_continue
}
}
}
return $sResult
}
proc close_telnet_session {} {
set sResult FAIL
set ::spawn_id $::device_ip
#This will send 'Ctrl+]' to close the telnet connection gracefully
if {[catch {send "\x1d"} errorMsg]} {
puts "Failed to send the commands..."
puts "Reason : $errorMsg"
return $sResult
}
expect {
timeout {return $sResult}
eof {return $sResult}
-nocase "telnet>"
}
if {[catch {send "quit\r"}]} {
puts "Failed to send the commands..."
puts "Reason : $errorMsg"
return $sResult
}
expect {
timeout {return $sResult}
eof {set sResult PASS}
}
return $sResult
}
Even though I am closing the connection gracefully, I can still see the process running in the task manager (in Windows 7). (Same case with Linux as well, telnet process shows up as <defunct> process).
If I run the script overnight and say I have to open the telnet connection about thousands of time (as my script involves rebooting the device multiple times and thus the management connection will be lost), it will end up reducing the performance.
This will lead to memory leak or failure in resource allocation when this happens continuously.
After searching a lot, I end up with exp_close and exp_wait.
# Killing the process in Windows...
exec taskkill /pid $telnet_process_id
exp_close -i $::device_id
exp_wait -i $::device_id; # This becomes a blocking call..
With the above code, exp_wait is keep on waiting and it is getting blocked in there.
To avoid the same, I have used -nowait flag as well, but still no use. It is returning immediately and the process still stays in the process chart.
What should be the optimal way to handle this issue?
In my experience, spawned process connections are usually terminated with a call to close. Is expect on windows different than expect on *nix in that regard?
I need to gather some statistics from a network switch and would like to use an expect script to output the data to a file. I would like to run this script as a cron job and have the data appended to the file when it is run. Following is the working code I have so far, I just do not know how to get the output to a file. Any help is greatly appreciated.
#!/bin/bash
#get mac-address count
/usr/bin/expect -f -<<EOD
spawn ssh user#192.168.1.100
sleep 2
#Catch the password prompt and send supplied password
expect {
"*word:" {send "password\r"}
}
sleep 1
#Get into enabled mode
expect {
"*>" {send "system-view\r"}
}
sleep 1
expect {
"*]" {send "display mac-address count\r"}
}
sleep 1
expect {
"*]" {send "quit\r"}
}
sleep 1
expect {
"*>" {send "quit\r"}
}
sleep 1
expect eof
EOD
I have this code that connects my network to an external vpn, but sometimes this connection is lost. I need my code to detect the error and try to connect again.
set force_conservative 0
if {$force_conservative} {
set send_slow {1 .1}
proc send {ignore arg} {
sleep .1
exp_send -s -- $arg
}
}
set timeout -1
spawn $env(SHELL)
match_max 100000
proc tryconnection {} {
send -- "./forticlientsslvpn_cli --server SERVER:PORT --vpnuser USER"
expect -exact "./forticlientsslvpn_cli --server SERVER:PORT --vpnuser USER"
send -- "\r"
expect -exact "\r\nPassword for VPN:"
send -- "PASSWORD\r"
expect -exact "\r\nSTATUS::Setting up the tunnel\r\nSTATUS::Connecting...\r"
send -- "Y\r"
expect -exact "\r\nSSLVPN down unexpectedly with error:6\r" {
puts "Send Ctrl+C"
send \003
tryconnection
}
expect eof
}
tryconnection
I would remove the -exact option:
expect "*SSLVPN down unexpectedly with error:6*" { ...
Try running with expect -d to see why your pattern is not matching when you lose connection.