App-RemoteCommand
view release on metacpan or search on metacpan
lib/App/RemoteCommand.pm view on Meta::CPAN
my @success = sort grep { $self->{exit}{$_} == 0 } keys $self->{exit}->%*;
my @fail = sort grep { $self->{exit}{$_} != 0 } keys $self->{exit}->%*;
if (!$self->{quiet}) {
print STDERR "\e[32mSUCCESS\e[m $_\n" for @success;
print STDERR "\e[31mFAIL\e[m $_\n" for @fail;
}
return @fail ? 1 : 0;
}
sub show_help ($self) {
open my $fh, '>', \my $out;
Pod::Usage::pod2usage
exitval => 'noexit',
input => $0,
output => $fh,
sections => 'SYNOPSIS|OPTIONS|EXAMPLES',
verbose => 99,
;
$out =~ s/^[ ]{4,6}/ /mg;
$out =~ s/\n$//;
print $out;
}
sub parse_options ($self, @argv) {
my $parser = Getopt::Long::Parser->new(
config => [qw(no_auto_abbrev no_ignore_case)],
);
$parser->getoptionsfromarray(
\@argv,
"c|concurrency=i" => \($self->{concurrency} = 5),
"h|help" => sub (@) { $self->show_help; exit 1 },
"s|script=s" => \($self->{script}),
"v|version" => sub (@) { printf "%s %s\n", __PACKAGE__, __PACKAGE__->VERSION; exit },
"a|ask-sudo-password" => \(my $ask_sudo_password),
"H|host-file=s" => \(my $host_file),
"sudo-password=s" => \($self->{sudo_password}),
"append-hostname!" => \(my $append_hostname = 1),
"append-time!" => \(my $append_time),
"sudo=s" => \($self->{sudo_user}),
"q|quiet" => \($self->{quiet}),
"F=s" => \($self->{configfile}),
) or exit(2);
my $host_arg = $host_file ? undef : shift @argv;
if ($self->{script}) {
$self->{script_arg} = \@argv;
} else {
$self->{command} = \@argv;
}
if (!($self->{command} || [])->@* && !$self->{script}) {
warn "COMMANDS or --script option is required\n";
exit(2);
}
if ($self->{script}) {
my ($tempfh, $tempfile) = File::Temp::tempfile(UNLINK => 1, EXLOCK => 0);
File::Copy::copy($self->{script}, $tempfh)
or die "copy $self->{script} to tempfile: $!";
close $tempfh;
chmod 0755, $tempfile;
$self->{script} = $tempfile;
}
$self->{format} = $self->make_format(
append_hostname => $append_hostname,
append_time => $append_time,
);
if ($ask_sudo_password) {
my $password = prompt $SUDO_PROMPT;
$self->{sudo_password} = $password;
}
$self->{host} = $host_file ? $self->parse_host_file($host_file)
: $self->parse_host_arg($host_arg);
$self;
}
sub cancel ($self, $signal) {
$self->{pending}->@* = ();
for my $ssh ($self->{running}->all) {
$ssh->cancel($signal);
}
}
sub one_tick ($self) {
while ($self->{running}->count < $self->{concurrency} and my $ssh = shift $self->{pending}->@*) {
$self->{running}->add($ssh);
}
my @ready = $self->{select}->can_read(TICK_SECOND);
DEBUG and logger "one tick running %d (watching %d, can_read %d), pending %d",
$self->{running}->count, $self->{select}->count, scalar @ready, scalar $self->{pending}->@*;
if ($self->{select}->count == 0) {
select undef, undef, undef, TICK_SECOND;
} else {
$self->_process($_) for @ready;
}
# -1: there is no child process
# 0: all child process are running
my $pid = waitpid -1, POSIX::WNOHANG;
my $exit = $?;
if ($pid > 0 and my $remove = $self->{select}->remove(pid => $pid)) {
$self->_post_process($remove);
}
for my $ssh ($self->{running}->all) {
my %args = (select => $self->{select}, $pid > 0 ? (pid => $pid, exit => $exit) : ());
my $is_running = $ssh->one_tick(%args);
if (!$is_running) {
$self->{exit}{$ssh->host} = $ssh->exit;
$self->{running}->remove($ssh);
}
}
}
# We close fh explicitly; otherwise it happens that
( run in 0.689 second using v1.01-cache-2.11-cpan-cdf2f3d4e48 )