Expect gets stuck sometimes during login - authentication

I have the following script. Sometimes, it runs fine and others it gets stuck. What could be wrong here?
#!/usr/bin/env expect
# set Variables
set timeout 60
set ipaddr [lindex $argv 0]
# start telnet connection
spawn telnet $ipaddr
match_max 100000
# Look for user prompt
expect "username:*"
send -- "admin\r"
expect "password:?"
# Send pass
send "thisisthepass\n"
# look for WWP prompt
expect ">"
send "sendthiscommand\r"
expect ">"
send "exit\r"
interact
The script runs fine till the end, but sometimes it gets stuck during login. This behavior is present even with the same IP: for example, it may run 1 out of 5 tries for the same IP.
I have tried adding some sleep between sending of the user and password, but it's still the same. I have also tried without expect, by sending directly the password string after the user one but still the same: sometimes the script runs fine but others it asks again for the password as if it's incorrect...
username: admin
password:
username:

Things I would do:
change send "thisisthepass\n" to send "thisisthepass\r"
include exp_internal 1 somewhere at the top of your script, and see what is going on when you have a failed attempt
exp_internal 1 will enable debugging with lots of good information on what is going on with expect's pattern matching. You can share it here and I'll be glad to take a look at it.
Are you sure the password prompt has an extra character after it (your ? in expect "password:?". Is it always there? Any chance different devices have slightly different password prompts?

Related

Get value of variable passed to spawn_id in Expect

I've managed to write a simple expect script to backup multiple router configs. This works fine but takes some time slow when the list of devices grows.
I tried to find an a way to spawn SSH sessions simultaneously and came across an article that goes:
# spawn all connections
foreach conn $allconnections {
spawn telnet $conn
lappend spawn_id_list $spawn_id
}
# run expect script for all connections individually
foreach id $spawn_id_list {
# this is important - for unknown (to me) reasons
set spawn_id $id
send "your_send_message"
expect "your_expect_pattern"
}
This seems to work but I am now faced with another challenge in creating a backup file named after the actual device. When $conn is passed to spawn_id and I create the backup file based on $id_configs.txt, I only get "expN" where N is a number which increments as each line on the device list is read. I've been experimenting on how to get the actual $id value back to use as filename but have yet to find a solution.
Appreciate any input from you guys. Thanks in advance!
% set pid [spawn telnet xx.xx.xxx.xx]
spawn telnet xx.xx.xxx.xx
29353
% set pid
29353
The spawn returns process id of whatsoever program being spawned. You can save that in a variable and use it further.

Not able to establish Oracle SQL session from within a BASH script

#!/bin/bash
#Oracle DB Info for NEXT
HOST="1.2.3.4"
PORT="5678"
SERVICE="MYDB"
DB_USER=$(whoami)
DB_PASS=$(base64 -d ~/.passwd)
DB_SCHEMA="my_db"
#Section for all of our functions.
function SQLConnection(){
sqlplus "$DB_USER"/"$DB_PASS"#"$HOST":"$PORT"/"$SERVICE"
}
function Connected(){
SQLConnection <<EOF
select sys_context('USERENV','SERVER_HOST') from dual;
EOF
}
function GetJMS(){
SQLConnection <<EOF
set echo on timing on lines 200 pages 100
select pd.destination from ${DB_SCHEMA}.pd_notification pd where pd.org_id = '$ORGID';
EOF
}
TODAY=$(date +"%A %B %d, %Y")
read -r -p $'\n\nWhat is the ORG ID? ' ORGID
read -r -p $'\n\nWhat is the REMOTE QUEUE MANAGER NAME? ' RQM
read -r -p $'\n\nWhat is the IP address of the REMOTE QUEUE MANAGER? ' CONN
read -r -p $'\n\nWhat is the PORT of the REMOTE QUEUE MANAGER? ' PORT
echo -en "* $(whoami)\n* $TODAY\n* MQ Setup $ORGID\n\nDEFINE +\n\tCHANNEL('$RQM.LQML') +\n\tCHLTYPE(SDR) +\n\tCONNAME('$CONN($PORT)') +\n\tXMITQ('BUF.2.$ORGID.XMQ')\n\tCHAUTH(TLS_RSA_WITH_AES_256_CBC_SHA256)\n\nDEFINE +\n\tCHANNEL('LQML.$RQM') +\n\tCHLTYPE(RCVR) +\n\tTRPTYPE(TCP)\n\nDEFINE +\n\tQLOCAL('$RQM') +\n\tTRIGDATA('LQML.$RQM') +\n\tINITQ('SYSTEM.CHANNEL.INITQ') +\n\tTRIGGER USAGE(XMITQ)\n\n" > ~/mqsetup.mqsc
CONNECTED=$(Connected | awk 'NR==16')
echo -en "\n\nHello From: $CONNECTED\n\n"
for JMSDESTINATION in $(GetJMS | awk 'NR>=16&&NR<=24{print $1}')
do
read -r -p $'\n\nWhich REMOTE QUEUE NAME matches with this ${JMSDESTINATION}?' RNAME
QDESC=$(echo "$JMSDESTINATION" | tr '.' ' ' | tr '[[:upper:]]' '[[:lower:]]')
echo -en "\n\nDEFINE +\n\tQR($JMSDESTINATION) +\n\t\tREPLACE DESCR('$ORGID $QDESC Queue') +\n\t\tREPLACE MAXDEPTH(5000) +\n\t\tXMITQ('BUF.2.$ORGID.XMQ') +\n\t\tRNAME('$RNAME') +\n\t\tRQMNAME('$RQM')" >> ~/mqsetup.mqsc
done
Here is the script I've built, hoping to automate the setup of IBM MQ Queues and Channels. My problem is that outside this script, I can establish an SQL Session without an issue, directly from the shell, provided I input the variables seen in the script. I can call the functions and everything returns just as I'd hope it would. When I run the exact same things from within the script, I get timeout errors ... the "Hello From" is blank, which tells me there is no DB connection.
I'm totally stumped as to why it all works great from outside the script, but inside it times out.
I appreciate the eyes and the help!
You're overwritng a variable value. You have this at the top of the script:
PORT="5678"
but then later on you do:
read -r -p $'\n\nWhat is the PORT of the REMOTE QUEUE MANAGER? ' PORT
which overwrites your 5678 value with whatever is entered there. That port may not be listening on the DB server at all, or may be doing something else, or if you don't enter a value it'll default to port 1521 when you connect. But either way the connection is going to fail, either quickly or slowly depending on the port state (e.g. slower maybe if a firewall blocks it).
If you test the connection by adding a Connected call before the read calls (as I initially did) then it seems to be working fine; but the connections after the reads don't work because port value it tries to connect to is now wrong.
Use a different name for the two variables, e.g. RQ_PORT for the second one - both in its read command and the subsequent creation of the ~/mqsetup.mqsc file.
You may also find it useful to add the -l flag to your SQL*Plus call so that if the connection fails for some reason it won't re-prompt for credentials, which in some circumstances can make the script appear to hang until you hit enter a few times.
Not directly relevant to the problem, but when automating anything like this I usually also use the -s flag to suppress the banners (which can vary between environments); and if you're only interested in capturing query output I'd usually set headings and/or pagination off, and feedback off, and generally set SQL*Plus up to generate as little noise as possible - it makes parsing out the interesting bits easier.

copy 3 newest files in remote server using expect the close the session

For starters, I'm a complete novice with expect scripts. I have written a few ssh scripts but I cant seem to figure out how to get the latest 3 log files after running a set of tests for a new build. My main goal is to find the latest log files and copy them to my local machine. PLEASE DON'T tell me that it's bad practice to hard code the login and password, I'm doing so because it's temporary to make the script work. My code currently...
#!/usr/bin/expect -f
set timeout 15
set prompt {\]\$ ?#}
spawn ssh -o "StrictHostKeyChecking no" "root#remote_ip"
expect {
"RSA key fingerprint" {send "yes\r"; exp_continue}
"assword:" {send "password\r"; exp_continue}
}
sleep 15
send -- "export DISPLAY=<display_ip>\r"
sleep 5
send "cd /path/to/test/\r"
sleep 5
set timeout -1
send "bash run.sh acceptance.test\r"
#Everything above all works. The tests has finished, about to cp log files
send "cd Log\r"
sleep 5
send -- "pwd\r"
sleep 5
set newestFile [send "ls -t | head -3"]
#tried [eval exec `ls -t | head -3`]
#No matter what I try, my code always gets stuck here. Either it wont close the session
#or ls: invalid option -- '|' or just nothing and it closes the session.
#usually never makes it beyond here :(
expect $prompt
sleep 5
puts $newestFile
sleep 5
send -- "exit\r"
sleep 5
set timeout 120
spawn rsync -azP root#remote_ip:'ls -t /logs/path/ | head -3' /local/path/
expect {
"fingerprint" {send "yes\r"; exp_continue};
"assword:" {send "password\r"; exp_continue};
}
Thanks in advance
When writing an expect script, you need to follow the pattern of expecting the remote side to write some output (e.g., a prompt) and then sending something to it in reply. The overall pattern is spawn, expect, send, expect, send, …, close, wait. If you don't expect from time to time, there are some buffers that fill up, which is probably what's happening to you.
Let's fix the section with the problems (though you should be expecting the prompt before this too):
send "cd Log\r"
expect -ex $prompt
send -- "pwd\r"
expect -ex $prompt
send "ls -t | head -3\r"
# Initialise a variable to hold the list of files produced
set newestFiles {}
# SKIP OVER THE LINE "TYPED IN" JUST ABOVE
expect \n
expect {
-re {^([^\r\n]*)\r\n} {
lappend newestFiles $expect_out(1,string)
exp_continue
}
-ex $prompt
}
# Prove what we've found for demonstration purposes
send_user "Found these files: \[[join $newestFiles ,]\]\n"
I've also made a few other corrections. In particular, send has no useful result itself, so we need an expect with a regular expression (use the -re flag) to pick out the filenames. I like to use the other form of the expect command for this, as that lets me match against several things at once. (I'm using the -ex option for exact matching with the prompts because that works better in my testing; you might need it, or might not.)
Also, make sure you use \r at the end of a line sent with send or the other side will be still be waiting “for you to press Return” which is what the \r simulates. And don't forget to use:
exp_internal 1
when debugging your code, as that tells you exactly what expect is up to.

