App-RemoteCommand
view release on metacpan or search on metacpan
lib/App/RemoteCommand/SSH.pm view on Meta::CPAN
package App::RemoteCommand::SSH;
use v5.24;
use warnings;
use experimental qw(lexical_subs signatures);
use App::RemoteCommand::Util qw(DEBUG logger);
use Net::OpenSSH;
use IO::Pty;
sub _ssh ($self, %args) {
my $loglevel = DEBUG ? "error" : "quiet";
Net::OpenSSH->new($args{host},
async => 1,
strict_mode => 0,
timeout => 6,
kill_ssh_on_timeout => 1,
master_setpgrp => 1,
master_opts => [
-o => "ConnectTimeout=5",
-o => "StrictHostKeyChecking=no",
-o => "UserKnownHostsFile=/dev/null",
-o => "LogLevel=$loglevel",
($args{configfile} ? (-F => $args{configfile}) : ()),
],
);
}
use constant STATE_TODO => 1;
use constant STATE_CONNECTING => 2;
use constant STATE_CONNECTED => 3;
use constant STATE_DISCONNECTING => 4;
use constant STATE_DONE => 5;
sub new ($class, %args) {
bless {
%args,
ssh => undef,
cmd => [],
at_exit => undef,
state => STATE_TODO,
exit => -1,
current => undef,
sudo => undef,
}, $class;
}
sub add ($self, $cmd) {
push $self->{cmd}->@*, $cmd;
}
sub at_exit ($self, @args) {
@args ? $self->{at_exit} = shift @args : $self->{at_exit};
}
sub cancel ($self, $signal) {
DEBUG and logger " CANCEL %s, state %s", $self->host, $self->{state};
if ($self->{state} == STATE_TODO) {
$self->{state} == STATE_DONE;
} elsif ($self->{state} == STATE_CONNECTING) {
$self->{cmd}->@* = ();
undef $self->{at_exit};
my $master_pid = $self->{ssh}->get_master_pid;
DEBUG and logger " SEND SIG$signal %s, process group for master pid %d", $self->host, $master_pid;
kill $signal => -$master_pid;
} elsif ($self->{state} == STATE_CONNECTED) {
$self->{cmd}->@* = ();
if ($signal
and $self->{current}
and $self->{current}{type} eq "cmd"
and my $pid = $self->{current}{pid}
) {
DEBUG and logger " SEND SIG$signal %s, pid %s", $self->host, $pid;
kill $signal => $pid;
}
} elsif ($self->{state} == STATE_DISCONNECTING) {
# nop
} elsif ($self->{state} == STATE_DONE) {
( run in 0.444 second using v1.01-cache-2.11-cpan-df04353d9ac )