7 =head1 NAME $RCSfile: rexec.pl,v $
9 Run arbitrary command on another machine
17 Andrew DeFaria <Andrew@ClearSCM.com>
25 Tue Jan 8 15:57:27 MST 2008
29 $Date: 2008/02/29 15:09:15 $
35 Usage: rexec.pl [-usa|ge] [-h|elp] [-v|erbose] [-d|ebug]
36 [-use|rname <username>] [-p|assword <password>]
38 -m|achines <host1>,<host2>,...
43 -usa|ge: Print this usage
44 -h|elp: Print detailed usage
45 -v|erbose: Verbose mode
46 -d|ebug: Print debug messages
47 -use|rname: User name to login as (Default: $USER - Env: REXEC_USER)
48 -p|assword: Password to use (Default: None - Env: REXEC_PASSWD)
49 -m|achines: Machine(s) to run the command on
50 -l|og: Log output (<machine>.log)
52 <command>: Commands to execute (Enclose multiple commands in quotes)
56 This script will perform and arbitrary command on a set of machines. It uses the
57 Rexec module which utilizes Perl::Expect to attempt a connection using ssh, rsh
58 and finally telnet. Username and password can be suppliec (or set up ssh
59 pre-shared key) to log in. This is especially important when ssh'ing into
60 Windows machines using Cygwin and wanting to use network resources. If you ssh
61 into a Windows box using pre-shared key then Windows will not have your
62 password and it needs it to authenticate your user to determine access to remote
63 file systems. Therefore on Windows machines, do not set up preshared key if you
64 wish to access remotely mounted file systems. Instead supply the username and
65 password (hopefully in a secure manner).
72 use Term::ANSIColor qw(:constants);
73 use POSIX ":sys_wait_h";
75 use lib "$FindBin::Bin/../lib", "$FindBin::Bin/../clearadm/lib";
82 my ($currentHost, $skip, $log);
85 usage => sub { pod2usage },
86 help => sub { pod2usage (-verbose => 2)},
87 verbose => sub { set_verbose },
88 debug => sub { set_debug },
89 username => $ENV{REXEC_USER} ? $ENV{REXEC_USER} : $ENV{USER},
90 password => $ENV{REXEC_PASSWD},
96 display BLUE . "\nInterrupted execution on $currentHost->{host}" . RESET;
98 display_nolf "Executing on " . YELLOW . $currentHost->{host} . RESET . " - "
99 . CYAN . BOLD . "C" . RESET . CYAN . "ontinue" . RESET . " or "
100 . MAGENTA . BOLD . "A" . RESET . MAGENTA . "bort run" . RESET . " ("
101 . CYAN . BOLD . "C" . RESET . "/"
102 . MAGENTA . BOLD . "a" . RESET . ")?";
105 my $answer = ReadKey (0);
108 if ($answer eq "\n") {
114 $answer = lc $answer;
116 if ($answer eq "s") {
118 display "Skipping $currentHost->{host}";
119 } elsif ($answer eq "a") {
120 display RED . "Aborting run". RESET;
123 display "Continuing...";
129 sub connectHost ($) {
133 $log = Logger->new (name => $host) if $opts{log};
136 $currentHost = Rexec->new (
138 username => $opts{username},
139 password => $opts{password},
143 # Problem with creating Rexec object. Log error if logging and return.
144 if ($@ || !$currentHost) {
146 $log->err ("Unable to connect to $host") if $opts{log};
148 display RED . 'ERROR:' . RESET . " Unable to connect";
156 my ($host, $cmd, $prompt) = @_;
160 verbose_nolf "Connecting to machine $host...";
162 display_nolf BOLD . YELLOW . "$host:" . RESET if $opts{verbose};
164 connectHost $host unless $currentHost and $currentHost->{host} eq $host;
166 return (1, ()) unless $currentHost;
168 verbose " connected";
170 display WHITE . UNDERLINE . "$cmd" . RESET if $opts{verbose};
172 @lines = $currentHost->execute ($cmd);
174 verbose "Disconnected from $host";
176 my $status = $currentHost->status;
178 return ($status, @lines);
181 $SIG{INT} = \&Interrupted;
196 $opts{debug} = get_debug if ref $opts{debug} eq 'CODE';
197 $opts{verbose} = get_verbose if ref $opts{verbose} eq 'CODE';
199 my $cmd = join ' ', @ARGV;
201 unless ($opts{machines}) {
202 $opts{machines} = [$ENV{REXEC_HOST}] if $ENV{REXEC_HOST};
205 pod2usage 'Must specify -machines to run on' unless $opts{machines};
209 push @machines, (split /,/, join (',', $_)) for (@{$opts{machines}});
211 $opts{machines} = [@machines];
213 my ($status, @lines);
215 for my $machine (@{$opts{machines}}) {
217 ($status, @lines) = execute $machine, $cmd;
219 display BOLD . YELLOW . "$machine:" . RESET . WHITE . $cmd;
221 error "Execution of $cmd on $machine yielded error $status" if $status;
223 display $_ for @lines;
227 verbose_nolf "Connecting to machine $machine...";
229 connectHost $machine;
232 my $cmdline = CmdLine->new ();
234 $cmdline->set_prompt (BOLD . YELLOW . "$machine:" . RESET . WHITE);
238 $cmd = $cmdline->get();
245 last if $cmd =~ /^\s*(exit|quit)\s*$/i;
246 next if $cmd =~ /^\s*$/;
250 ($status, @lines) = execute $machine, $cmd;
252 error "Execution of $cmd on $machine yielded error $status" if $status;
254 display $_ for @lines;