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
|
|
|
|
|
|
|
|