File Coverage

blib/lib/Net/SSH/Expect.pm
Criterion Covered Total %
statement 42 255 16.4
branch 0 104 0.0
condition 0 57 0.0
subroutine 14 45 31.1
pod 22 24 91.6
total 78 485 16.0


line stmt bran cond sub pod time code
1             package Net::SSH::Expect;
2 1     1   61639 use 5.008000;
  1         3  
  1         40  
3 1     1   7 use warnings;
  1         2  
  1         31  
4 1     1   5 use strict;
  1         7  
  1         60  
5 1         10 use fields qw(
6             host user password port no_terminal escape_char ssh_option
7             raw_pty exp_internal exp_debug log_file log_stdout restart_timeout_upon_receive
8             timeout terminator expect debug next_line before match after binary
9 1     1   1105 );
  1         10031  
10 1     1   1770 use Expect;
  1         85976  
  1         87  
11 1     1   12 use Carp;
  1         2  
  1         70  
12 1     1   6 use POSIX qw(:signal_h WNOHANG);
  1         3  
  1         10  
13            
14             our $VERSION = '1.09';
15            
16             # error contants
17 1     1   734 use constant ILLEGAL_STATE => "IllegalState";
  1         3  
  1         76  
18 1     1   5 use constant ILLEGAL_STATE_NO_SSH_CONNECTION => "IllegalState: you don't have a valid SSH connection to the server";
  1         2  
  1         49  
19 1     1   5 use constant ILLEGAL_ARGUMENT => "IllegalArgument";
  1         3  
  1         165  
20 1     1   7 use constant SSH_AUTHENTICATION_ERROR => "SSHAuthenticationError";
  1         1  
  1         57  
21 1     1   6 use constant SSH_PROCESS_ERROR => "SSHProcessError";
  1         2  
  1         84  
22 1     1   5 use constant SSH_CONNECTION_ERROR => "SSHConnectionError";
  1         2  
  1         143  
23 1     1   21 use constant SSH_CONNECTION_ABORTED => "SSHConnectionAborted";
  1         2  
  1         4338  
