package Sprocket::Plugin::TermProxy; use Sprocket qw( AIO -Data::Dumper -POE -POE::Filter::Line -POE::Filter::Stream -POE::Wheel::Run -Term::VT102 ); use base 'Sprocket::Plugin'; use bytes; use strict; use warnings; sub new { shift->SUPER::new( name => 'TermProxy', cols => 80, rows => 24, @_ ); } # --------------------------------------------------------- # server sub local_connected { my ( $self, $server, $con ) = @_; $self->take_connection( $con ); my $x = $con->x; $x->{term} = Term::VT102->new( cols => $self->{cols}, rows => $self->{rows}, ); if ( $> == 0 ) { $con->call( 'exec_wheel' ); } else { $con->time_out( 10 ); $x->{_connecting} = 1; my $out = "Login:"; $x->{term}->process($out); $con->send($out); } } sub local_receive { my $self = shift; my ( $server, $con, $data ) = @_; return unless( defined( $data ) ); my $x = $con->x; if ( $x->{_connecting} ) { #chomp( $data ); $data =~ s/(\x0D\x0A?|\x0A\x0D?)$//; # login if ( $data =~ /^[a-zA-Z0-9_.\- ]+$/ ) { delete $x->{_connecting}; $con->time_out( undef ); $con->call( exec_wheel => $data ); } else { my $out = "Connection closed\n"; $x->{term}->process($out); $con->send($out); $con->close(); } } else { if ( $data && $data =~ /^~~~\s/ ) { for my $row ( 1 .. $self->{rows} ) { my $r = $x->{term}->row_sgrtext( $row )."\r\n"; warn $r; $con->send( $r ); } return; } if ( $x->{_run_wheel} ) { #$x->{term}->process( $data ); $x->{_run_wheel}->put( $data ); } else { $con->close(); } } return; } sub exec_wheel { my ( $self, $server, $con, $login ) = @_; my $wheel = $con->x->{_run_wheel} = POE::Wheel::Run->new( # Set the program to execute, and optionally some parameters. Program => sub { my ( $login, $cols, $rows, $path ) = @_; $sprocket->stop(); @ENV{qw( COLUMNS LINES TERM PATH )} = ( $cols, $rows, 'linux', $path ); if ( $> == 0 ) { exec( '/bin/login' ); } else { exec( '/usr/bin/ssh', '-oPreferredAuthentications=keyboard-interactive,password', '-oNoHostAuthenticationForLocalhost=yes', '-oLogLevel=FATAL', '-F/dev/null', '-l', $login, 'localhost', ); } }, Conduit => 'pty', Winsize => [ $self->{rows}, $self->{cols} ], ProgramArgs => [ $login, $self->{cols}, $self->{rows}, $ENV{PATH}, ], StdoutEvent => $con->event( 'wheel_stdout' ), ErrorEvent => $con->event( 'wheel_error' ), CloseEvent => $con->event( 'wheel_closed' ), StdinFilter => POE::Filter::Stream->new(), StdoutFilter => POE::Filter::Stream->new(), ); my $pid = $con->x->{_pid} = $wheel->PID(); if ( $poe_kernel->can( 'sig_child' ) ) { # handler already in Sprocket $poe_kernel->sig_child( $pid => $con->event( 'wheel_sig_child' ) ); } else { warn "WARNING: This version of POE will leave zombies, upgrade the perl package: POE\n"; # XXX old poe child stuff? } # $con->resume(); return; } sub local_disconnected { my ( $self, $server, $con ) = @_; delete $con->x->{_run_wheel}; return; } sub wheel_stdout { my ( $self, $server, $con, $data ) = @_; $con->x->{term}->process( $data ); $con->send( $data ); return; } sub wheel_error { my ( $self, $server, $con, $errstr, $errnum ) = @_; return unless ( $errnum != 0 ); warn "error: $errstr\n"; # $con->send("ERROR: $errstr"); delete $con->x->{_run_wheel}; delete $con->x->{term}; $con->close(); return; } sub wheel_closed { my ( $self, $server, $con ) = @_; delete $con->x->{_run_wheel}; delete $con->x->{term}; $con->close(); return; } sub wheel_sig_child { my ( $self, $server, $con ) = @_; $poe_kernel->sig_handled(); $con->call( 'wheel_closed' ); } 1;