1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
|
package Net::CLI::Interact::Transport::Role::ConnectCore;
{
$Net::CLI::Interact::Transport::Role::ConnectCore::VERSION = '1.121640';
}
use Moose::Role;
use Net::Telnet ();
sub connect_core {
my $self = shift;
if ($self->use_net_telnet_connection) {
return $self->_via_native(@_);
}
else {
return $self->_via_spawn(@_);
}
}
sub _via_native {
my $self = shift;
my $t = Net::Telnet->new(Cmd_remove_mode => 1);
$t->open(Host => $self->runtime_options)
or confess "failed to open Net::Telnet connection to target device.";
return $t;
}
sub _via_spawn {
my $self = shift;
my $t = Net::Telnet->new(
Binmode => 1,
Cmd_remove_mode => 1,
Telnetmode => 0,
);
$t->fhopen( $self->_spawn_command(@_) )
or confess "failed to spawn connection to target device.";
return $t;
}
# this code is based on that in Expect.pm, and found to be the most reliable.
# minor alterations to use CORE::close and confess, and to reap child.
use FileHandle;
use IO::Pty;
use POSIX qw(WNOHANG);
has 'childpid' => (
is => 'rw',
isa => 'Int',
);
sub REAPER {
# http://www.perlmonks.org/?node_id=10516
my $stiff;
1 while (($stiff = waitpid(-1, &WNOHANG)) > 0);
$SIG{CHLD} = \&REAPER;
}
sub _spawn_command {
my $self = shift;
my @command = @_;
my $pty = IO::Pty->new();
# try to install handler to reap children
$SIG{CHLD} = \&REAPER
if !defined $SIG{CHLD};
# set up pipe to detect childs exec error
pipe(STAT_RDR, STAT_WTR) or confess "Cannot open pipe: $!";
STAT_WTR->autoflush(1);
eval {
fcntl(STAT_WTR, F_SETFD, FD_CLOEXEC);
};
my $pid = fork;
if (! defined ($pid)) {
confess "Cannot fork: $!" if $^W;
return undef;
}
if($pid) { # parent
my $errno;
CORE::close STAT_WTR;
$pty->close_slave();
$pty->set_raw();
# now wait for child exec (eof due to close-on-exit) or exec error
my $errstatus = sysread(STAT_RDR, $errno, 256);
confess "Cannot sync with child: $!" if not defined $errstatus;
CORE::close STAT_RDR;
if ($errstatus) {
$! = $errno+0;
confess "Cannot exec(@command): $!\n" if $^W;
return undef;
}
# store pid for killing if we're in cygwin
$self->childpid( $pid );
}
else { # child
CORE::close STAT_RDR;
$pty->make_slave_controlling_terminal();
my $slv = $pty->slave()
or confess "Cannot get slave: $!";
$slv->set_raw();
CORE::close($pty);
CORE::close(STDIN);
open(STDIN,"<&". $slv->fileno())
or confess "Couldn't reopen STDIN for reading, $!\n";
CORE::close(STDOUT);
open(STDOUT,">&". $slv->fileno())
or confess "Couldn't reopen STDOUT for writing, $!\n";
CORE::close(STDERR);
open(STDERR,">&". $slv->fileno())
or confess "Couldn't reopen STDERR for writing, $!\n";
{ exec(@command) };
print STAT_WTR $!+0;
confess "Cannot exec(@command): $!\n";
}
return $pty;
}
1;
|