File Coverage

blib/lib/Net/SSH2/Expect.pm
Criterion Covered Total %
statement 6 110 5.4
branch 0 32 0.0
condition 0 6 0.0
subroutine 2 15 13.3
pod 8 9 88.8
total 16 172 9.3


line stmt bran cond sub pod time code
1             #
2             # (c) 2011 Jan Gehring
3             # vim: set ts=3 sw=3 tw=0:
4             # vim: set expandtab:
5             #
6              
7             =head1 NAME
8              
9             Net::SSH2::Expect - An Expect like module for Net::SSH2
10              
11             =head1 DESCRIPTION
12              
13             This is a module to have expect like features for Net::SSH2. Please report bugs at GitHub L
14              
15             =head1 DEPENDENCIES
16              
17             =over 4
18              
19             =item *
20              
21             L
22              
23             =back
24              
25             =head1 SYNOPSIS
26              
27             use Net::SSH2::Expect;
28            
29             my $exp = Net::SSH2::Expect->new($ssh2);
30             $exp->spawn("passwd");
31             $exp->expect($timeout, [
32             qr/Enter new UNIX password:/ => sub {
33             my ($exp, $line) = @_;
34             $exp->send($new_password);
35             },
36             qr/Retype new UNIX password:/ => sub {
37             my ($exp, $line) = @_;
38             $exp->send($new_password);
39             },
40             ]);
41              
42             =head1 CLASS METHODS
43              
44             =cut
45              
46             package Net::SSH2::Expect;
47              
48 1     1   845 use strict;
  1         3  
  1         46  
49 1     1   7 use warnings;
  1         3  
  1         1231  
