#!/usr/bin/env perl
use warnings;  
use strict;  

use AnyEvent;  
use AnyEvent::Impl::Perl;  
use AnyEvent::Socket;  
use AnyEvent::Handle;  
  
use MYDan::Agent::Client;
use MYDan::Util::OptConf;
use IO::Socket;
use Term::ReadKey;
use POSIX qw( :sys_wait_h );
use Data::UUID;
use IO::Poll qw( POLLIN POLLHUP POLLOUT POLLERR);
use Term::Size;

$| ++;

=head1 SYNOPSIS

 $0 --host host

    get a shell from remote machine

 $0 --host host --listen 9999

 $0 --host host --listen 9999 [--addr 10.10.10.1]\
      [--user user(default `id -un`)] [--sudo sudoer]

=cut

$MYDan::Util::OptConf::THIS = 'agent';
my $option = MYDan::Util::OptConf->load();
my %o = $option->get( qw( host=s listen=i addr=s user=s sudo=s ) )->dump();
$option->assert( 'host' );

my $cv = AE::cv;  

my $listen = $o{listen};

unless( $listen )
{
    my $scan = `netstat  -tun|awk '{print \$4}'|awk -F: '{print \$2}'`;
    my %open = map{ $_ => 1 }my @open = $scan =~ /(\d+)/g;
    my %port = map{ $_ => 1 }65112 .. 65535;
    ( $listen ) = grep{ ! $open{$_} }keys %port;
}

my $socket = IO::Socket::INET->new (
    LocalPort => $listen,
    Type      => SOCK_STREAM,
    Reuse     => 1,
    Listen    => 1
) or die "listen $listen: $!\n";

$o{user} = `id -un` and chop $o{user}  unless $o{user};

my $uuid = Data::UUID->new->create_str();

my ($cols, $rows) = Term::Size::chars *STDOUT{IO};

my %query = (
    env => +{ TERM => 'linux' },
    code => 'shell',
    argv => [ $o{addr}, $listen, $uuid, $rows, $cols ],
    map{ $_ => $o{$_} }qw( user sudo )
);

my $host = delete $o{host};
my %result = MYDan::Agent::Client->new( 
    $host 
)->run( %o, query => \%query ); 

my $call = $result{$host};
die "call fail:$call\n" 
    unless $call && $call =~ /--- 0\n$/;

my $soc = $socket->accept();
$soc->blocking(0);

my $poll = IO::Poll->new();
$poll->mask( $soc => POLLIN  );
$poll->mask( \*STDIN => POLLIN );

ReadMode(4);

syswrite( $soc, $uuid, 36 );

while ( $poll->handles && $soc ) {
    $poll->poll();
    for my $handle ( $poll->handles( POLLIN ) ) 
    {
        my ( $data, $byte );
        if ( $handle eq $soc )
        {
            if ( $byte = sysread( $soc, $data, 1024 ) ) { syswrite( STDOUT, $data, $byte ); }
            else { $soc->shutdown(2); last; }
        }

        syswrite( $soc, $data, $byte ) 
            if ( $handle eq \*STDIN )
            &&  ( $byte = sysread( STDIN, $data, 1024 ) );
    }
    if( $poll->handles( POLLHUP | POLLERR) )
    {
        $soc->shutdown( 2 );
        last;
    }
}

ReadMode(0);