Subject: | Module can hang when using an identity file & auto_yes => 1 |
Some transfers can complete before the first call to expect().
When this happens & auto_yes is true EOF is not detected, this causes a
latter call to expect() to hang indefinitely if $timeout_err has not
been specified which, by default, isn't.
From the code:
# EOF is sent here but is not detected
if($auto_yes){
while($scp->expect($timeout_auto,-re=>'[Yy]es\/[Nn]o')){
$scp->send("yes\n");
}
}
#...
#
# EOF is never detected here and we hang
unless($no_check || $verbose){
$error = ($scp->expect($timeout_err,
# other matches
['eof' => sub{ $eof = 1 } ],
))[1];
}
else{
$error = ($scp->expect($timeout_err, ['eof' => sub { $eof = 1 }]))
[1];
}
Here's a sample that includes Expect output:
[sshaw@localhost ~]$ cat scp.pl
$Expect::Exp_Internal = 1;
my $scp = Net::SCP::Expect->new(host => 'hostey',
identity_file => "$ENV{HOME}/id_rsa",
auto_yes => 1);
$scp->scp(shift);
[sshaw@localhost ~]$ perl scp.pl a.out
Spawned 'scp -q -i '/home/sshaw/id_rsa' 'a.out' 'sshaw@hostey:a.out''
spawn id(5)
Pid: 10902
Tty: /dev/pts/6
at /usr/lib/perl5/site_perl/5.8.8/Expect.pm line 181
Expect::spawn('Expect', 'scp -q -i \'/home/sshaw/id_rsa\'
\'a.out\' \'sshaw@pstor3:a...') called at /usr/lib/perl5/
site_perl/5.8.8/Net/SCP/Expect.pm line 204
Net::SCP::Expect::scp('Net::SCP::Expect=HASH(0x841cb44)',
'a.out') called at scp.pl line 18
Starting EXPECT pattern matching...
at /usr/lib/perl5/site_perl/5.8.8/Expect.pm line 561
Expect::expect('Expect=GLOB(0x8529304)', 1, '-re', '[Yy]es\/
[Nn]o') called at /usr/lib/perl5/site_perl/5.8.8/Net/SCP/Expect.pm line
213
Net::SCP::Expect::scp('Net::SCP::Expect=HASH(0x841cb44)',
'a.out') called at scp.pl line 18
spawn id(5): list of patterns:
#1: -re `[Yy]es\\/[Nn]o'
spawn id(5): Does `'
match:
pattern #1: -re `[Yy]es\\/[Nn]o'? No.
Waiting for new data (1 seconds)...
Returning from expect with TIMEOUT or EOF
Starting EXPECT pattern matching...
at /usr/lib/perl5/site_perl/5.8.8/Expect.pm line 561
Expect::expect('Expect=GLOB(0x8529304)', 'undef', 'ARRAY
(0x85297fc)', 'ARRAY(0x852985c)', 'ARRAY(0x8529ec8)') called at /usr/
lib/perl5/site_perl/5.8.8/Net/SCP/Expect.pm line 273
Net::SCP::Expect::scp('Net::SCP::Expect=HASH(0x841cb44)',
'a.out') called at scp.pl line 18
spawn id(5): list of patterns:
#1: -re `(?-xism:[Pp]ass.*)'
#2: -re `(?-xism:\w+.*)'
#3: -eof `'
spawn id(5): Does `'
match:
pattern #1: -re `(?-xism:[Pp]ass.*)'? No.
pattern #2: -re `(?-xism:\w+.*)'? No.
pattern #3: -eof `'? No.
Waiting for new data (unlimited seconds)...
I've attached a path that fixes this with minimal code restructuring.
-Skye
Subject: | Net-SCP-Expect-0.16.expect-eof.patch |
--- Net-SCP-Expect-0.16/Expect.pm 2009-02-06 09:16:10.000000000 -0800
+++ Net-SCP-Expect-0.16a/Expect.pm 2011-05-27 04:24:42.000000000 -0700
@@ -210,10 +210,21 @@ sub scp{
$scp->log_stdout(0);
+ my $eof = 0;
+ my $error;
+ ################################################################
+ # Some transfers can finish by the time we get here.
+ # If $timeout_err is not set and we don't check for EOF
+ # we would wait indefinitely below for an EOF that will never happen.
+ ################################################################
if($auto_yes){
- while($scp->expect($timeout_auto,-re=>'[Yy]es\/[Nn]o')){
- $scp->send("yes\n");
- }
+ $error = ($scp->expect($timeout_auto,
+ [qr/[Yy]es\/[Nn]o/ => sub {
+ $scp->send("yes\n");
+ exp_continue;
+ }
+ ],
+ ['eof' => sub { $eof = 1 }]))[1];
}
if ($password) {
@@ -243,38 +254,37 @@ sub scp{
# The exception to this is verbose output, which can mistakenly
# be picked up by Expect.
################################################################
- my $error;
- my $eof = 0;
- unless($no_check || $verbose){
-
- $error = ($scp->expect($timeout_err,
- [qr/[Pp]ass.*/ => sub{
- my $error = $scp->before() || $scp->match();
- if($handler){
- $handler->($error);
- return;
- }
- else{
- croak("Error: Bad password [$error]");
- }
- }
- ],
- [qr/\w+.*/ => sub{
- my $error = $scp->match() || $scp->before();
- if($handler){
- $handler->($error);
- return;
- }
- else{
- croak("Error: last line returned was: $error");
- }
- }
- ],
- ['eof' => sub{ $eof = 1 } ],
- ))[1];
- }
- else{
- $error = ($scp->expect($timeout_err, ['eof' => sub { $eof = 1 }]))[1];
+ unless($eof) {
+ unless($no_check || $verbose){
+ $error = ($scp->expect($timeout_err,
+ [qr/[Pp]ass.*/ => sub{
+ my $error = $scp->before() || $scp->match();
+ if($handler){
+ $handler->($error);
+ return;
+ }
+ else{
+ croak("Error: Bad password [$error]");
+ }
+ }
+ ],
+ [qr/\w+.*/ => sub{
+ my $error = $scp->match() || $scp->before();
+ if($handler){
+ $handler->($error);
+ return;
+ }
+ else{
+ croak("Error: last line returned was: $error");
+ }
+ }
+ ],
+ ['eof' => sub{ $eof = 1 } ],
+ ))[1];
+ }
+ else{
+ $error = ($scp->expect($timeout_err, ['eof' => sub { $eof = 1 }]))[1];
+ }
}
if($verbose){ print $scp->after(),"\n" }