App-RemoteCommand
view release on metacpan or search on metacpan
lib/App/RemoteCommand.pm view on Meta::CPAN
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}) {
sub ($host, $msg) { "[@{[strftime '%F %T', localtime]}] $msg\n" };
} elsif ($opt{append_hostname}) {
sub ($host, $msg) { "[$host] $msg\n" };
} else {
sub ($host, $msg) { "$msg\n" };
}
}
sub parse_host_arg ($self, $host_arg) {
[ List::Util::uniq string_glob_permute($host_arg) ];
}
sub parse_host_file ($self, $host_file) {
open my $fh, "<", $host_file or die "Cannot open '$host_file': $!\n";
my @host;
while (my $line = <$fh>) {
$line =~ s/^\s+//; $line =~ s/\s+$//;
push @host, string_glob_permute($line) if $line =~ /^[^#\s]/;
}
[ List::Util::uniq @host ];
}
1;
__END__
=encoding utf-8
=for stopwords passphrase
=head1 NAME
App::RemoteCommand - simple remote command launcher via ssh
=head1 SYNOPSIS
$ rcommand [OPTIONS] HOSTS COMMANDS
$ rcommand [OPTIONS] --script SCRIPT HOSTS
$ rcommand [OPTIONS] --host-file FILE COMMANDS
( run in 0.337 second using v1.01-cache-2.11-cpan-e1769b4cff6 )