24            
25             sub new {
26 0     0 0   my $type = shift;
27 0           my %args = @_;
28 0   0       my Net::SSH::Expect $self = fields::new(ref $type || $type);
29            
30             # Options used to configure the SSH command
31 0   0       $self->{host} = $args{host}|| undef;
32 0   0       $self->{user} = $args{user} || $ENV{'USER'};
33 0   0       $self->{password} = $args{password} || undef;
34 0   0       $self->{port} = $args{port} || undef; # ssh -p
35 0   0       $self->{no_terminal} = $args{no_terminal} || 0; # ssh -T
36 0   0       $self->{escape_char} = $args{escape_char} || undef; # ssh -e
37 0   0       $self->{ssh_option} = $args{ssh_option} || undef; # arbitrary ssh options
38 0   0       $self->{binary} = $args{binary} || "ssh"; # path to SSH binary.
39            
40             # Options used to configure the Expect object
41 0   0       $self->{raw_pty} = $args{raw_pty} || 0;
42 0   0       $self->{exp_internal} = $args{exp_internal} || 0;
43 0   0       $self->{exp_debug} = $args{exp_debug} || 0;
44 0   0       $self->{log_file} = $args{log_file} || undef;
45 0   0       $self->{log_stdout} = $args{log_stdout} || 0;
46 0   0       $self->{restart_timeout_upon_receive} = $args{restart_timeout_upon_receive} || 0;
47            
48             # Attributes for this module
49 0 0         $self->timeout(defined $args{timeout} ? $args{timeout} : 1);
50 0   0       $self->{terminator} = $args{terminator} || "\n";
51 0           $self->{next_line} = "";
52 0           $self->{expect} = undef; # this will hold the Expect instance
53 0   0       $self->{debug} = $args{debug} || 0;
54 0           $self->{before} = "";
55 0           $self->{match} = "";
56 0           $self->{after} = "";
57            
58             # validating the user input
59 0           foreach my $key (keys %args) {
60 0 0         if (! exists $self->{$key} ) {
61 0           croak ILLEGAL_ARGUMENT . " attribute '$key' is not a valid constructor argument.";
62             }
63             }
64            
65 0           return $self;
66             }
67            
68             # boolean run_ssh() - forks the ssh client process opening an ssh connection to the SSH server.
69             #
70             # This method has three roles:
71             # 1) Instantiate a new Expect object configuring it with all the defaults and user-defined
72             # settings.
73             # 2) Define the ssh command line using the defaults and user-defined settings
74             # 3) Fork the ssh process using the spawn() method of the Expect instance we created.
75             # The SSH connection is established on this step using the user account set in the 'user'
76             # constructor attribute. No password is sent here, that happens only in the login() method.
77             #
78             # This method is run internally by the login() method so you don't need to run it yourself
79             # in most of the cases. You'll run this method alone if you had set up public-key authentication
80             # between the ssh client and the ssh server. In this case you only need to call this method
81             # to have an authenticated ssh connection, you won't call login(). Note that when you
82             # use public-key authentication you won't need to set the 'password' constructor attribute
83             # but you still need to define the 'user' attribute.
84             # If you don't know how to setup public-key authentication there's a good guide at
85             # http://sial.org/howto/openssh/publickey-auth/
86             #
87             # returns:
88             # boolean: 1 if the ssh ran OK or 0 otherwise. In case of failures, use $! to do get info.
89             sub run_ssh {
90 0     0 1   my Net::SSH::Expect $self = shift;
91            
92 0           my $user = $self->{user};
93 0           my $host = $self->{host};
94            
95 0 0         croak(ILLEGAL_STATE . " field 'host' is not set.") unless $host;
96 0 0         croak(ILLEGAL_STATE . " field 'user' is not set.") unless $user;
97            
98 0           my $log_file = $self->{log_file};
99 0           my $log_stdout = $self->{log_stdout};
100 0           my $exp_internal = $self->{exp_internal};
101 0           my $exp_debug = $self->{exp_debug};
102 0           my $no_terminal = $self->{no_terminal};
103 0           my $raw_pty = $self->{raw_pty};
104 0           my $escape_char = $self->{escape_char};
105 0           my $ssh_option = $self->{ssh_option};
106 0           my $port = $self->{port};
107 0           my $rtup = $self->{restart_timeout_upon_receive};
108            
109             # Gather flags.
110 0           my $flags = "";
111 0 0         $flags .= $escape_char ? "-e '$escape_char' " : "-e none ";
112 0 0         $flags .= "-p $port " if $port;
113 0 0         $flags .= "-T " if $no_terminal;
114 0 0         $flags .= $ssh_option if $ssh_option;
115            
116             # this sets the ssh command line
117 0           my $ssh_string = $self->{binary} . " $flags $user\@$host";
118            
119             # creating the Expect object
120 0           my $exp = new Expect();
121            
122             # saving this instance
123 0           $self->{expect} = $exp;
124            
125             # configuring the expect object
126 0           $exp->log_stdout($log_stdout);
127 0 0         $exp->log_file($log_file, "w") if $log_file;
128 0           $exp->exp_internal($exp_internal);
129 0           $exp->debug($exp_debug);
130 0           $exp->raw_pty($raw_pty);
131 0           $exp->restart_timeout_upon_receive($rtup);
132 0           my $success = $exp->spawn($ssh_string);
133            
134 0           return (defined $success);
135             }
136            
137             # string login ([$login_prompt, $password_prompt] [,$test_success]) - authenticates on the ssh server.
138             # This method responds to the authentication prompt sent by the SSH server.
139             # You can customize the "Login:" and "Password:" prompts that must be expected by passing their
140             # patterns as arguments to this method, although this method has default values that work to most
141             # SSH servers out there.
142             # It runs the run_ssh() method only if it wasn't run before(), but it'll die
143             # if run_ssh() returns false.
144             #
145             # param:
146             # $login_prompt: A pattern string used to match the "Login:" prompt. The default
147             # pattern is qr/ogin:\s*$/
148             #
149             # $password_prompt: A pattern string used to match the "Password:" prompt. The default
150             # pattern is qr/[Pp]assword.*?:|[Pp]assphrase.*?:/
151             #
152             # $test_success: 0 | 1. if 1, login will do an extra-test to verify if the password
153             # entered was accepted. The test consists in verifying if, after sending the password,
154             # the "Password" prompt shows up again what would indicate that the password was rejected.
155             # This test is disabled by default.
156             #
157             # OBS: the number of paramaters passed to this method will tell it what parameters are being passed:
158             # 0 parameters: login() : All the default values will be used.
159             # 1 parameter: login(1) : The $test_success parameter is set.
160             # 2 parameters: login("Login:", "Password:") : the $login_prompt and $password_prompt parameters are set.
161             # 3 parameters: login("Login:", "Password;", 1) : the three parameters received values in this order.
162             #
163             # returns:
164             # string: whatever the SSH server wrote in my input stream after loging in. This usually is some
165             # welcome message and/or the remote prompt. You could use this string to do your verification
166             # that the login was successful. The content returned is removed from the input stream.
167             # dies:
168             # IllegalState: if any of 'host' or 'user' or 'password' fields are unset.
169             # SSHProccessError: if run_ssh() failed to spawn the ssh process
170             # SSHConnectionError: if the connection failed for some reason, like invalid 'host' address or network problems.
171             sub login {
172 0     0 1   my Net::SSH::Expect $self = shift;
173            
174             # setting the default values for the parameters
175 0           my ($login_prompt, $password_prompt, $test_success) = ( qr/ogin:\s*$/, qr/[Pp]assword.*?:|[Pp]assphrase.*?:/, 0);
176            
177             # attributing the user defined values
178 0 0 0       if (@_ == 2 || @_ == 3) {
179 0           $login_prompt = shift;
180 0           $password_prompt = shift;
181             }
182 0 0         if (@_ == 1) {
183 0           $test_success = shift;
184             }
185            
186 0           my $user = $self->{user};
187 0           my $password = $self->{password};
188 0           my $timeout = $self->{timeout};
189 0           my $t = $self->{terminator};
190            
191 0 0         croak(ILLEGAL_STATE . " field 'user' is not set.") unless $user;
192 0 0         croak(ILLEGAL_STATE . " field 'password' is not set.") unless $password;
193            
194             # spawns the ssh process if this wasn't done yet
195 0 0         if (! defined($self->{expect})) {
196 0 0         $self->run_ssh() or croak SSH_PROCESS_ERROR . " Couldn't start ssh: $!\n";
197             }
198            
199 0           my $exp = $self->get_expect();
200            
201             # loggin in
202             $self->_sec_expect($timeout,
203 0     0     [ qr/\(yes\/no\)\?\s*$/ => sub { $exp->send("yes$t"); exp_continue; } ],
  0            
204 0     0     [ $password_prompt => sub { $exp->send("$password$t"); } ],
205 0     0     [ $login_prompt => sub { $exp->send("$user$t"); exp_continue; } ],
  0            
206 0     0     [ qr/REMOTE HOST IDEN/ => sub { print "FIX: .ssh/known_hosts\n"; exp_continue; } ],
  0            
207             [ timeout => sub
208             {
209 0     0     croak SSH_AUTHENTICATION_ERROR . " Login timed out. " .
210             "The input stream currently has the contents bellow: " .
211             $self->peek();
212             }
213 0           ]
214             );
215            
216             # verifying if we failed to logon
217 0 0         if ($test_success) {
218             $self->_sec_expect($timeout,
219             [ $password_prompt =>
220             sub {
221 0     0     my $error = $self->peek();
222 0           croak(SSH_AUTHENTICATION_ERROR . " Error: Bad password [$error]");
223             }
224 0           ]
225             );
226             }
227            
228             # swallows any output the server wrote to my input stream after loging in
229 0           return $self->read_all();
230             }
231            
232            
233            
234             # boolean waitfor ($string [, $timeout, $match_type])
235             # This method reads until a pattern or string is found in the input stream.
236             # All the characters before and including the match are removed from the input stream.
237             #
238             # After waitfor returns, use the methods before(), match() and after() to get the data
239             # 'before the match', 'what matched', and 'after the match' respectively.
240             #
241             # If waitfor returns false, whatever content is on input stream can be accessed with
242             # before(). In this case before() will return the same content as peek().
243             #
244             # params:
245             # $string: a string to be matched. It can be a regular expression or a literal string
246             # anb its interpretation as one or other depends on $match_type. Default is
247             # 're', what treats $string as a regular expression.
248             #
249             # $timeout: the timeout in seconds while waiting for $string
250             #
251             # $match_type: match_type affects how $string will be matched:
252             # '-re': means $string is a regular expression.
253             # '-ex': means $string is an "exact match", i.e., will be matched literally.
254             #
255             # returns:
256             # boolean: 1 is returned if string was found, 0 otherwise. When the match fails
257             # waitfor() will only return after waiting $timeout seconds.
258             #
259             # dies:
260             # SSH_CONNECTION_ABORTED if EOF is found (error type 2)
261             # SSH_PROCESS_ERROR if the ssh process has died (error type 3)
262             # SSH_CONNECTION_ERROR if unknown error (type 4) is found
263             sub waitfor {
264 0     0 1   my Net::SSH::Expect $self = shift;
265 0           my $pattern = shift;
266 0 0         my $timeout = @_ ? shift : $self->{timeout};
267 0 0         my $match_type = @_ ? shift : '-re';
268 0 0 0       croak ( ILLEGAL_ARGUMENT . "match_type '$match_type' is invalid." )
269             unless ($match_type eq '-re' || $match_type eq '-ex');
270            
271 0           my ($pos, $error);
272 0           ($pos, $error, $self->{match}, $self->{before}, $self->{after})
273             = $self->_sec_expect($timeout, $match_type, $pattern);
274            
275 0           my $debug = $self->{debug};
276            
277             # sanity verification
278             # Enforcing that match before and after have correct values
279 0 0         if (! defined $pos) { # if the pattern failed to match
280             # match should be undef
281 0 0         if (defined $self->{match}) {
282 0 0         if ($debug) {
283 0           carp ("The last expect() didn't match but \$exp->match() returned content '". $self->{match} ."'." .
284             " We'll set \$self->{match} to undef explicitly;");
285             }
286 0           $self->{match} = undef;
287             }
288             # after should be undef
289 0 0         if (defined $self->{after}) {
290 0 0         if ($debug) {
291 0           carp ("The last expect() didn't match but \$exp->after() returned content '". $self->{after} ."'." .
292             " We'll set \$self->{after} to undef explicitly;");
293             }
294 0           $self->{after} = undef;
295             }
296             }
297            
298 0           return (defined $pos);
299             }
300            
301             # string before() - returns the "before match" data of the last waitfor() call, or empty string.
302             # if the last waitfor() didn't match, before() will return all the current content on the input
303             # stream, just as if you had called peek() with the same timeout.
304             sub before {
305 0     0 1   my Net::SSH::Expect $self = shift;
306 0           return $self->{before};
307             }
308            
309             # string match() - returns the "match" data of the last waitfor() call, or undef if didn't match.
310             sub match {
311 0     0 1   my Net::SSH::Expect $self = shift;
312 0           return $self->{match};
313             }
314            
315             # string after() - returns the "after match" data of the last waitfor() call, or undef if didn't match.
316             sub after {
317 0     0 1   my Net::SSH::Expect $self = shift;
318 0           return $self->{after};
319             }
320            
321            
322             # send ("string") - breaks on through to the other side.
323             sub send {
324 0     0 1   my Net::SSH::Expect $self = shift;
325 0           my $send = shift;
326 0 0         croak (ILLEGAL_ARGUMENT . " missing argument 'string'.") unless ($send);
327 0           my $exp = $self->get_expect();
328 0           my $t = $self->{terminator};
329 0           $exp->send($send . $t);
330             }
331            
332             # peek([$timeout]) - returns what is in the input stream without removing anything
333             # params:
334             # $timeout: how many seconds peek() will wait for input
335             # dies:
336             # SSH_CONNECTION_ABORTED if EOF is found (error type 2)
337             # SSH_PROCESS_ERROR if the ssh process has died (error type 3)
338             # SSH_CONNECTION_ERROR if unknown error (type 4) is found
339             sub peek {
340 0     0 1   my Net::SSH::Expect $self = shift;
341 0 0         my $timeout = @_ ? shift : $self->{timeout};
342 0           my $exp = $self->get_expect();
343 0           $self->_sec_expect($timeout);
344 0           return $exp->before();
345             }
346            
347             # string eat($string)- removes all the head of the input stream until $string inclusive.
348             # eat() will only be able to remove the $string if it's currently present on the
349             # input stream because eat() will wait 0 seconds before removing it.
350             #
351             # Use it associated with peek to eat everything that appears on the input stream:
352             #
353             # while ($chunk = $exp->eat($exp->peak())) {
354             # print $chunk;
355             # }
356             #
357             # Or use the read_all() method that does the above loop for you returning the accumulated
358             # result.
359             #
360             # param:
361             # string: a string currently available on the input stream.
362             # If $string doesn't start in the head, all the content before $string will also
363             # be removed.
364             #
365             # If $string is undef or empty string it will be returned immediately as it.
366             #
367             # returns:
368             # string: the removed content or empty string if there is nothing in the input stream.
369             #
370             # dies:
371             # SSH_CONNECTION_ABORTED if EOF is found (error type 2)
372             # SSH_PROCESS_ERROR if the ssh process has died (error type 3)
373             # SSH_CONNECTION_ERROR if unknown error (type 4) is found
374             #
375             # debbuging features:
376             # The following warnings are printed to STDERR if $exp->debug() == 1:
377             # eat() prints a warning is $string wasn't found in the head of the input stream.
378             # eat() prints a warning is $string was empty or undefined.
379             #
380             sub eat {
381 0     0 1   my Net::SSH::Expect $self = shift;
382 0           my $string = shift;
383 0 0 0       unless (defined $string && $string ne "") {
384 0 0         if ($self->{debug}) {
385 0           carp ("eat(): param \$string is undef or empty string\n");
386             }
387 0           return $string;
388             }
389            
390 0           my $exp = $self->get_expect();
391            
392             # the top of the input stream that will be removed from there and
393             # returned to the user
394 0           my $top;
395            
396             # eat $string from (hopefully) the head of the input stream
397 0           $self->_sec_expect(0, '-ex', $string);
398 0           $top .= $exp->match();
399            
400             # if before() returns any content, the $string passed is not in the beginning of the
401             # input stream.
402 0 0 0       if (defined $exp->before() && !($exp->before() eq "") ) {
403 0 0         if ($self->{debug}) {
404 0           carp ("eat(): param \$string '$string' was found on the input stream ".
405             "after '". $exp->before() . "'.");
406             }
407 0           $top = $exp->before() . $top;
408             }
409 0           return $top;
410             }
411            
412             # string read_all([$timeout]) - reads and remove all the output from the input stream.
413             # The reading/removing process will be interrupted after $timeout seconds of inactivity
414             # on the input stream.
415             sub read_all {
416 0     0 1   my Net::SSH::Expect $self = shift;
417 0 0         my $timeout = @_ ? shift : $self->{timeout};
418 0           my $out;
419 0           while ($self->_sec_expect($timeout, '-re', qr/[\s\S]+/)) {
420 0           $out .= $self->get_expect()->match();
421             }
422 0           return $out;
423             }
424            
425            
426             # boolean has_line([$timeout]) - tells if there is one more line on the input stream
427             sub has_line {
428 0     0 0   my Net::SSH::Expect $self = shift;
429 0 0         my $timeout = @_ ? shift : $self->{timeout};
430 0           $self->{next_line} = $self->read_line($timeout);
431 0           return (defined $self->{next_line});
432             }
433            
434             # string read_line([$timeout]) - reads the next line from the input stream
435             # Read a line of text. A line is considered to be terminated by the 'teminator'
436             # character. Default is "\n". Lines can also be ended with "\r" or "\r\n".
437             # Remember to adequate this for your system with the terminator() method.
438             # When there are no more lines available, read_line() returns undef. Note that this doen't mean
439             # there is no data left on input stream since there can be a string not terminated with the
440             # 'terminator' character, notably the remote prompt could be left there when read_line() returns
441             # undef.
442             #
443             # params:
444             # $timeout: the timeout waiting for a line. Defaults to timeout().
445             #
446             # returns:
447             # string: a line on the input stream, without the trailing 'terminator' character.
448             # An empty string indicates that the line read only contained the 'terminator'
449             # character (an empty line)
450             # undef: when there are no more lines on the input stream.
451             #
452             sub read_line {
453 0     0 1   my Net::SSH::Expect $self = shift;
454 0 0         my $timeout = @_ ? shift : $self->{timeout};
455 0           my $t = $self->{terminator};
456 0           my $line = undef;
457 0 0         if ( $self->waitfor($t, $timeout) ) {
458 0           $line = $self->before();
459             }
460 0           return $line;
461             }
462            
463             # string exec($cmd [,$timeout]) - executes a command, returns the complete output
464             sub exec {
465 0     0 1   my Net::SSH::Expect $self = shift;
466 0           my $cmd = shift;
467 0 0         my $timeout = @_ ? shift : $self->{timeout};
468 0           $self->send($cmd);
469 0           return $self->read_all($timeout);
470             }
471            
472             sub close {
473 0     0 1   my Net::SSH::Expect $self = shift;
474 0           my $exp = $self->get_expect();
475 0           $exp->hard_close();
476 0           return 1;
477             }
478            
479            
480             # returns
481             # reference: the internal Expect object used to manage the ssh connection.
482             sub get_expect {
483 0     0 1   my Net::SSH::Expect $self = shift;
484 0 0         my $exp = defined ($self->{expect}) ? $self->{expect} :
485             croak (ILLEGAL_STATE_NO_SSH_CONNECTION);
486 0           return $exp;
487             }
488            
489             # void restart_timeout_upon_receive( 0 | 1 ) - changes the timeout counter behaviour
490             # params:
491             # boolean: if true, sets the timeout to "inactivity timeout", if false
492             # sets it to "absolute timeout".
493             # dies:
494             # IllegalParamenter if argument is not given.
495             sub restart_timeout_upon_receive {
496 0     0 1   my Net::SSH::Expect $self = shift;
497 0 0         my $value = @_ ? shift : croak (ILLEGAL_ARGUMENT . " missing argument.");
498 0           $self->get_expect()->restart_timeout_upon_receive($value);
499             }
500            
501             #
502             # Setter methods
503             #
504            
505             sub host {
506 0     0 1   my Net::SSH::Expect $self = shift;
507 0 0         croak(ILLEGAL_ARGUMENT . " No host supplied to 'host()' method") unless @_;
508 0           $self->{host} = shift;
509             }
510            
511             sub user {
512 0     0 1   my Net::SSH::Expect $self = shift;
513 0 0         croak(ILLEGAL_ARGUMENT . " No user supplied to 'user()' method") unless @_;
514 0           $self->{user} =shift;
515             }
516            
517             sub password{
518 0     0 1   my Net::SSH::Expect $self = shift;
519 0 0         croak(ILLEGAL_ARGUMENT . " No password supplied to 'password()' method") unless @_;
520 0           $self->{password} = shift;
521             }
522            
523             sub port {
524 0     0 1   my Net::SSH::Expect $self = shift;
525 0 0         croak(ILLEGAL_ARGUMENT . " No value passed to 'port()' method") unless @_;
526 0           my $port = shift;
527 0 0 0       croak (ILLEGAL_ARGUMENT . " Passed number '$port' is not a valid port number")
      0        
528             if ($port !~ /\A\d+\z/ || $port < 1 || $port > 65535);
529 0           $self->{port} = $port;
530             }
531            
532             sub terminator {
533 0     0 1   my Net::SSH::Expect $self = shift;
534 0 0         $self->{terminator} = shift if (@_);
535 0           return $self->{terminator};
536             }
537            
538             # boolean debug([0|1]) - gets/sets the $exp->{debug} attribute.
539             sub debug {
540 0     0 1   my Net::SSH::Expect $self = shift;
541 0 0         if (@_) {
542 0           $self->{debug} = shift;
543             }
544 0           return $self->{debug};
545             }
546            
547             # number timeout([$number]) - get/set the default timeout used for every method
548             # that reads data from the input stream.
549             # The only exception is eat() that has its timeout defined as 0.
550             sub timeout {
551 0     0 1   my Net::SSH::Expect $self = shift;
552 0 0         if (! @_ ) {
553 0           return $self->{timeout};
554             }
555 0           my $timeout = shift;
556 0 0 0       if ( $timeout !~ /\A\d+\z/ || $timeout < 0) {
557 0           croak (ILLEGAL_ARGUMENT . " timeout '$timeout' is not a positive number.");
558             }
559 0           $self->{timeout} = $timeout;
560             }
561            
562             #
563             # Private Methods
564             #
565            
566             # _sec_expect(@params) - secure expect. runs expect with @params and croaks if problems happen
567             # Note: timeout is not considered a problem.
568             # params:
569             # the same parameters as expect() accepts.
570             # returns:
571             # the same as expect() returns
572             # dies:
573             # SSH_CONNECTION_ABORTED if EOF is found (error type 2)
574             # SSH_PROCESS_ERROR if the ssh process has died (error type 3)
575             # SSH_CONNECTION_ERROR if unknown error is found (error type 4)
576             sub _sec_expect {
577 0     0     my Net::SSH::Expect $self = shift;
578 0 0         my @params = @_ ? @_ : die ("\@params cannot be undefined.");
579 0           my $exp = $self->get_expect();
580 0           my ($pos, $error, $match, $before, $after) = $exp->expect(@params);
581 0 0         if (defined $error) {
582 0           my $error_first_digit = substr($error, 0, 1);
583 0 0         if ($error_first_digit eq '2') {
    0          
    0          
584             # found eof
585 0           croak (SSH_CONNECTION_ABORTED);
586             } elsif ($error_first_digit eq '3') {
587             # ssh process died
588 0           croak (SSH_PROCESS_ERROR . " The ssh process was terminated.");
589             } elsif ($error_first_digit eq '4') {
590             # unknown reading error
591 0           croak (SSH_CONNECTION_ERROR . " Reading error type 4 found: $error");
592             }
593             }
594 0 0         if (wantarray()) {
595 0           return ($pos, $error, $match, $before, $after);
596             } else {
597 0           return $pos;
598             }
599             }
600            
601             1;
602            
603            
604