How to set the Interact timeframe for expect - expect

I am using expect via telnet to connect the box and then using expect script as follows:
expect -c 'spawn -noecho telnet IP' exp_script
On the expect script [exp_script] - Setting the user prompt and then using interact to interact the user to pass their cmd and stay on the prompt for further uses.
However I need to restrict the user to interact with box for certain time, the time frame will be dynamic and changes based on users.
So my question is how to restrict the users to interact the box via expect with certain timeframe only and then user should auto exit from box.
Highly appreciated for any help here!!

This uses the Tcl after command to schedule code to run after X milliseconds
spawn -noecho telnet IP
proc get_user_time_in_milliseconds {username} {
# checks here
set limit 5000 ;# => 5 seconds
return $limit
}
proc byebye {spawn_id} {
send_user "\n\nYour time is up. Bye!\n"
exp_close $spawn_id
exit
}
after [get_user_time_in_milliseconds $env(LOGNAME)] [list byebye $spawn_id]
# rest of expect code goes here.
interact

Related

How to spawn telnet session based on user input

I created a script that queried everything on a specified device, however too much data is printed and I'd like to be able to query specific areas of the device configuration depending what arguments are passed.
My initial script which prints everything works fine
#!/usr/bin/expect
set hostName [lindex $argv 0]
set timeout 2
spawn telnet $hostName
expect -re "login"
send "xxx\n"
expect -re "Password:"
send "<password>"
sleep 0.5
send "<query 1>"
sleep 0.5
send "<query 2>"
interact
Now I am creating a help section which involves including bash, and am having some trouble getting both expect & bash to work at the same time, along with being able to interact at the end. Does anyone have any advice?
with the below, the help function works but the send commands etc. don't work
#!/bin/bash
#!/usr/bin/expect -f
With the below I am unable to run the help menu
#!/usr/bin/expect -f
#!/bin/bash
I get this error:
invalid command name "Help()"
while executing
"Help()"
(file "./xxx" line 5)
Does anyone have any advice?

What does it mean to combine interact with return in expect?

I want to use shell and expect to log in to the server with only one command.
But when interact and return are used in one statement, I don't understand what they mean.In addition, "Password" will only appear when SSH link multiplexing is disconnected, so I hope that when "Password" does not appear, it will not send "mypassord".
So how to write scripts to deal with these two situations in a unified way?
When SSH multiplexed links were established, my script was stuck in "expect "Password"".How to deal with it?
In addition, I would like to ask what does the -o parameter mean after the interact? And what is the meaning of using interact and return together?
What's the difference between "send --" and "send "?
I have enabled SSH master connection. Before I log on to the server, I need to first enter the password, and then enter a number to represent which machine I log on to. But when SSH multiplexed links are established, the option of entering passwords becomes redundant.
#!/usr/bin/expect
# ssh command
set cmd [lindex $argv 0]
set relay_num [lindex $argv 1]
set timeout -1
# run ssh command
spawn bash -c "$cmd"
expect "Password*"
send "mypassord\r"
interact -o -nobuffer -re "Option" return
send -- "$relay_num\r"
interact
In combination with the -o switch, that line is looking at the output of the command for the regex "Option". The return will cause the interact command to end and the script continues to the send command (that is explained in the man page).
I think it would be more clear to use expect "Option" instead.

Run multiple SSH sessions in parallel

I have a script that helps me update the IOS of my Cisco devices when ever I need to. It works fine and I have no issues with the script itself other then the fact that it only does one device at a time.
Is there something that I can research to make the script run asymmetrically so it can do multiple sessions at one time?
The script consists of an expect script which is setup like so:
set timeout 6
set hostname [lindex argv $0]
set password [lindex argv $1]
spawn ssh $hostname
expect "TACACS*:"
send "$password\r"
expect "#"
send "term length 0\r"
< other similar commands >
interact
The main bash script works as follows:
IP=$(cat ./iphosts)
read -p "Please enter your TACACS Password:" password
for i in $IP
do
expect 01.exp $i $password | tee -a bulk.log
done
interact
Both the expect and .sh script have a little bit more to each but those usually post script completion tasks like reporting or additional commands.
Thank you for any information that you can provide on this!
You can use the xargs tool to start a number of processes in parallel. For example:
#!/bin/sh
read -p "Please enter your TACACS Password:" password
xargs -IADDRESS -P4 expect 01.exp ADDRESS $password < ./iphosts
This uses the -P argument to xargs to run up to 4 processes at a time. You could scale up the argument to -P to run more processes in parallel.
But there's a problem here: you're calling interact in your expect script, which suggests that the script is expecting (possibly requires) interactive input from you when it is running. If this is the case, the solution presented here won't work. You would need to rewrite your expect script so that it does not require any user interaction.
You may also want to investigate a tool like Ansible which (a) does this sort of parallel execution by default and (b) has explicit support for configuring a variety of network devices.

