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";
81 my ($currentHost, $skip, $log);
84 usage => sub { pod2usage },
85 help => sub { pod2usage (-verbose => 2)},
86 verbose => sub { set_verbose },
87 debug => sub { set_debug },
88 username => $ENV{REXEC_USER} ? $ENV{REXEC_USER} : $ENV{USER},
89 password => $ENV{REXEC_PASSWD},
95 display BLUE . "\nInterrupted execution on $currentHost->{host}" . RESET;
97 display_nolf "Executing on " . YELLOW . $currentHost->{host} . RESET . " - "
98 . CYAN . BOLD . "C" . RESET . CYAN . "ontinue" . RESET . " or "
99 . MAGENTA . BOLD . "A" . RESET . MAGENTA . "bort run" . RESET . " ("
100 . CYAN . BOLD . "C" . RESET . "/"
101 . MAGENTA . BOLD . "a" . RESET . ")?";
104 my $answer = ReadKey (0);
107 if ($answer eq "\n") {
113 $answer = lc $answer;
115 if ($answer eq "s") {
117 display "Skipping $currentHost->{host}";
118 } elsif ($answer eq "a") {
119 display RED . "Aborting run". RESET;
122 display "Continuing...";
128 sub connectHost ($) {
132 $log = Logger->new (name => $host) if $opts{log};
135 $currentHost = Rexec->new (
137 username => $opts{username},
138 password => $opts{password},
142 # Problem with creating Rexec object. Log error if logging and return.
143 if ($@ || !$currentHost) {
145 $log->err ("Unable to connect to $host") if $opts{log};
147 display RED . 'ERROR:' . RESET . " Unable to connect";
155 my ($host, $cmd, $prompt) = @_;
159 verbose_nolf "Connecting to machine $host...";
161 display_nolf BOLD . YELLOW . "$host:" . RESET if $opts{verbose};
163 connectHost $host unless $currentHost and $currentHost->{host} eq $host;
165 return (1, ()) unless $currentHost;
167 verbose " connected";
169 display WHITE . UNDERLINE . "$cmd" . RESET if $opts{verbose};
171 @lines = $currentHost->execute ($cmd);
173 verbose "Disconnected from $host";
175 my $status = $currentHost->status;
177 return ($status, @lines);
180 $SIG{INT} = \&Interrupted;
195 $opts{debug} = get_debug if ref $opts{debug} eq 'CODE';
196 $opts{verbose} = get_verbose if ref $opts{verbose} eq 'CODE';
198 my $cmd = join ' ', @ARGV;
200 unless ($opts{machines}) {
201 $opts{machines} = [$ENV{REXEC_HOST}] if $ENV{REXEC_HOST};
204 pod2usage 'Must specify -machines to run on' unless $opts{machines};
208 push @machines, (split /,/, join (',', $_)) for (@{$opts{machines}});
210 $opts{machines} = [@machines];
212 my ($status, @lines);
214 for my $machine (@{$opts{machines}}) {
216 ($status, @lines) = execute $machine, $cmd;
218 display BOLD . YELLOW . "$machine:" . RESET . WHITE . $cmd;
220 error "Execution of $cmd on $machine yielded error $status" if $status;
222 display $_ for @lines;
226 verbose_nolf "Connecting to machine $machine...";
228 connectHost $machine;
232 display_nolf BOLD . YELLOW . "$machine:" . RESET . WHITE;
241 last if $cmd =~ /^\s*(exit|quit)\s*$/i;
242 next if $cmd =~ /^\s*$/;
246 ($status, @lines) = execute $machine, $cmd;
248 error "Execution of $cmd on $machine yielded error $status" if $status;
250 display $_ for @lines;