App-RemoteCommand

 view release on metacpan or  search on metacpan

lib/App/RemoteCommand.pm  view on Meta::CPAN

        $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
# perl warns "unnable to close filehandle properly: Input/output error" under ssh proxy
sub _process ($self, $ready) {
    my ($fh, $pid, $host, $buffer) = @{$ready}{qw(fh pid host buffer)};
    my $len = sysread $fh, my $buf, 64*1024;
    my ($errno, $errmsg) = (0+$!, "$!");
    DEBUG and logger " READ %s, pid %d, len %s, err: %s",
        $host, $pid, defined $len ? $len : 'undef', $errmsg || "N/A";
    if ($len) {
        if (my @line = $buffer->add($buf)->get) {
            print $self->{format}->($host, $_) for @line;
            if ($ready->{sudo} and @line == 1 and $line[0] eq $SUDO_FAIL) {
                $self->{select}->remove(fh => $fh);
                close $fh;
                return;
            }
        }

        if ($buffer->raw eq $SUDO_PROMPT) {
            $ready->{sudo}++;
            my ($line) = $buffer->get(1);
            print $self->{format}->($host, $line);
            if (my $sudo_password = $self->{sudo_password}) {
                syswrite $fh, "$sudo_password\n";
            } else {
                my $err = "have to provide sudo passowrd first, try again with --ask-sudo-password option.";
                print $self->{format}->($host, $err);
                $self->{select}->remove(fh => $fh);
                close $fh;
            }
        }
    } elsif (!defined $len) {
        if ($errno != Errno::EIO) { # this happens when use ssh proxy, so skip
            print $self->{format}->($host, "sysread $errmsg");
        }
    } else {
        my @line = $buffer->get(1);
        print $self->{format}->($host, $_) for @line;
        $self->{select}->remove(fh => $fh);
        close $fh;
    }
}

sub _post_process ($self, $ready) {
    my ($fh, $pid, $host, $buffer) = @{$ready}{qw(fh pid host buffer)};
    if ($fh) {
        # XXX: We use select() here; otherwise it happens that
        # <$fh> is blocked under ssh proxy
        my $select = IO::Select->new($fh);
        while ($select->can_read(TICK_SECOND)) {
            my $len = sysread $fh, my $buf, 64*1024;
            DEBUG and logger " POST READ %s, pid %d, len %s", $host, $pid, defined $len ? $len : 'undef';
            if (defined $len && $len > 0) {
                $buffer->add($buf);
            } else {
                last;
            }
        }
        my @line = $buffer->get(1);
        print $self->{format}->($host, $_) for @line;
        close $fh;
    }
}

sub register ($self) {

    my @prefix = ("env", "SUDO_PROMPT=$SUDO_PROMPT");
    push @prefix, "sudo", "-u", $self->{sudo_user} if $self->{sudo_user};

    my (@ssh_cmd, $ssh_at_exit);
    my @command;
    if (my $script = $self->{script}) {
        my $name = sprintf "/tmp/%s.%d.%d", $SCRIPT, time, rand(10_000);
        push @ssh_cmd, sub ($ssh) {
            my $pid = $ssh->scp_put({async => 1, copy_attrs => 1}, $script, $name);
            return ($pid, undef);
        };
        $ssh_at_exit = sub ($ssh) {
            my $pid = $ssh->system({async => 1}, "rm", "-f", $name);
            return ($pid, undef);
        };
        @command = (@prefix, $name, $self->{script_arg}->@*);
    } else {
        my $escape = qr{[^a-zA-Z0-9/_:%\.-]};
        @command = (
            @prefix,
            ($self->{command}->@* == 1 && $self->{command}[0] =~ $escape ? ("bash", "-c") : ()),
            $self->{command}->@*,
        );
    }
    DEBUG and logger "execute %s", join(" ", map { qq('$_') } @command);
    push @ssh_cmd, sub ($ssh) {
        my ($fh, $pid) = $ssh->open2pty(@command);
        return ($pid, $fh);
    };

    for my $host ($self->{host}->@*) {
        my $ssh = App::RemoteCommand::SSH->new(host => $host, configfile => $self->{configfile});
        $ssh->add($_) for @ssh_cmd;
        $ssh->at_exit($ssh_at_exit) if $ssh_at_exit;
        push $self->{pending}->@*, $ssh;
    }
}

sub make_format ($self, %opt) {
    if ($opt{append_time} && $opt{append_hostname}) {
        sub ($host, $msg) { "[@{[strftime '%F %T', localtime]}][$host] $msg\n" };
    } elsif ($opt{append_time}) {



( run in 0.471 second using v1.01-cache-2.11-cpan-e1769b4cff6 )