Continue working on SSH session spawned on a previous proc

I wrote my first Expect script without using functions, its task is to connect to a remote host via SSH, then from the remote host start a TFTP copy.
I want to offer the user the option to copy another file, then I thought I would need to change my Expect code from a single sequence of lines and split it into functions.
First function would be to spawn SSH:
proc Connect {sshOptions userName switchIP userPassword aclFile} {
spawn -noecho ssh "$sshOptions" $userName#$switchIP
log_user 0
expect {
timeout exit
-exact "Password:" { send -- "$userPassword\r" }
}
expect "*test#"
send_user "Connection established\n"
}
Second function would be to start the copy:
proc Copy_ACL {aclFile} {
send_user "Copying ACL file...\n"
send "***copy command here**"
expect "TFTP get operation was successful"
send_user "ACL File copied\n"
}
Then I call functions as follows:
Connect $ssh_Options $user_Name $switch_IP $user_Password $acl_File
Copy_ACL $acl_File
At the moment, the issue I am facing is that, it seems function "Copy_ACL" does not know anything about the SSH session spawned by the previous function Connect. The copy command is sent to stdout although "log_user" has been set to 0.
What am I missing?
Also, is there another book about Expect besides "Exploring Expect"?
I had some success modifying existing code to use the same Expect session in two procs. All that seemed to be necessary was to make expect_out and spawn_id global variables so they could be shared between the procs. Of course, this means that only one Expect session can run at once, but that isn't likely to be a problem.

Expect Script - Starting logging from a certain point, and stop at a certain point

Hope you're all well!
I've recently begun putting together a little utility for a few our of network engineers to use. It's function is to retrieve circuit information which is then parsed and outputted in a nice format. Of course, part of this involves an SSH connection to the box required and running the command to generate the desired output.
Here's the script I've currently got. It works fine, runs the desired command, sends a few blank spaces to prompt the system to display more circuits, and then finishes off by exiting. All of this is being logged into a file with a name identical to the box's hostname. Great.
However my issue is apparent when I read the file produced and see that it includes a ton of data, including the command I ran and unnecessary stats provided on connection. I'm only looking for the logging to begin when I issue the command, and for it to cut off afterwards. As I'm not familiar with expect, I'd really appreciate any guidance.
PS. Apologies if I've done anything stupid; I'm pretty new to the language itself and the support out there isn't that great.
Thanks.
set ip [lindex $argv 0]
set hostname [lindex $argv 1]
set timeout 10
set user ""
set password ""
# Spawning the ssh session
spawn ssh $ip -l $user
# Awaiting prompt for password
expect "$user#$ip's password:"
sleep 1;
# Awaiting prompt
send "$password\n"
sleep 2
log_file -noappend $hostname;
send "terminal length 0\n"
sleep 1
send "show int desc\n"
sleep 5
send "exit\n"
interact
exit
You can control the output by means of placing the log_file in your desired place.
When you want to start logging, you can add the line as (which you already have in your code)
log_file my_log_file.log
Whatever printed in console will be logged after these command execution in to the file named my_log_file.log. By default, if the file is already present in the location, it will be appended. If you add the flag -noappend, then it will overwrite the existing file.
log_file -noappend fresh.log
If you want to stop the logging, then simply give the log_file without any arguments as such
log_file
From this point, whatever output generated will not be saved.
For example, you are logging to the some switch and giving some credentials and executing some commands can be something like as follows,
spawn ssh some_ip
expect some_thing
#Login code here
log_file output.log; # Start logging
#some actions here
log_file; # stopping logging here.
# some more actions
# end
You are sending the spaces for multiple times. Instead, you can set the terminal length to 0 as follows,
send "term len 0"
expect "prompt"
This will avoid the overhead of the sending it multiple times. Also, in your case, the spaces will be sent to the switch very fast, since there is nothing to expect. If you are still interested to do it without 'term len 0' , then at least you can put the code in a loop, like as follows,
for { set i 0 } { $i < 10 } { incr i } {
send " "
expect "something"
}
But, this way of doing is not advisable, since there is a possibility of need to send more than 10 spaces, then this will fail.

Resources