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