What does the number after `exit` command in expect scripts mean - linux

I am looking at an expect script at it has the following lines:
#some heading
send -- "some command\n"
expect {
-re $more {
send -- " "
exp_continue
}
">" { }
default { exit 230 }
}
# some heading
send -- "some command\n"
expect {
-re $more {
send -- " "
exp_continue
}
">" { }
default { exit 211 }
}
So what do the numbers "230" and "211" mean after the exit command.

The numbers are exit codes. They range from 0-255 and are used to communicate program success or errors to other applications that might invoke that program (e.g. your shell).
In bash and many other shells, you can check the exit status of the last program using $?. An exit status of 0 indicates success, any non-0 status means failure. You should refer to the program's documentation to see what the different exit codes could mean.
See also the Wikipedia entry on exit status.

Related

expect error handling - spawn id not open

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.

Zombie telnet process pile up while using spawn with Expect

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?

Node.js command get error while using pgrep

In my code, I need to check if a program is started. To do so, I have a function 'running':
function running(app, callback) {
var arg = 'pgrep --count ' + app;
exec( arg, function(err, stdout, stderr) {
if (err) {
console.log('Error:' + inspect(err) + ' ' + stderr);
callback('0');
} else {
var data = '' + stdout;
callback(data.charAt(0)); //Will be 0 only if no app is started
}
});
}
It worked well for some times, but now I get:
Error: { [Error: Command failed: ]
[stack]: [Getter/Setter],
[arguments]:undefined,
[type]: undefined,
[message]: 'Command failed: ',
killed: false,
code: 1,
signal: null }
(stderr is empty)
I don t understand why and so can t think of any solution.
Does anyone could tell me why do I get this error?
pgrep will return a non-zero status if there are no processes matching your request. Node will interpret this non-zero status as meaning that pgrep failed. This can easily be checked at the shell, by using echo $? which shows you the exit status of the previous command. Assuming you have some bash instances running
$ pgrep --count bash; echo $?
You'll see on the console the number of bash instance running and the exit code which will be 0. Now, if you try with something that does not exist:
$ pgrep --count nonexistent; echo $?
You'll see a count of 0 and an exit status of 1.
Here is what the man page for pgrep says about the exit status:
EXIT STATUS
0 One or more processes matched the criteria.
1 No processes matched.
2 Syntax error in the command line.
3 Fatal error: out of memory etc.
So you could check the result with something like this:
var count;
if (err) {
if (err.code === 1)
count = 0; // Status 1 means no match, so we don't have to parse anything.
else {
// Real error, fail hard...
}
}
else {
count = ... ; // parse count from stdout
}
callback(count);
var arg = 'pgrep --count ' + app,
There are two issues here:
on Linux, it's not --count it's -c
that line should end with a ;, not a comma.

How to use expect with optional prompts?

Let's say I am trying to write an expect script for a test.sh that has three prompts: prompt1, prompt2, prompt3.
My code is like this:
spawn test.sh
expect "prompt1"
send "pass1"
expect "prompt2"
send "pass2"
expect "prompt3"
send "pass3"
However, prompt2 only occurs half the time. If prompt2 doesn't show up, the expect script breaks. How would I write expect code that skips over prompt2 if it doesn't show up?
EDIT:
Fixed my code:
/usr/bin/expect -c '
spawn ./test.sh
expect {
"prompt1" {
send "pass1\r"
exp_continue
}
"prompt2" {
send "pass2\r"
exp_continue
}
"prompt3" {
send "pass3\r"
exp_continue
}
}
interact return
'
This way, the rest of the script executes and provides output.
As long as you have a case that will be always be expected to hit and don't include an exp_continue in that case, you can can remove duplication and handle optional prompts easily:
expect "prompt1"
send "pass1"
expect {
"prompt2" {
send "pass2"
exp_continue
}
"prompt3" {
send "pass3"
}
}
You can expect multiple things:
expect {
"prompt2" {
send "pass2"
expect "prompt3"
send "pass3"
}
"prompt3" {
send "pass3"
}
}

How to return spawned process exit code in Expect script?

I use expect for running test scripts.
Tests return success/failure through exit code. But expect return equivalent exit code.
How to make expect return proper exit status?
My tests are sql scripts run with psql (postgresql command processor).
Since psql doesn't allow to specify database password as a command line parameter, expect scripts do that.
So, my expect script looks like:
spawn $SPAWN_CMD
expect {
-re "Enter password for new role:" {
send "$PWPROMPT\n"
exp_continue
} -re "Enter it again:" {
send "$PWPROMPT\n"
exp_continue
} -re "Password(.*)" {
send "$PASSWORD\n"
exp_continue
} -re "Password(.*):" {
send "$PASSWORD\n"
exp_continue
} eof
}
You're already waiting for the eof at the end of your loop, you just need to use wait and catch the result:
spawn true
expect eof
catch wait result
exit [lindex $result 3]
Exits with 0.
spawn false
expect eof
catch wait result
exit [lindex $result 3]
Exits with 1.

Resources