Fixed up Rexec.pm. Handles my prompt better. Also sets prompt to '@@@' so that it...
[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 ssh {
164   my ($self) = shift;
165
166   my ($logged_in, $timedout, $password_attempts) = 0;
167
168   $self->{protocol} = 'ssh';
169
170   my $user = $self->{username} ? "$self->{username}\@" : '';
171
172   my $remote = Expect->new ("ssh $self->{opts} $user$self->{host}");
173
174   return unless $remote;
175
176   $remote->log_user ($debug);
177
178   $remote->expect (
179     $self->{timeout},
180
181     # If password is prompted for, and if one has been specified, then
182     # use it
183     [ qr "[P|p]assword: $",
184       sub {
185         # If we already supplied the password then it must not have
186         # worked so this protocol is no good.
187         return if $password_attempts;
188
189         my $exp = shift;
190
191         # If we're being prompted for password and there is no
192         # password to supply then there is nothing much we can do but
193         # return undef since we can't get in with this protocol
194         return unless $self->{password};
195
196         $exp->send ("$self->{password}\n") if $self->{password};
197         $password_attempts++;
198
199         exp_continue;
200       }
201     ],
202
203     # Discard lines that begin with "ssh:" (like "ssh: <host>: not
204     # found")
205     [ qr'\nssh: ',
206       sub {
207         return;
208       }
209     ],
210
211     # If we find a prompt then everything's good
212     [ $self->{prompt},
213       sub {
214         $logged_in = 1;
215       }
216     ],
217
218     # Of course we may time out...
219     [ timeout =>
220       sub {
221         $timedout = 1;
222       }
223     ],
224   );
225
226   if ($logged_in) {
227     # It's always hard to find the prompt. So let's make a distintive one
228     $self->{prompt} = '@@@';
229     $self->{handle} = $remote;
230
231     if ($self->{shellstyle} eq 'sh') {
232       $self->execute ('PS1=@@@');
233     } else {
234       $self->execute ('set prompt=@@@');
235     } # if
236
237     return $remote;
238   } elsif ($timedout) {
239     carp "WARNING: $self->{host} is not responding to $self->{protocol} protocol";
240     undef $remote;
241     return;
242   } else {
243     carp "WARNING: Unable to connect to $self->{host} using $self->{protocol} protocol";
244     return;
245   } # if
246 } # ssh
247
248 sub rlogin {
249   my ($self) = shift;
250
251   my ($logged_in, $timedout, $password_attempts) = 0;
252
253   $self->{protocol} = "rlogin";
254
255   my $user = $self->{username} ? "-l $self->{username}" : "";
256
257   my $remote = Expect->new ("rsh $user $self->{host}");
258
259   return unless $remote;
260
261   $remote->log_user ($debug);
262
263   $remote->expect (
264     $self->{timeout},
265
266     # If password is prompted for, and if one has been specified, then
267     # use it
268     [ qr "[P|p]assword: $",
269       sub {
270         # If we already supplied the password then it must not have
271         # worked so this protocol is no good.
272         return if $password_attempts;
273
274         my $exp = shift;
275
276         # If we're being prompted for password and there is no
277         # password to supply then there is nothing much we can do but
278         # return undef since we can't get in with this protocol
279         return unless $self->{password};
280
281         $exp->send ("$self->{password}\n");
282         $password_attempts++;
283
284         exp_continue;
285       }
286     ],
287
288     # HACK! rlogin may return "<host>: unknown host" which clashes
289     # with some prompts (OK it clashes with my prompt...)
290     [ ": unknown host",
291       sub {
292         return;
293       }
294     ],
295
296     # If we find a prompt then everything's good
297     [ $self->{prompt},
298       sub {
299         $logged_in = 1;
300       }
301     ],
302
303     # Of course we may time out...
304     [ timeout =>
305       sub {
306         $timedout = 1;
307       }
308     ],
309   );
310
311   if ($logged_in) {
312     # It's always hard to find the prompt. So let's make a distintive one
313     $self->{prompt} = '@@@';
314     $self->{handle} = $remote;
315
316     if ($self->{shellstyle} eq 'sh') {
317       $self->execute ('PS1=@@@');
318     } else {
319       $self->execute ('set prompt=@@@');
320     } # if
321
322     return $remote;
323   } elsif ($timedout) {
324     carp "WARNING: $self->{host} is not responding to $self->{protocol} protocol";
325     undef $remote;
326     return;
327   } else {
328     carp "WARNING: Unable to connect to $self->{host} using $self->{protocol} protocol";
329     return;
330   } # if
331 } # rlogin
332
333 sub telnet {
334   my ($self) = shift;
335
336   my ($logged_in, $timedout, $password_attempts) = 0;
337
338   $self->{protocol} = "telnet";
339
340   my $remote = Expect->new ("telnet $self->{host}");
341
342   return unless $remote;
343
344   $remote->log_user ($debug);
345
346   $remote->expect (
347     $self->{timeout},
348
349     # If login is prompted for, and if what has been specified, then
350     # use it
351     [ qr "login: $",
352       sub {
353         my $exp = shift;
354
355         # If we're being prompted for username and there is no
356         # username to supply then there is nothing much we can do but
357         # return undef since we can't get in with this protocol
358         return unless $self->{username};
359
360         $exp->send ("$self->{username}\n");
361         exp_continue;
362       }
363     ],
364
365     # If password is prompted for, and if one has been specified, then
366     # use it
367     [ qr "[P|p]assword: $",
368       sub {
369         # If we already supplied the password then it must not have
370         # worked so this protocol is no good.
371         return if $password_attempts;
372
373         my $exp = shift;
374
375         # If we're being prompted for password and there is no
376         # password to supply then there is nothing much we can do but
377         # return undef since we can't get in with this protocol
378         return unless $self->{password};
379
380         $exp->send ("$self->{password}\n");
381         $password_attempts++;
382
383         exp_continue;
384       }
385     ],
386
387     # HACK! rlogin may return "<host>: Unknown host" which clashes
388     # with some prompts (OK it clashes with my prompt...)
389     [ ": Unknown host",
390       sub {
391         return;
392       }
393     ],
394
395     # If we find a prompt then everything's good
396     [ $self->{prompt},
397       sub {
398         $logged_in = 1;
399       }
400     ],
401
402     # Of course we may time out...
403     [ timeout =>
404       sub {
405         $timedout = 1;
406       }
407     ],
408   );
409
410   if ($logged_in) {
411     # It's always hard to find the prompt. So let's make a distintive one
412     $self->{prompt} = '@@@';
413     $self->{handle} = $remote;
414
415     if ($self->{shellstyle} eq 'sh') {
416       $self->execute ('PS1=@@@');
417     } else {
418       $self->execute ('set prompt=@@@');
419     } # if
420
421     return $remote;
422   } elsif ($timedout) {
423     carp "WARNING: $self->{host} is not responding to $self->{protocol} protocol";
424     undef $remote;
425     return;
426   } else {
427     carp "WARNING: Unable to connect to $self->{host} using $self->{protocol} protocol";
428     return;
429   } # if
430 } # telnet
431
432 sub login () {
433   my ($self) = shift;
434
435 =pod
436
437 =head2 login
438
439 Performs a login on the remote host. Normally this is done during
440 construction but this method allows you to login, say again, as maybe
441 another user...
442
443 Parameters:
444
445 =for html <blockquote>
446
447 =over
448
449 =item None
450
451 =back
452
453 =for html </blockquote>
454
455 Returns:
456
457 =for html <blockquote>
458
459 =over
460
461 =item Nothing
462
463 =back
464
465 =for html </blockquote>
466
467 =cut
468
469   # Close any prior opened sessions
470   $self->logoff if ($self->{handle});
471
472   my $remote;
473
474   if ($self->{protocol}) {
475     if ($self->{protocol} eq "ssh") {
476       return $self->ssh;
477     } elsif ($self->{protocol} eq "rsh" or $self->{protocol} eq "rlogin") {
478       return $self->rlogin;
479     } elsif ($self->{protocol} eq "telnet") {
480       return $self->telnet;
481     } else {
482       croak "ERROR: Invalid protocol $self->{protocol} specified", 1;
483     } # if
484   } else {
485     return $remote if $remote = $self->ssh;
486     return $remote if $remote = $self->rlogin;
487     return $self->telnet;
488   } # if
489
490   return;
491 } # login
492
493 sub logoff {
494   my ($self) = shift;
495
496 =pod
497
498 =head3 logoff
499
500 Performs a logout on the remote host. Normally handled in the
501 destructor but you could call logout to logout if you wish.
502
503 Parameters:
504
505 =for html <blockquote>
506
507 =over
508
509 =item None
510
511 =back
512
513 =for html </blockquote>
514
515 Returns:
516
517 =for html <blockquote>
518
519 =over
520
521 =item Nothing
522
523 =back
524
525 =for html </blockquote>
526
527 =cut
528
529   $self->{handle}->soft_close;
530
531   undef $self->{handle};
532   undef $self->{status};
533   undef $self->{lines};
534
535   return;
536 } # logoff
537
538 sub new {
539   my ($class) = shift;
540
541 =pod
542
543 =head3 new (<parms>)
544
545 This method instantiates a new Rexec object. Currently only hash style
546 parameter passing is supported.
547
548 Parameters:
549
550 =for html <blockquote>
551
552 =over
553
554 =item host => <host>:
555
556 Specifies the host to connect to. Default: localhost
557
558 =item username => <username>
559
560 Specifies the username to use if prompted. Default: No username specified.
561
562 =item password => <password>
563
564 Specifies the password to use if prompted. Default: No password
565 specified. Note passwords must be in cleartext at this
566 time. Specifying them makes you insecure!
567
568 =item prompt => <prompt regex>
569
570 Specifies a regex describing how to identify a prompt. Default: 
571 qr'[#>:$](\s*|\e.+)$'
572
573 =item protocol => <ssh|rsh|rlogin|telnet>
574
575 Specifies the protocol to use when connecting. Default: Try them all
576 starting with ssh.
577
578 =item opts => <options>
579
580 Additional options for protocol (e.g. -X for ssh and X forwarding)
581
582 =item verbose => <0|1>
583
584 If true then status messages are echoed to stdout. Default: 0.
585
586 =back
587
588 =for html </blockquote>
589
590 Returns:
591
592 =for html <blockquote>
593
594 =over
595
596 =item Rexec object
597
598 =back
599
600 =for html </blockquote>
601
602 =cut
603
604   my %parms = @_;
605
606   my $self = {};
607
608   $self->{host}       = $parms{host}       ? $parms{host}       : 'localhost';
609   $self->{username}   = $parms{username};
610   $self->{password}   = $parms{password};
611   $self->{prompt}     = $parms{prompt}     ? $parms{prompt}     : $DEFAULT_PROMPT;
612   $self->{protocol}   = $parms{protocol};
613   $self->{verbose}    = $parms{verbose};
614   $self->{shellstyle} = $parms{shellstyle} ? $parms{shellstyle} : 'sh';
615   $self->{opts}       = $parms{opts}       ? $parms{opts}       : '';
616   $self->{timeout}    = $parms{timeout}    ? $parms{timeout}    : $default_login_timeout;
617
618   if ($self->{shellstyle} ne 'sh' and $self->{shellstyle} ne 'csh') {
619     croak 'ERROR: Unknown shell style specified. Must be one of "sh" or "csh"',
620   } # if
621
622   bless ($self, $class);
623
624   # now login...
625   $self->{handle} = $self->login;
626
627   # Set timeout to $default_exec_timeout
628   $self->{timeout} = $default_exec_timeout;
629
630   return $self->{handle} ? $self : undef;
631 } # new
632
633 sub execute ($$) {
634   my ($self, $cmd, $timeout) = @_;
635
636 =pod
637
638 =head3 exec ($cmd, $timeout)
639
640 This method executes a command on the remote host returning an array
641 of lines that the command produced, if any. Status of the command is
642 stored in the object and accessible via the status method.
643
644 Parameters:
645
646 =for html <blockquote>
647
648 =over
649
650 =item $cmd:
651
652 Command to execute remotely
653
654 =item $timeout
655
656 Set timeout for this execution. If timeout is 0 then wait forever. If
657 you wish to interrupt this then set up a signal handler.
658
659 =back
660
661 =for html </blockquote>
662
663 Returns:
664
665 =for html <blockquote>
666
667 =over
668
669 =item @lines
670
671 An array of lines from STDOUT of the command. If STDERR is also wanted
672 then add STDERR redirection to $cmd. Exit status is not returned by
673 retained in the object. Use status method to retrieve it.
674
675 =back
676
677 =for html </blockquote>
678
679 =cut
680
681   # If timeout is specified for this exec then use it - otherwise
682   # use the object's defined timeout.
683   $timeout = $timeout ? $timeout : $self->{timeout};
684
685   # If timeout is set to 0 then the user wants an indefinite
686   # timeout. But Expect wants it to be undefined. So undef it if
687   # it's 0. Note this means we do not support Expect's "check it
688   # only one time" option.
689   undef $timeout if $timeout == 0;
690
691   # If timeout is < 0 then the user wants to run the command in the
692   # background and return. We still need to wait as we still may
693   # timeout so change $timeout to the $default_exec_timeout in this
694   # case and add a "&" to the command if it's not already there.
695   # because the user has added a & to the command to run it in the
696   if ($timeout && $timeout < 0) {
697     $timeout = $default_exec_timeout;
698     $cmd .= "&" if $cmd !~ /&$/;
699   } # if
700
701   # Set status to -2 indicating nothing happened! We should never
702   # return -2 (unless a command manages to set $? to -2!)
703   $self->{status} = -2;
704
705   # Empty lines of any previous command output
706   @lines = ();
707
708   # Hopefully we will not see the following in the output string
709   my $errno_str = "ReXeCerRoNO=";
710   my $start_str = "StaRT";
711
712   my $compound_cmd;
713
714   # If cmd ends in a & then it makes no sense to compose a compound
715   # command. The original command will be in the background and thus
716   # we should not attempt to get a status - there will be none.
717   if ($cmd !~ /&$/) {
718     $compound_cmd = "echo $start_str; $cmd; echo $errno_str";
719     $compound_cmd .= $self->{shellstyle} eq "sh" ? "\$?" : "\$status";
720   } else {
721     $compound_cmd = $cmd;
722   } # if
723
724   $self->{handle}->send ("$compound_cmd\n");
725
726   $self->{handle}->expect (
727     $timeout,
728
729     [ timeout =>
730       sub {
731         $self->{status} = -1;
732       }
733     ],
734
735     [ qr "\n$start_str",
736       sub {
737         exp_continue;
738       }
739     ],
740
741     [ qr "\n$errno_str",
742       sub {
743         my ($exp) = @_;
744
745         my $before = $exp->before;
746         my $after  = $exp->after;
747
748         if ($after =~ /(\d+)/) {
749           $self->{status} = $1;
750         } # if
751
752         my @output = split /\n/, $before;
753
754         chomp @output;
755         chop @output if $output[0] =~ /\r$/;
756
757         foreach (@output) {
758           next if /^$/;
759           last if /$errno_str=/;
760
761           push @lines, $_;
762         } # foreach
763
764         exp_continue;
765       }
766     ],
767
768     [ $self->{prompt},
769       sub {
770         print 'Hit prompt!' if $debug;
771       }
772     ],
773   );
774
775   $self->{lines} = \@lines;
776
777   return @lines;
778 } # exec
779
780 sub abortCmd (;$) {
781   my ($self, $timeout) = @_;
782
783 =pod
784
785 =head3 abortCmd
786
787 Aborts the current command by sending a Control-C (assumed to be the
788 interrupt character).
789
790 Parameters:
791
792 =for html <blockquote>
793
794 =over
795
796 =item None
797
798 =back
799
800 =for html </blockquote>
801
802 Returns:
803
804 =for html <blockquote>
805
806 =over
807
808 =item $status
809
810 1 if abort was successful (we got a command prompt back) or 0 if it
811 was not.
812
813 =back
814
815 =for html </blockquote>
816
817 =cut
818
819   # If timeout is specified for this exec then use it - otherwise
820   # use the object's defined timeout.
821   $timeout = $timeout ? $timeout : $self->{timeout};
822
823   # If timeout is set to 0 then the user wants an indefinite
824   # timeout. But Expect wants it to be undefined. So undef it if
825   # it's 0. Note this means we do not support Expect's "check it
826   # only one time" option.
827   undef $timeout if $timeout == 0;
828
829   # Set status to -2 indicating nothing happened! We should never
830   # return -2 (unless a command manages to set $? to -2!)
831   $self->{status} = -2;
832
833   $self->{handle}->send ("\cC");
834
835   $self->{handle}->expect (
836     $timeout,
837
838     [ timeout =>
839       sub {
840         $self->{status} = -1;
841       }
842     ],
843
844     [ $self->{prompt},
845       sub {
846         print "Hit prompt!" if $debug;
847       }
848     ],
849   );
850
851   return $self->{status};
852 } # abortCmd
853
854 sub status {
855   my ($self) = @_;
856
857 =pod
858
859 =head3 status
860
861 Returns the status of the last command executed remotely.
862
863 Parameters:
864
865 =for html <blockquote>
866
867 =over
868
869 =item None
870
871 =back
872
873 =for html </blockquote>
874
875 Returns:
876
877 =for html <blockquote>
878
879 =over
880
881 =item $status
882
883 Last status from exec.
884
885 =back
886
887 =for html </blockquote>
888
889 =cut
890
891   return $self->{status};
892 } # status
893
894 sub shellstyle {
895   my ($self) = @_;
896
897 =pod
898
899 =head3 shellstyle
900
901 Returns the shellstyle
902
903 Parameters:
904
905 =for html <blockquote>
906
907 =over
908
909 =item None
910
911 =back
912
913 =for html </blockquote>
914
915 Returns:
916
917 =for html <blockquote>
918
919 =over
920
921 =item "sh"|"csh"
922
923 sh: Bourne or csh: for csh style shells
924
925 =back
926
927 =for html </blockquote>
928
929 =cut
930
931   return $self->{shellstyle};
932 } # shellstyle
933
934 sub lines () {
935   my ($self) = @_;
936
937 =pod
938
939 =head3 lines
940
941 Returns the lines array from the last command called by exec.
942
943 Parameters:
944
945 =for html <blockquote>
946
947 =over
948
949 =item None
950
951 =back
952
953 =for html </blockquote>
954
955 Returns:
956
957 =for html <blockquote>
958
959 =over
960
961 =item @lines
962
963 An array of lines from the last call to exec.
964
965 =back
966
967 =for html </blockquote>
968
969 =cut
970
971   return @{$self->{lines}};
972 } # lines
973
974 sub print_lines () {
975   my ($self) = @_;
976
977 =pod
978
979 =head3 print_lines
980
981 Essentially prints the lines array to stdout
982
983 Parameters:
984
985 =for html <blockquote>
986
987 =over
988
989 =item None
990
991 =back
992
993 =for html </blockquote>
994
995 Returns:
996
997 =for html <blockquote>
998
999 =over
1000
1001 =item Nothing
1002
1003 =back
1004
1005 =for html </blockquote>
1006
1007 =cut
1008
1009   print "$_\n" foreach ($self->lines);
1010
1011   return;
1012 } # print_lines
1013
1014 sub getHost () {
1015   my ($self) = @_;
1016
1017 =pod
1018
1019 =head3 host
1020
1021 Returns the host from the object.
1022
1023 Parameters:
1024
1025 =for html <blockquote>
1026
1027 =over
1028
1029 =item None
1030
1031 =back
1032
1033 =for html </blockquote>
1034
1035 Returns:
1036
1037 =for html <blockquote>
1038
1039 =over
1040
1041 =item $hostname
1042
1043 =back
1044
1045 =for html </blockquote>
1046
1047 =cut
1048
1049   return $self->{host};
1050 } # getHost
1051
1052 sub DESTROY {
1053   my ($self) = @_;
1054
1055   $self->{handle}->hard_close
1056     if $self->{handle};
1057
1058   return;
1059 } # destroy
1060
1061 sub getTimeout {
1062   my ($self) = @_;
1063
1064 =head3 getTimeout
1065
1066 Returns the timeout from the object.
1067
1068 Parameters:
1069
1070 =for html <blockquote>
1071
1072 =over
1073
1074 =item None
1075
1076 =back
1077
1078 =for html </blockquote>
1079
1080 Returns:
1081
1082 =for html <blockquote>
1083
1084 =over
1085
1086 =item $timeout
1087
1088 =back
1089
1090 =for html </blockquote>
1091
1092 =cut
1093
1094   return $self->{timeout} ? $self->{timeout} : $default_login_timeout;
1095 } # getTimeout
1096
1097 sub setTimeout ($) {
1098   my ($self, $timeout) = @_;
1099
1100 =pod
1101
1102 =head3 setTimeout ($timeout)
1103
1104 Sets the timeout value for subsequent execution.
1105
1106 Parameters:
1107
1108 =for html <blockquote>
1109
1110 =over
1111
1112 =item $timeout
1113
1114 New timeout value to set
1115
1116 =back
1117
1118 =for html </blockquote>
1119
1120 Returns:
1121
1122 =for html <blockquote>
1123
1124 =over
1125
1126 =item $timeout
1127
1128 Old timeout value
1129
1130 =back
1131
1132 =for html </blockquote>
1133
1134 =cut
1135
1136   my $oldTimeout = $self->getTimeout;
1137   $self->{timeout} = $timeout;
1138
1139   return $oldTimeout;
1140 } # setTimeout
1141
1142 1;
1143
1144 =head1 DIAGNOSTICS
1145
1146 =head2 Errors
1147
1148 If verbose is turned on then connections or failure to connect will be
1149 echoed to stdout.
1150
1151 =head3 Error text
1152
1153   <host> is not responding to <protocol>
1154   Connected to <host> using <protocol> protocol
1155   Unable to connect to <host> using <protocol> protocol
1156
1157 =head2 Warnings
1158
1159 Specifying cleartext passwords is not recommended for obvious security concerns.
1160
1161 =head1 CONFIGURATION AND ENVIRONMENT
1162
1163 Configuration files and environment variables.
1164
1165 =over
1166
1167 =item None
1168
1169 =back
1170
1171 =head1 DEPENDENCIES
1172
1173 =head2 Perl Modules
1174
1175 =for html <a href="http://search.cpan.org/~rgiersig/Expect-1.21/Expect.pod">Expect</a>
1176
1177 =head3 ClearSCM Perl Modules
1178
1179 =for html <p><a href="/php/scm_man.php?file=lib/Display.pm">Display</a></p>
1180
1181 =head1 INCOMPATABILITIES
1182
1183 None yet...
1184
1185 =head1 BUGS AND LIMITATIONS
1186
1187 There are no known bugs in this module.
1188
1189 Please report problems to Andrew DeFaria <Andrew@ClearSCM.com>.
1190
1191 =head1 LICENSE AND COPYRIGHT
1192
1193 This Perl Module is freely available; you can redistribute it and/or
1194 modify it under the terms of the GNU General Public License as
1195 published by the Free Software Foundation; either version 2 of the
1196 License, or (at your option) any later version.
1197
1198 This Perl Module is distributed in the hope that it will be useful,
1199 but WITHOUT ANY WARRANTY; without even the implied warranty of
1200 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1201 General Public License (L<http://www.gnu.org/copyleft/gpl.html>) for more
1202 details.
1203
1204 You should have received a copy of the GNU General Public License
1205 along with this Perl Module; if not, write to the Free Software Foundation,
1206 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
1207 reserved.
1208
1209 =cut