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 )