Expect 'send' not working as expected

I am a beginner in expect...I have written a small script which has to login to a router and execute few commands..
But somehow i am finding that even though when i have used send "admin show platform" THRICE, it is only working twice for me.. I only get the output of admin show platform twice.
Can anyone check the code and point me where actually i am screwing up the code..
Gsaxena#
Gsaxena#
Gsaxena# ./testTool
spawn /usr/bin/ksh
telnet 5.28.7.103
$ telnet 5.28.7.103
Trying 5.28.7.103...
Connected to 5.28.7.103.
Escape character is '^]'.
User Access Verification
Username:
Username: lab
Password:
RP/0/RP0/CPU0:Billorani#debug ospf ospf1 adj
Mon Oct 14 17:16:06.144 UTC
**RP/0/RP0/CPU0:Billorani#show platform**
Mon Oct 14 17:16:06.416 UTC
Node Type PLIM State Config State
------------- ----------------- ------------------ --------------- ---------------
x/x/x0 xxxxG N/A IN-RESET PWR,NSHUT,MON
**RP/0/RP0/CPU0:Billorani#show platform**
Mon Oct 14 17:16:06.416 UTC
Node Type PLIM State Config State
------------- ----------------- ------------------ --------------- ---------------
x/x/xxx0 xxxxG N/A IN-RESET PWR,NSHUT,MON
RP/0/RP0/CPU0:Billorani#
Gsaxena#
Gsaxena#
Gsaxena#
Gsaxena#
Gsaxena#
#!/usr/bin/expect
set timeout 30
set hostcut "Bil"
sleep 5
set timeout 5
spawn /usr/bin/ksh
send "telnet 5.8.7.103\r"
expect ".*\'\^\]\'\. *"
send "\r"
expect "Username\:"
send "lab\n"
expect "Password\: "
send "lab\n"
sleep 10
expect -re "RP\/.\/.*\/CPU.:$hostcut.*#"
send "debug ospf ospf1 adj\n"
expect -re "RP\/.\/.*\/CPU.:$hostcut.*#"
send "admin show platform\n"
expect -re "RP\/.\/.*\/CPU.:$hostcut.*#"
send "admin show platform\n"
expect -re "RP\/.\/.*\/CPU.:$hostcut.*#"
send "admin show platform\n"
I should really be placing this not in an actual answer but in a comment, since I do not have a final answer for you, but it seems comments can only be left by folks who have been around for some time (there's a minimum reputation before you can leave them).
Anyways, what I wanted to suggest was that you add exp_internal 1 somewhere near the start of your script. This will provide a ton of useful debugging information, and will most likely point at what is going on. Feel free to post it here if you need help with it.
I can't tell what is wrong from the information you posted... nothing seems obviously at fault. One thing I would do differently is instead of spawning a Korn shell process and then send a telnet commando to it, I would just spawn the telnet command directly (less code, less resources). But that is not what is bothering you, so never mind that.
I'm not familiar with the OS you're logging in to... is that Cisco IOS XR? I'm baffled by the fact that you issue an admin show platform command, yet only show platform shows in your stdout? Also, what's the deal with the dual asterisks (**) some prompts show, while others don't?
One last thing, which may seem dumb, but... can you manually access the device and issue those 4 exact commands, in that exact order?
Regards,
James
In your code
send "admin show platform\n"
Use "\r" instead of "\n"

proc_open interaction

Here's what I'm trying to achieve: open a shell (korn or bash, doesn't matter), from that shell, I want to open a ssh connection (ssh user#host). At some point it is likely to happen I will be prompted for either a password or I might be asked whether or not I'm sure I want to connect (offending keys).
Before anyone asks: yes, I am aware there is a plugin for ssh2 exec calls, but the servers I'm working on don't support it, and are unlikely to do so.
Here's what I've tried so far:
$desc = array(array('pipe','r'),array('pipe','w'));//used in all example code
$p = proc_open('ssh user#host',$desc,$pipes);
if(!is_resource($p)){ die('#!#$%');}//will omit this line from now on
sleep(1);//omitting this,too but it's there every time I need it
Then I tried to read console output (stream_get_contents($pipes[1])) to see what I have to pass next (either password, yes or return 'connection failed: '.stream_get_contents($pipes[1]) and proc_close $p.
This gave me the following error:
Pseudo-terminal will not be allocated because stdin is not a terminal.
So, I though ssh was called in the php:// io-stream context, seems a plausible explanation of the above error.
Next: I though about my first SO question and decided it might be a good idea to open a bash/ksh shell first:
$p = proc_open('bash',$desc,$pipes);
And take it from there, but I got the exact same error message, only this time, the script stopped running but ssh did run. So I got hopeful, then felt stupid and, eventually, desperate:
$p=proc_open('bash && ssh user#host',$desc,$pipes);
After a few seconds wait, I got the following error:
PHP Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 133693440 bytes)
The Call Stack keeps bringing up the stream_get_contents line, even in my last desperate attempt:
#!/path/to/bin/php -n
<?php
$p = proc_open('bash && ssh user#host',array(array('pipe','r'),array('pipe','w')),$ps);
if (!is_resource($p))
{
die('FFS');
}
usleep(10);
fwrite($ps[0],'yes'."\n");
fflush($ps[0]);
usleep(20);
fwrite($ps[0],'password'."\n");
fflush($ps[0]);
usleep(20);
fwrite($ps[0],'whoami'."\n");
fflush($ps[0]);
usleep(2);
$msg = stream_get_contents($ps[1]);
fwrite($ps[0],'exit'."\n");
fclose($ps[0]);
fclose($ps[1]);
proc_close($p);
?>
I know, its a mess, a lot of fflush and redundancy, but the point is: I know this connection will first prompt me for offending keys, and then ask a password. My guess is the stream in $pipes[1] holds the ssh connection, hence it's content is huge. what I need then, is a pipe inside a pipe... is this even possible? I must be missing something, what good is a pipe if this isn't possible...
My guess is the proc_open command is wrong to begin with, (error: Broken pipe). But I really can't see any other way around the first error... any thoughts? Or follow up questions if the above rant isn't at all clear (which it probably isn't).
Before anyone asks: yes, I am aware there is a plugin for ssh2 exec
calls, but the servers I'm working on don't support it, and are
unlikely to do so.
There are actually two. The PECL module, which is a PITA that most servers don't have installed anyway and phpseclib, a pure PHP SSH2 implementation. An example of its use:
<?php
include('Net/SSH2.php');
$ssh = new Net_SSH2('www.domain.tld');
if (!$ssh->login('username', 'password')) {
exit('Login Failed');
}
echo $ssh->exec('pwd');
echo $ssh->exec('ls -la');
?>