7632dd777141a0cb966435f0012beaa4bbde8cbc
[clearscm.git] / lib / Rexec.pm
1 =pod                                                                                    
2                                                                                         
3 =head1 NAME $RCSfile: Rexec.pm,v $                                                      
4                                                                                         
5 Execute commands remotely and returning the output and status of the                    
6 remotely executed command.                                                              
7                                                                                         
8 =head1 VERSION                                                                          
9                                                                                         
10 =over                                                                                   
11                                                                                         
12 =item Author:                                                                           
13                                                                                         
14 Andrew DeFaria <Andrew@ClearSCM.com>                                                    
15                                                                                         
16 =item Revision:                                                                         
17                                                                                         
18 $Revision: 1.21 $                                                                       
19                                                                                         
20 =item Created:                                                                          
21                                                                                         
22 Mon Oct  9 18:28:28 CDT 2006                                                            
23                                                                                         
24 =item Modified:                                                                         
25                                                                                         
26 $Date: 2012/04/07 00:39:48 $                                                            
27                                                                                         
28 =back                                                                                   
29                                                                                         
30 =head1 SYNOPSIS                                                                         
31                                                                                         
32   use Rexec;                                                                            
33                                                                                         
34   my $status;                                                                           
35   my $cmd;                                                                              
36   my @lines;                                                                            
37                                                                                         
38   my $remote = new Rexec (host => $host);                                               
39                                                                                         
40   if ($remote) {                                                                        
41     print "Connected using " . $remote->{protocol} . " protocol\n";                     
42                                                                                         
43     $cmd = "ls /tmp";                                                                   
44     @lines = $remote->execute ($cmd);                                                   
45     $status = $remote->status;                                                          
46     print "$cmd status: $status\n";                                                     
47     $remote->print_lines;                                                               
48                                                                                         
49     print "$_\n" foreach ($remote->execute ("cat /etc/passwd"));                        
50   } else {                                                                              
51     print "Unable to connect to $username\@$host\n";                                    
52   } # if                                                                                
53                                                                                         
54 =head1 DESCRIPTION                                                                      
55                                                                                         
56 This module provides an object oriented interface to executing remote                   
57 commands on Linux/Unix system (or potentially well configured Windows                   
58 machines with Cygwin installed). Upon object creation a connection is                   
59 attempted to the specified host in a cascaded fashion. First ssh is                     
60 attempted, then rsh/rlogin and finally telnet. This clearly favors                      
61 secure methods over those less secure ones. If username or password is                  
62 prompted for, and if they are supplied, then they are used, otherwise                   
63 the attempted connection is considered failed.                                          
64                                                                                         
65 Once connected the caller can use the exec method to execute commands                   
66 on the remote host. Upon object destruction the connection is                           
67 shutdown. Output from the remotely executed command is returned                         
68 through the exec method and also avaiable view the lines                                
69 method. Remote status is available via the status method. This means                    
70 you can now more reliably obtain the status of the command executed                     
71 remotely instead of just the status of the ssh/rsh command itself.                      
72                                                                                         
73 Note: Currently no attempt has been made to differentiate output                        
74 written to stdout and stderr.                                                           
75                                                                                         
76 As Expect is used to drive the remote session particular attention                      
77 should be defining a regex to locate the prompt. The standard prompt                    
78 regex (if not specified by the caller at object creation) is qr'[#>:$]                  
79 $'. This covers most default and common prompts.                                        
80                                                                                         
81 =head1 Handling Timeouts                                                                
82                                                                                         
83 The tricky thing when dealing with remote execution is attempting to                    
84 determine if the remote machine has finished, stopped responding or                     
85 otherwise crashed. It's more of an art than a science! The best one                     
86 can do it send the command along and wait for a response. But how long                  
87 to wait is the question. If your wait is too short then you run the                     
88 risk of timing out before the remote command is finished. If you wait                   
89 too long then you can be possibly waiting for something that will not                   
90 be happening because the remote machine is either down or did not                       
91 behave in a manner that you expected it to.                                             
92                                                                                         
93 To a large extent this module attempts to mitigate these issues on the                  
94 principal that remote command execution is pretty well known. You log                   
95 in and get a prompt. Issue a command and get another prompt. If the                     
96 prompts are well known and easily determinable things go                                
97 smoothly. However what happens if you execute a command remotely that                   
98 will take 30 minutes to finish?                                                         
99                                                                                         
100 This module has two timeout values. The first is login timeout. It's                    
101 assumed that logins should happen fairly quickly. The default timeout                   
102 for logins is 5 seconds.                                                                
103                                                                                         
104 Command timeouts are set by default to 30 seconds. Most commands will                   
105 finish before then. If you expect a command to take much longer then                    
106 you can set an alternate timeout period.                                                
107                                                                                         
108 You can achieve longer timeouts in several ways. To give a longer                       
109 login timeout specify your timeout to the new call. To give a longer                    
110 exec timeout either pass a longer timeout to exec or set it view                        
111 setTimeout. The current exec timeout is returned by getTimeout.                         
112                                                                                         
113 =head1 METHODS                                                                          
114                                                                                         
115 The following routines are exported:                                                    
116                                                                                         
117 =cut                                                                                    
118                                                                                         
119 package Rexec;                                                                          
120                                                                                         
121 use strict;                                                                             
122 use warnings;                                                                           
123                                                                                         
124 use base 'Exporter';                                                                    
125                                                                                         
126 use Carp;                                                                               
127 use Expect;                                                                             
128                                                                                         
129 our $VERSION = '1.0';                                                                   
130                                                                                         
131 # This is the "normal" definition of a prompt. However what's normal?                   
132 # For example, my prompt it typically the machine name followed by a                    
133 # colon. But even that appears in error messages such as <host>: not                    
134 # found and will be mistaken for a prompt. No real good way to handle                   
135 # this so we define a standard prompt here and allow the caller to                      
136 # override that. But overriding it is tricky and left as an exercise                    
137 # to the caller.                                                                        
138                                                                                         
139 # Here we have a number of the common prompt characters [#>:%$]                         
140 # followed by a space and end of line.                                                  
141 our $DEFAULT_PROMPT = qr'[#>:%$] $';                                                    
142                                                                                         
143 my $default_login_timeout = 5;                                                          
144 my $default_exec_timeout  = 30;                                                         
145                                                                                         
146 my $debug = $ENV{DEBUG} || 0;                                                           
147                                                                                         
148 our @EXPORT = qw (                                                                      
149   exec                                                                                  
150   host                                                                                  
151   lines                                                                                 
152   login                                                                                 
153   logout                                                                                
154   new                                                                                   
155   print_lines                                                                           
156   status                                                                                
157 );                                                                                      
158                                                                                         
159 my @lines;                                                                              
160                                                                                         
161 sub ssh {                                                                               
162   my ($self) = @_;                                                                      
163                                                                                         
164   my ($logged_in, $timedout, $password_attempts) = 0;                                   
165                                                                                         
166   $self->{protocol} = 'ssh';                                                            
167                                                                                         
168   my $user = $self->{username} ? "$self->{username}\@" : '';                            
169                                                                                         
170   my $remote = Expect->new ("ssh $self->{opts} $user$self->{host}");                    
171                                                                                         
172   return if !$remote;                                                                   
173                                                                                         
174   $remote->log_user ($debug);                                                           
175                                                                                         
176   $remote->expect (                                                                     
177     $self->{timeout},                                                                   
178                                                                                         
179     # If password is prompted for, and if one has been specified, then                  
180     # use it                                                                            
181     [ qr "[P|p]assword: $",                                                             
182       sub {                                                                             
183         # If we already supplied the password then it must not have                     
184         # worked so this protocol is no good.                                           
185         return if $password_attempts;                                                   
186                                                                                         
187         my $exp = shift;                                                                
188                                                                                         
189         # If we're being prompted for password and there is no                          
190         # password to supply then there is nothing much we can do but                   
191         # return undef since we can't get in with this protocol                         
192         return if !$self->{password};                                                   
193                                                                                         
194         $exp->send ("$self->{password}\n") if $self->{password};                        
195         $password_attempts++;                                                           
196                                                                                         
197         exp_continue;                                                                   
198       }                                                                                 
199     ],                                                                                  
200                                                                                         
201     # Discard lines that begin with "ssh:" (like "ssh: <host>: not                      
202     # found")                                                                           
203     [ qr'\nssh: ',                                                                      
204       sub {                                                                             
205         return;                                                                         
206       }                                                                                 
207     ],                                                                                  
208                                                                                         
209     # If we find a prompt then everything's good                                        
210     [ $self->{prompt},                                                                  
211       sub {                                                                             
212         $logged_in = 1;                                                                 
213       }                                                                                 
214     ],                                                                                  
215                                                                                         
216     # Of course we may time out...                                                      
217     [ timeout =>                                                                        
218       sub {                                                                             
219         $timedout = 1;                                                                  
220       }                                                                                 
221     ],                                                                                  
222   );                                                                                    
223                                                                                         
224   if ($logged_in) {                                                                     
225     return $remote;                                                                     
226   } elsif ($timedout) {                                                                 
227     carp "WARNING: $self->{host} is not responding to $self->{protocol} protocol";      
228     undef $remote;                                                                      
229     return;                                                                             
230   } else {                                                                              
231     carp "WARNING: Unable to connect to $self->{host} using $self->{protocol} protocol";
232     return;                                                                             
233   } # if                                                                                
234 } # ssh                                                                                 
235                                                                                         
236 sub rlogin {                                                                            
237   my ($self) = @_;                                                                      
238                                                                                         
239   my ($logged_in, $timedout, $password_attempts) = 0;                                   
240                                                                                         
241   $self->{protocol} = "rlogin";                                                         
242                                                                                         
243   my $user = $self->{username} ? "-l $self->{username}" : "";                           
244                                                                                         
245   my $remote = Expect->new ("rsh $user $self->{host}");                                 
246                                                                                         
247   return if !$remote;                                                                   
248                                                                                         
249   $remote->log_user ($debug);                                                           
250                                                                                         
251   $remote->expect (                                                                     
252     $self->{timeout},                                                                   
253                                                                                         
254     # If password is prompted for, and if one has been specified, then                  
255     # use it                                                                            
256     [ qr "[P|p]assword: $",                                                             
257       sub {                                                                             
258         # If we already supplied the password then it must not have                     
259         # worked so this protocol is no good.                                           
260         return if $password_attempts;                                                   
261                                                                                         
262         my $exp = shift;                                                                
263                                                                                         
264         # If we're being prompted for password and there is no                          
265         # password to supply then there is nothing much we can do but                   
266         # return undef since we can't get in with this protocol                         
267         return if !$self->{password};                                                   
268                                                                                         
269         $exp->send ("$self->{password}\n");                                             
270         $password_attempts++;                                                           
271                                                                                         
272         exp_continue;                                                                   
273       }                                                                                 
274     ],                                                                                  
275                                                                                         
276     # HACK! rlogin may return "<host>: unknown host" which clashes                      
277     # with some prompts (OK it clashes with my prompt...)                               
278     [ ": unknown host",                                                                 
279       sub {                                                                             
280         return;                                                                         
281       }                                                                                 
282     ],                                                                                  
283                                                                                         
284     # If we find a prompt then everything's good                                        
285     [ $self->{prompt},                                                                  
286       sub {                                                                             
287         $logged_in = 1;                                                                 
288       }                                                                                 
289     ],                                                                                  
290                                                                                         
291     # Of course we may time out...                                                      
292     [ timeout =>                                                                        
293       sub {                                                                             
294         $timedout = 1;                                                                  
295       }                                                                                 
296     ],                                                                                  
297   );                                                                                    
298                                                                                         
299   if ($logged_in) {                                                                     
300     return $remote;                                                                     
301   } elsif ($timedout) {                                                                 
302     carp "WARNING: $self->{host} is not responding to $self->{protocol} protocol";      
303     undef $remote;                                                                      
304     return;                                                                             
305   } else {                                                                              
306     carp "WARNING: Unable to connect to $self->{host} using $self->{protocol} protocol";
307     return;                                                                             
308   } # if                                                                                
309 } # rlogin                                                                              
310                                                                                         
311 sub telnet {                                                                            
312   my ($self) = @_;                                                                      
313                                                                                         
314   my ($logged_in, $timedout, $password_attempts) = 0;                                   
315                                                                                         
316   $self->{protocol} = "telnet";                                                         
317                                                                                         
318   my $remote = Expect->new ("telnet $self->{host}");                                    
319                                                                                         
320   return if !$remote;                                                                   
321                                                                                         
322   $remote->log_user ($debug);                                                           
323                                                                                         
324   $remote->expect (                                                                     
325     $self->{timeout},                                                                   
326                                                                                         
327     # If login is prompted for, and if what has been specified, then                    
328     # use it                                                                            
329     [ qr "login: $",                                                                    
330       sub {                                                                             
331         my $exp = shift;                                                                
332                                                                                         
333         # If we're being prompted for username and there is no                          
334         # username to supply then there is nothing much we can do but                   
335         # return undef since we can't get in with this protocol                         
336         return if !$self->{username};                                                   
337                                                                                         
338         $exp->send ("$self->{username}\n");                                             
339         exp_continue;                                                                   
340       }                                                                                 
341     ],                                                                                  
342                                                                                         
343     # If password is prompted for, and if one has been specified, then                  
344     # use it                                                                            
345     [ qr "[P|p]assword: $",                                                             
346       sub {                                                                             
347         # If we already supplied the password then it must not have                     
348         # worked so this protocol is no good.                                           
349         return if $password_attempts;                                                   
350                                                                                         
351         my $exp = shift;                                                                
352                                                                                         
353         # If we're being prompted for password and there is no                          
354         # password to supply then there is nothing much we can do but                   
355         # return undef since we can't get in with this protocol                         
356         return if !$self->{password};                                                   
357                                                                                         
358         $exp->send ("$self->{password}\n");                                             
359         $password_attempts++;                                                           
360                                                                                         
361         exp_continue;                                                                   
362       }                                                                                 
363     ],                                                                                  
364                                                                                         
365     # HACK! rlogin may return "<host>: Unknown host" which clashes                      
366     # with some prompts (OK it clashes with my prompt...)                               
367     [ ": Unknown host",                                                                 
368       sub {                                                                             
369         return;                                                                         
370       }                                                                                 
371     ],                                                                                  
372                                                                                         
373     # If we find a prompt then everything's good                                        
374     [ $self->{prompt},                                                                  
375       sub {                                                                             
376         $logged_in = 1;                                                                 
377       }                                                                                 
378     ],                                                                                  
379                                                                                         
380     # Of course we may time out...                                                      
381     [ timeout =>                                                                        
382       sub {                                                                             
383         $timedout = 1;                                                                  
384       }                                                                                 
385     ],                                                                                  
386   );                                                                                    
387                                                                                         
388   if ($logged_in) {                                                                     
389     return $remote;                                                                     
390   } elsif ($timedout) {                                                                 
391     carp "WARNING: $self->{host} is not responding to $self->{protocol} protocol";      
392     undef $remote;                                                                      
393     return;                                                                             
394   } else {                                                                              
395     carp "WARNING: Unable to connect to $self->{host} using $self->{protocol} protocol";
396     return;                                                                             
397   } # if                                                                                
398 } # telnet                                                                              
399                                                                                         
400 sub login () {                                                                          
401   my ($self) = @_;                                                                      
402                                                                                         
403 =pod                                                                                    
404                                                                                         
405 =head2 login                                                                            
406                                                                                         
407 Performs a login on the remote host. Normally this is done during                       
408 construction but this method allows you to login, say again, as maybe                   
409 another user...                                                                         
410                                                                                         
411 Parameters:                                                                             
412                                                                                         
413 =for html <blockquote>                                                                  
414                                                                                         
415 =over                                                                                   
416                                                                                         
417 =item None                                                                              
418                                                                                         
419 =back                                                                                   
420                                                                                         
421 =for html </blockquote>                                                                 
422                                                                                         
423 Returns:                                                                                
424                                                                                         
425 =for html <blockquote>                                                                  
426                                                                                         
427 =over                                                                                   
428                                                                                         
429 =item Nothing                                                                           
430                                                                                         
431 =back                                                                                   
432                                                                                         
433 =for html </blockquote>                                                                 
434                                                                                         
435 =cut                                                                                    
436                                                                                         
437   # Close any prior opened sessions                                                     
438   $self->logoff if ($self->{handle});                                                   
439                                                                                         
440   my $remote;                                                                           
441                                                                                         
442   if ($self->{protocol}) {                                                              
443     if ($self->{protocol} eq "ssh") {                                                   
444       return $self->ssh;                                                                
445     } elsif ($self->{protocol} eq "rsh" or $self->{protocol} eq "rlogin") {             
446       return $self->rlogin;                                                             
447     } elsif ($self->{protocol} eq "telnet") {                                           
448       return $self->telnet;                                                             
449     } else {                                                                            
450       croak "ERROR: Invalid protocol $self->{protocol} specified", 1;                   
451     } # if                                                                              
452   } else {                                                                              
453     return $remote if $remote = $self->ssh;                                             
454     return $remote if $remote = $self->rlogin;                                          
455     return $self->telnet;                                                               
456   } # if                                                                                
457                                                                                         
458   return;                                                                               
459 } # login                                                                               
460                                                                                         
461 sub logoff {                                                                            
462   my ($self) = @_;                                                                      
463                                                                                         
464 =pod                                                                                    
465                                                                                         
466 =head3 logoff                                                                           
467                                                                                         
468 Performs a logout on the remote host. Normally handled in the                           
469 destructor but you could call logout to logout if you wish.                             
470                                                                                         
471 Parameters:                                                                             
472                                                                                         
473 =for html <blockquote>                                                                  
474                                                                                         
475 =over                                                                                   
476                                                                                         
477 =item None                                                                              
478                                                                                         
479 =back                                                                                   
480                                                                                         
481 =for html </blockquote>                                                                 
482                                                                                         
483 Returns:                                                                                
484                                                                                         
485 =for html <blockquote>                                                                  
486                                                                                         
487 =over                                                                                   
488                                                                                         
489 =item Nothing                                                                           
490                                                                                         
491 =back                                                                                   
492                                                                                         
493 =for html </blockquote>                                                                 
494                                                                                         
495 =cut                                                                                    
496                                                                                         
497   $self->{handle}->soft_close;                                                          
498                                                                                         
499   undef $self->{handle};                                                                
500   undef $self->{status};                                                                
501   undef $self->{lines};                                                                 
502                                                                                         
503   return;                                                                               
504 } # logoff                                                                              
505                                                                                         
506 sub new {                                                                               
507   my ($class) = shift;                                                                  
508                                                                                         
509 =pod                                                                                    
510                                                                                         
511 =head3 new (<parms>)                                                                    
512                                                                                         
513 This method instantiates a new Rexec object. Currently only hash style                  
514 parameter passing is supported.                                                         
515                                                                                         
516 Parameters:                                                                             
517                                                                                         
518 =for html <blockquote>                                                                  
519                                                                                         
520 =over                                                                                   
521                                                                                         
522 =item host => <host>:                                                                   
523                                                                                         
524 Specifies the host to connect to. Default: localhost                                    
525                                                                                         
526 =item username => <username>                                                            
527                                                                                         
528 Specifies the username to use if prompted. Default: No username specified.              
529                                                                                         
530 =item password => <password>                                                            
531                                                                                         
532 Specifies the password to use if prompted. Default: No password                         
533 specified. Note passwords must be in cleartext at this                                  
534 time. Specifying them makes you insecure!                                               
535                                                                                         
536 =item prompt => <prompt regex>                                                          
537                                                                                         
538 Specifies a regex describing how to identify a prompt. Default: qr'[#>:$] $'            
539                                                                                         
540 =item protocol => <ssh|rsh|rlogin|telnet>                                               
541                                                                                         
542 Specifies the protocol to use when connecting. Default: Try them all                    
543 starting with ssh.                                                                      
544                                                                                         
545 =item opts => <options>                                                                 
546                                                                                         
547 Additional options for protocol (e.g. -X for ssh and X forwarding)                      
548                                                                                         
549 =item verbose => <0|1>                                                                  
550                                                                                         
551 If true then status messages are echoed to stdout. Default: 0.                          
552                                                                                         
553 =back                                                                                   
554                                                                                         
555 =for html </blockquote>                                                                 
556                                                                                         
557 Returns:                                                                                
558                                                                                         
559 =for html <blockquote>                                                                  
560                                                                                         
561 =over                                                                                   
562                                                                                         
563 =item Rexec object                                                                      
564                                                                                         
565 =back                                                                                   
566                                                                                         
567 =for html </blockquote>                                                                 
568                                                                                         
569 =cut                                                                                    
570                                                                                         
571   my %parms = @_;                                                                       
572                                                                                         
573   my $self = {};                                                                        
574                                                                                         
575   $self->{host}       = $parms{host}       ? $parms{host}       : 'localhost';          
576   $self->{username}   = $parms{username};                                               
577   $self->{password}   = $parms{password};                                               
578   $self->{prompt}     = $parms{prompt}     ? $parms{prompt}     : $DEFAULT_PROMPT;      
579   $self->{protocol}   = $parms{protocol};                                               
580   $self->{verbose}    = $parms{verbose};                                                
581   $self->{shellstyle} = $parms{shellstyle} ? $parms{shellstyle} : 'sh';                 
582   $self->{opts}       = $parms{opts}       ? $parms{opts}       : '';                   
583   $self->{timeout}    = $parms{timeout}    ? $parms{timeout}    : $default_login_timeout;
584                                                                                         
585   if ($self->{shellstyle} ne 'sh' and $self->{shellstyle} ne 'csh') {                   
586     croak 'ERROR: Unknown shell style specified. Must be one of "sh" or "csh"', 1;      
587   } # if                                                                                
588                                                                                         
589   bless ($self, $class);                                                                
590                                                                                         
591   # now login...                                                                        
592   $self->{handle} = $self->login;                                                       
593                                                                                         
594   # Set timeout to $default_exec_timeout                                                
595   $self->{timeout} = $default_exec_timeout;                                             
596                                                                                         
597   return $self->{handle} ? $self : undef;                                               
598 } # new                                                                                 
599                                                                                         
600 sub execute ($$) {                                                                      
601   my ($self, $cmd, $timeout) = @_;                                                      
602                                                                                         
603 =pod                                                                                    
604                                                                                         
605 =head3 exec ($cmd, $timeout)                                                            
606                                                                                         
607 This method executes a command on the remote host returning an array                    
608 of lines that the command produced, if any. Status of the command is                    
609 stored in the object and accessible via the status method.                              
610                                                                                         
611 Parameters:                                                                             
612                                                                                         
613 =for html <blockquote>                                                                  
614                                                                                         
615 =over                                                                                   
616                                                                                         
617 =item $cmd:                                                                             
618                                                                                         
619 Command to execute remotely                                                             
620                                                                                         
621 =item $timeout                                                                          
622                                                                                         
623 Set timeout for this execution. If timeout is 0 then wait forever. If                   
624 you wish to interrupt this then set up a signal handler.                                
625                                                                                         
626 =back                                                                                   
627                                                                                         
628 =for html </blockquote>                                                                 
629                                                                                         
630 Returns:                                                                                
631                                                                                         
632 =for html <blockquote>                                                                  
633                                                                                         
634 =over                                                                                   
635                                                                                         
636 =item @lines                                                                            
637                                                                                         
638 An array of lines from STDOUT of the command. If STDERR is also wanted                  
639 then add STDERR redirection to $cmd. Exit status is not returned by                     
640 retained in the object. Use status method to retrieve it.                               
641                                                                                         
642 =back                                                                                   
643                                                                                         
644 =for html </blockquote>                                                                 
645                                                                                         
646 =cut                                                                                    
647                                                                                         
648   # If timeout is specified for this exec then use it - otherwise                       
649   # use the object's defined timeout.                                                   
650   $timeout = $timeout ? $timeout : $self->{timeout};                                    
651                                                                                         
652   # If timeout is set to 0 then the user wants an indefinite                            
653   # timeout. But Expect wants it to be undefined. So undef it if                        
654   # it's 0. Note this means we do not support Expect's "check it                        
655   # only one time" option.                                                              
656   undef $timeout if $timeout == 0;                                                      
657                                                                                         
658   # If timeout is < 0 then the user wants to run the command in the                     
659   # background and return. We still need to wait as we still may                        
660   # timeout so change $timeout to the $default_exec_timeout in this                     
661   # case and add a "&" to the command if it's not already there.                        
662   # because the user has added a & to the command to run it in the                      
663   if ($timeout && $timeout < 0) {                                                       
664     $timeout = $default_exec_timeout;                                                   
665     $cmd .= "&" if $cmd !~ /&$/;                                                        
666   } # if                                                                                
667                                                                                         
668   # Set status to -2 indicating nothing happened! We should never                       
669   # return -2 (unless a command manages to set $? to -2!)                               
670   $self->{status} = -2;                                                                 
671                                                                                         
672   # Empty lines of any previous command output                                          
673   @lines = ();                                                                          
674                                                                                         
675   # Hopefully we will not see the following in the output string                        
676   my $errno_str = "ReXeCerRoNO=";                                                       
677   my $start_str = "StaRT";                                                              
678                                                                                         
679   my $compound_cmd;                                                                     
680                                                                                         
681   # If cmd ends in a & then it makes no sense to compose a compound                     
682   # command. The original command will be in the background and thus                    
683   # we should not attempt to get a status - there will be none.                         
684   if ($cmd !~ /&$/) {                                                                   
685     $compound_cmd = "echo $start_str; $cmd; echo $errno_str";                           
686     $compound_cmd .= $self->{shellstyle} eq "sh" ? "\$?" : "\$status";                  
687   } else {                                                                              
688     $compound_cmd = $cmd;                                                               
689   } # if                                                                                
690                                                                                         
691   $self->{handle}->send ("$compound_cmd\n");                                            
692                                                                                         
693   $self->{handle}->expect (                                                             
694     $timeout,                                                                           
695                                                                                         
696     [ timeout =>                                                                        
697       sub {                                                                             
698         $self->{status} = -1;                                                           
699       }                                                                                 
700     ],                                                                                  
701                                                                                         
702     [ qr "\n$start_str",                                                                
703       sub {                                                                             
704         exp_continue;                                                                   
705       }                                                                                 
706     ],                                                                                  
707                                                                                         
708     [ qr "\n$errno_str",                                                                
709       sub {                                                                             
710         my ($exp) = @_;                                                                 
711                                                                                         
712         my $before = $exp->before;                                                      
713         my $after  = $exp->after;                                                       
714                                                                                         
715         if ($after =~ /(\d+)/) {                                                        
716           $self->{status} = $1;                                                         
717         } # if                                                                          
718                                                                                         
719         my @output = split /\n/, $before;                                               
720                                                                                         
721         chomp @output;                                                                  
722         chop @output if $output[0] =~ /\r$/;                                            
723                                                                                         
724         foreach (@output) {                                                             
725           next if /^$/;                                                                 
726           last if /$errno_str=/;                                                        
727                                                                                         
728           push @lines, $_;                                                              
729         } # foreach                                                                     
730                                                                                         
731         exp_continue;                                                                   
732       }                                                                                 
733     ],                                                                                  
734                                                                                         
735     [ $self->{prompt},                                                                  
736       sub {                                                                             
737         print 'Hit prompt!' if $debug;                                                  
738       }                                                                                 
739     ],                                                                                  
740   );                                                                                    
741                                                                                         
742   $self->{lines} = \@lines;                                                             
743                                                                                         
744   return @lines;                                                                        
745 } # exec                                                                                
746                                                                                         
747 sub abortCmd (;$) {                                                                     
748   my ($self, $timeout) = @_;                                                            
749                                                                                         
750 =pod                                                                                    
751                                                                                         
752 =head3 abortCmd                                                                         
753                                                                                         
754 Aborts the current command by sending a Control-C (assumed to be the                    
755 interrupt character).                                                                   
756                                                                                         
757 Parameters:                                                                             
758                                                                                         
759 =for html <blockquote>                                                                  
760                                                                                         
761 =over                                                                                   
762                                                                                         
763 =item None                                                                              
764                                                                                         
765 =back                                                                                   
766                                                                                         
767 =for html </blockquote>                                                                 
768                                                                                         
769 Returns:                                                                                
770                                                                                         
771 =for html <blockquote>                                                                  
772                                                                                         
773 =over                                                                                   
774                                                                                         
775 =item $status                                                                           
776                                                                                         
777 1 if abort was successful (we got a command prompt back) or 0 if it                     
778 was not.                                                                                
779                                                                                         
780 =back                                                                                   
781                                                                                         
782 =for html </blockquote>                                                                 
783                                                                                         
784 =cut                                                                                    
785                                                                                         
786   # If timeout is specified for this exec then use it - otherwise                       
787   # use the object's defined timeout.                                                   
788   $timeout = $timeout ? $timeout : $self->{timeout};                                    
789                                                                                         
790   # If timeout is set to 0 then the user wants an indefinite                            
791   # timeout. But Expect wants it to be undefined. So undef it if                        
792   # it's 0. Note this means we do not support Expect's "check it                        
793   # only one time" option.                                                              
794   undef $timeout if $timeout == 0;                                                      
795                                                                                         
796   # Set status to -2 indicating nothing happened! We should never                       
797   # return -2 (unless a command manages to set $? to -2!)                               
798   $self->{status} = -2;                                                                 
799                                                                                         
800   $self->{handle}->send ("\cC");                                                        
801                                                                                         
802   $self->{handle}->expect (                                                             
803     $timeout,                                                                           
804                                                                                         
805     [ timeout =>                                                                        
806       sub {                                                                             
807         $self->{status} = -1;                                                           
808       }                                                                                 
809     ],                                                                                  
810                                                                                         
811     [ $self->{prompt},                                                                  
812       sub {                                                                             
813         print "Hit prompt!" if $debug;                                                  
814       }                                                                                 
815     ],                                                                                  
816   );                                                                                    
817                                                                                         
818   return $self->{status};                                                               
819 } # abortCmd                                                                            
820                                                                                         
821 sub status {                                                                            
822   my ($self) = @_;                                                                      
823                                                                                         
824 =pod                                                                                    
825                                                                                         
826 =head3 status                                                                           
827                                                                                         
828 Returns the status of the last command executed remotely.                               
829                                                                                         
830 Parameters:                                                                             
831                                                                                         
832 =for html <blockquote>                                                                  
833                                                                                         
834 =over                                                                                   
835                                                                                         
836 =item None                                                                              
837                                                                                         
838 =back                                                                                   
839                                                                                         
840 =for html </blockquote>                                                                 
841                                                                                         
842 Returns:                                                                                
843                                                                                         
844 =for html <blockquote>                                                                  
845                                                                                         
846 =over                                                                                   
847                                                                                         
848 =item $status                                                                           
849                                                                                         
850 Last status from exec.                                                                  
851                                                                                         
852 =back                                                                                   
853                                                                                         
854 =for html </blockquote>                                                                 
855                                                                                         
856 =cut                                                                                    
857                                                                                         
858   return $self->{status};                                                               
859 } # status                                                                              
860                                                                                         
861 sub shellstyle {                                                                        
862   my ($self) = @_;                                                                      
863                                                                                         
864 =pod                                                                                    
865                                                                                         
866 =head3 shellstyle                                                                       
867                                                                                         
868 Returns the shellstyle                                                                  
869                                                                                         
870 Parameters:                                                                             
871                                                                                         
872 =for html <blockquote>                                                                  
873                                                                                         
874 =over                                                                                   
875                                                                                         
876 =item None                                                                              
877                                                                                         
878 =back                                                                                   
879                                                                                         
880 =for html </blockquote>                                                                 
881                                                                                         
882 Returns:                                                                                
883                                                                                         
884 =for html <blockquote>                                                                  
885                                                                                         
886 =over                                                                                   
887                                                                                         
888 =item "sh"|"csh"                                                                        
889                                                                                         
890 sh: Bourne or csh: for csh style shells                                                 
891                                                                                         
892 =back                                                                                   
893                                                                                         
894 =for html </blockquote>                                                                 
895                                                                                         
896 =cut                                                                                    
897                                                                                         
898   return $self->{shellstyle};                                                           
899 } # shellstyle                                                                          
900                                                                                         
901 sub lines () {                                                                          
902   my ($self) = @_;                                                                      
903                                                                                         
904 =pod                                                                                    
905                                                                                         
906 =head3 lines                                                                            
907                                                                                         
908 Returns the lines array from the last command called by exec.                           
909                                                                                         
910 Parameters:                                                                             
911                                                                                         
912 =for html <blockquote>                                                                  
913                                                                                         
914 =over                                                                                   
915                                                                                         
916 =item None                                                                              
917                                                                                         
918 =back                                                                                   
919                                                                                         
920 =for html </blockquote>                                                                 
921                                                                                         
922 Returns:                                                                                
923                                                                                         
924 =for html <blockquote>                                                                  
925                                                                                         
926 =over                                                                                   
927                                                                                         
928 =item @lines                                                                            
929                                                                                         
930 An array of lines from the last call to exec.                                           
931                                                                                         
932 =back                                                                                   
933                                                                                         
934 =for html </blockquote>                                                                 
935                                                                                         
936 =cut                                                                                    
937                                                                                         
938   return @{$self->{lines}};                                                             
939 } # lines                                                                               
940                                                                                         
941 sub print_lines () {                                                                    
942   my ($self) = @_;                                                                      
943                                                                                         
944 =pod                                                                                    
945                                                                                         
946 =head3 print_lines                                                                      
947                                                                                         
948 Essentially prints the lines array to stdout                                            
949                                                                                         
950 Parameters:                                                                             
951                                                                                         
952 =for html <blockquote>                                                                  
953                                                                                         
954 =over                                                                                   
955                                                                                         
956 =item None                                                                              
957                                                                                         
958 =back                                                                                   
959                                                                                         
960 =for html </blockquote>                                                                 
961                                                                                         
962 Returns:                                                                                
963                                                                                         
964 =for html <blockquote>                                                                  
965                                                                                         
966 =over                                                                                   
967                                                                                         
968 =item Nothing                                                                           
969                                                                                         
970 =back                                                                                   
971                                                                                         
972 =for html </blockquote>                                                                 
973                                                                                         
974 =cut                                                                                    
975                                                                                         
976   print "$_\n" foreach ($self->lines);                                                  
977                                                                                         
978   return;                                                                               
979 } # print_lines                                                                         
980                                                                                         
981 sub getHost () {                                                                        
982   my ($self) = @_;                                                                      
983                                                                                         
984 =pod                                                                                    
985                                                                                         
986 =head3 host                                                                             
987                                                                                         
988 Returns the host from the object.                                                       
989                                                                                         
990 Parameters:                                                                             
991                                                                                         
992 =for html <blockquote>                                                                  
993                                                                                         
994 =over                                                                                   
995                                                                                         
996 =item None                                                                              
997                                                                                         
998 =back                                                                                   
999                                                                                         
1000 =for html </blockquote>                                                                 
1001                                                                                         
1002 Returns:                                                                                
1003                                                                                         
1004 =for html <blockquote>                                                                  
1005                                                                                         
1006 =over                                                                                   
1007                                                                                         
1008 =item $hostname                                                                         
1009                                                                                         
1010 =back                                                                                   
1011                                                                                         
1012 =for html </blockquote>                                                                 
1013                                                                                         
1014 =cut                                                                                    
1015                                                                                         
1016   return $self->{host};                                                                 
1017 } # getHost                                                                             
1018                                                                                         
1019 sub DESTROY {                                                                           
1020   my ($self) = @_;                                                                      
1021                                                                                         
1022   $self->{handle}->hard_close                                                           
1023     if $self->{handle};                                                                 
1024                                                                                         
1025   return;                                                                               
1026 } # destroy                                                                             
1027                                                                                         
1028 sub getTimeout {                                                                        
1029   my ($self) = @_;                                                                      
1030                                                                                         
1031 =head3 getTimeout                                                                       
1032                                                                                         
1033 Returns the timeout from the object.                                                    
1034                                                                                         
1035 Parameters:                                                                             
1036                                                                                         
1037 =for html <blockquote>                                                                  
1038                                                                                         
1039 =over                                                                                   
1040                                                                                         
1041 =item None                                                                              
1042                                                                                         
1043 =back                                                                                   
1044                                                                                         
1045 =for html </blockquote>                                                                 
1046                                                                                         
1047 Returns:                                                                                
1048                                                                                         
1049 =for html <blockquote>                                                                  
1050                                                                                         
1051 =over                                                                                   
1052                                                                                         
1053 =item $timeout                                                                          
1054                                                                                         
1055 =back                                                                                   
1056                                                                                         
1057 =for html </blockquote>                                                                 
1058                                                                                         
1059 =cut                                                                                    
1060                                                                                         
1061   return $self->{timeout} ? $self->{timeout} : $default_login_timeout;                  
1062 } # getTimeout                                                                          
1063                                                                                         
1064 sub setTimeout ($) {                                                                    
1065   my ($self, $timeout) = @_;                                                            
1066                                                                                         
1067 =pod                                                                                    
1068                                                                                         
1069 =head3 setTimeout ($timeout)                                                            
1070                                                                                         
1071 Sets the timeout value for subsequent execution.                                        
1072                                                                                         
1073 Parameters:                                                                             
1074                                                                                         
1075 =for html <blockquote>                                                                  
1076                                                                                         
1077 =over                                                                                   
1078                                                                                         
1079 =item $timeout                                                                          
1080                                                                                         
1081 New timeout value to set                                                                
1082                                                                                         
1083 =back                                                                                   
1084                                                                                         
1085 =for html </blockquote>                                                                 
1086                                                                                         
1087 Returns:                                                                                
1088                                                                                         
1089 =for html <blockquote>                                                                  
1090                                                                                         
1091 =over                                                                                   
1092                                                                                         
1093 =item $timeout                                                                          
1094                                                                                         
1095 Old timeout value                                                                       
1096                                                                                         
1097 =back                                                                                   
1098                                                                                         
1099 =for html </blockquote>                                                                 
1100                                                                                         
1101 =cut                                                                                    
1102                                                                                         
1103   my $oldTimeout = $self->getTimeout;                                                   
1104   $self->{timeout} = $timeout;                                                          
1105                                                                                         
1106   return $oldTimeout;                                                                   
1107 } # setTimeout                                                                          
1108                                                                                         
1109 1;                                                                                      
1110                                                                                         
1111 =head1 DIAGNOSTICS                                                                      
1112                                                                                         
1113 =head2 Errors                                                                           
1114                                                                                         
1115 If verbose is turned on then connections or failure to connect will be                  
1116 echoed to stdout.                                                                       
1117                                                                                         
1118 =head3 Error text                                                                       
1119                                                                                         
1120   <host> is not responding to <protocol>                                                
1121   Connected to <host> using <protocol> protocol                                         
1122   Unable to connect to <host> using <protocol> protocol                                 
1123                                                                                         
1124 =head2 Warnings                                                                         
1125                                                                                         
1126 Specifying cleartext passwords is not recommended for obvious security concerns.        
1127                                                                                         
1128 =head1 CONFIGURATION AND ENVIRONMENT                                                    
1129                                                                                         
1130 Configuration files and environment variables.                                          
1131                                                                                         
1132 =over                                                                                   
1133                                                                                         
1134 =item None                                                                              
1135                                                                                         
1136 =back                                                                                   
1137                                                                                         
1138 =head1 DEPENDENCIES                                                                     
1139                                                                                         
1140 =head2 Perl Modules                                                                     
1141                                                                                         
1142 =for html <a href="http://search.cpan.org/~rgiersig/Expect-1.21/Expect.pod">Expect</a><b
1143                                                                                         
1144 =head3 ClearSCM Perl Modules                                                            
1145                                                                                         
1146 =for html <p><a href="/php/cvs_man.php?file=lib/Display.pm">Display</a></p>             
1147                                                                                         
1148 =head1 INCOMPATABILITIES                                                                
1149                                                                                         
1150 None yet...                                                                             
1151                                                                                         
1152 =head1 BUGS AND LIMITATIONS                                                             
1153                                                                                         
1154 There are no known bugs in this module.                                                 
1155                                                                                         
1156 Please report problems to Andrew DeFaria <Andrew@ClearSCM.com>.                         
1157                                                                                         
1158 =head1 LICENSE AND COPYRIGHT                                                            
1159                                                                                         
1160 This Perl Module is freely available; you can redistribute it and/or                    
1161 modify it under the terms of the GNU General Public License as                          
1162 published by the Free Software Foundation; either version 2 of the                      
1163 License, or (at your option) any later version.                                         
1164                                                                                         
1165 This Perl Module is distributed in the hope that it will be useful,                     
1166 but WITHOUT ANY WARRANTY; without even the implied warranty of                          
1167 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU                        
1168 General Public License (L<http://www.gnu.org/copyleft/gpl.html>) for more               
1169 details.                                                                                
1170                                                                                         
1171 You should have received a copy of the GNU General Public License                       
1172 along with this Perl Module; if not, write to the Free Software Foundation,             
1173 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.                          
1174 reserved.                                                                               
1175                                                                                         
1176 =cut