50              
51             our $VERSION = "0.2";
52              
53             =over 4
54              
55             =item new($ssh2)
56              
57             Constructor: You need to parse an connected Net::SSH2 Object.
58              
59             =cut
60              
61             our $Log_Stdout = 1;
62              
63             sub new {
64 0     0 1   my $that = shift;
65 0   0       my $proto = ref($that) || $that;
66 0           my $self = {};
67              
68 0           bless($self, $proto);
69              
70 0           $self->{"__shell"} = $_[0]->channel();
71 0           $self->{"__shell"}->pty("vt100");
72 0           $self->{"__shell"}->shell;
73              
74 0           $self->{"__log_stdout"} = $Net::SSH2::Expect::Log_Stdout;
75 0     0     $self->{"__log_to"} = sub {};
  0            
76 0           $self->{"output"} = [];
77              
78 0           return $self;
79             }
80              
81             =item log_stdout(0|1)
82              
83             Log on STDOUT.
84              
85             =cut
86             sub log_stdout {
87 0     0 1   my ($self, $log) = @_;
88 0           $self->{"__log_stdout"} = $log;
89             }
90              
91             =item log_file($file)
92              
93             Log everything to a file. $file can be a filename, a filehandle or a subRef.
94              
95             =cut
96             sub log_file {
97 0     0 1   my ($self, $file) = @_;
98 0           $self->{"__log_to"} = $file;
99             }
100              
101             sub shell {
102 0     0 0   my ($self) = @_;
103 0           return $self->{"__shell"};
104             }
105              
106             =item spawn($command, @parameters)
107              
108             Spawn $command with @parameters as parameters.
109              
110             =cut
111             sub spawn {
112 0     0 1   my ($self, $command, @parameters) = @_;
113              
114 0           my $cmd = "$command " . join(" ", @parameters);
115              
116 0           $self->shell->write("echo 'randomnum'\n");
117              
118 0           my $line = "";
119 0           while($line ne "randomnum\r") {
120 0           my $buf;
121 0           $self->shell->read($buf, 1);
122              
123 0 0         if($buf eq "\n") {
124 0           $line = "";
125 0           next;
126             }
127              
128 0           $line .= $buf;
129             }
130              
131 0           $cmd .= " && echo ___END___0_";
132              
133             #$self->shell->write("PS1=''\n$cmd\necho ___END___\$?_\n");
134 0           $self->shell->write("$cmd\n");
135 0           $self->shell->flush;
136              
137 0           $line = "";
138 0           my $counter = 0;
139 0           while(1) {
140 0           my $buf;
141 0           $self->shell->read($buf, 1);
142 0 0         if($buf eq "\r") { next; }
  0            
143 0           $line .= $buf;
144              
145 0 0         if($line =~ m/$cmd\n/s) {
146 0           $counter++;
147             }
148              
149 0 0 0       if($line =~ m/$cmd\n/s && $counter==2) {
150 0           last;
151             }
152              
153 0 0         if($buf eq "\n") {
154 0           $line = "";
155 0           next;
156             }
157             }
158              
159             }
160              
161             =item soft_close()
162              
163             Currently only an alias to hard_close();
164              
165             =cut
166              
167             sub soft_close {
168 0     0 1   my ($self) = @_;
169 0           $self->hard_close;
170             }
171              
172             =item hard_close();
173              
174             Stops the execution of the process.
175              
176             =cut
177              
178             sub hard_close {
179 0     0 1   my ($self) = @_;
180 0           die;
181             }
182              
183             =item expect($timeout, @match_patters)
184              
185             This method controls the execution of your process.
186              
187             =cut
188              
189             sub expect {
190 0     0 1   my ($self, $timeout, @match_patterns) = @_;
191              
192 0           $? = 1;
193             eval {
194 0           my $success = 0;
195 0     0     local $SIG{'ALRM'} = sub { die; };
  0            
196 0           alarm $timeout;
197              
198 0           my $line = "";
199 0           while(1) {
200 0           my $buf;
201 0           $self->shell->read($buf, 1);
202 0 0         if($buf eq "\r") { next; }
  0            
203              
204             # log to stdout if wanted
205 0           $line .= $buf;
206              
207 0 0         if($self->_check_patterns($line, @match_patterns)) {
208 0           $line = "";
209 0           alarm $timeout;
210 0           next;
211             }
212              
213 0 0         if($line =~ m/^___END___0_/) {
214 0           $? = 0;
215 0           $success = 1;
216 0           last;
217             }
218              
219 0 0         print $buf if $self->{"__log_stdout"};
220 0           $self->_log($buf);
221              
222 0 0         if($buf eq "\n") {
223 0           chomp $line;
224 0           push(@{ $self->{output} }, $line);
  0            
225 0           $line = "";
226             }
227              
228             }
229              
230 0 0         if($self->{output}->[0] =~ m/^\s*$/) {
231 0           shift @{ $self->{output} };
  0            
232             }
233 0           return $success;
234 0 0         } or do {
235 0           return;
236             };
237             }
238              
239             =item send($string)
240              
241             Send a string to the running command.
242              
243             =cut
244              
245             sub send {
246 0     0 1   my ($self, $str) = @_;
247 0           $self->shell->write($str);
248             }
249              
250             sub _check_patterns {
251 0     0     my ($self, $line, @match_patterns) = @_;
252              
253 0           my $pattern_hash = { @{$match_patterns[0]} };
  0            
254              
255 0           for my $pattern (keys %{ $pattern_hash }) {
  0            
256 0 0         if($line =~ $pattern) {
257 0           my $code = $pattern_hash->{$pattern};
258 0           &$code($self, $line);
259 0           return 1;
260             }
261             }
262             }
263              
264             sub _log {
265 0     0     my ($self, $str) = @_;
266              
267 0           my $log_to = $self->{"__log_to"};
268              
269 0 0         if(ref($log_to) eq "CODE") {
    0          
270 0           &$log_to($str);
271             }
272             elsif(ref($log_to) eq "GLOB") {
273 0           print $log_to $str;
274             }
275             else {
276             # log to a file
277 0 0         open(my $fh, ">>", $log_to) or die($!);
278 0           print $fh $str;
279 0           close($fh);
280             }
281              
282             }
283              
284             =back
285              
286             =cut
287              
288             1;
289