Skip Menu |

This queue is for tickets about the Net-SSH-Perl CPAN distribution.

Report information
The Basics
Id: 64517
Status: resolved
Priority: 0/
Queue: Net-SSH-Perl

People
Owner: schwigon [...] cpan.org
Requestors: de [...] lusion.org
Cc:
AdminCc:

Bug Information
Severity: Important
Broken in: 1.34
Fixed in: (no value)



Subject: Issues with PTY support in SSH2
Currently, the use_pty support for SSH2 is disabled. I tried enabling it but found that programs that read from STDIN often hang attempting to read after consuming all input. I've put some work into fixing that. My investigation indicates that the issue is due to the way that PTYs are handled on the server side. The child process on the server side never sees an EOF condition. When there is no PTY, the SSH server closes the STDIN file descriptor, which causes EOF to return. Since STDIN and STDOUT share a file descriptor when a PTY is allocated, the file handle cannot be closed and the read blocks. I believe that appending an 0x04 or two is actually the right thing to do, based on _Advanced UNIX Programming_, section 4.2 (page 204): ================================= The user can end a line in one of two ways. Most often, a newline character is the terminator. The return key is pressed, but the device driver translates the return (octal 15) to a newline (octal 12). Alternatively, the user can generate an end-offile (EOF) character by pressing Ctrl-d. In this case the line is made available to read as is, without a newline terminator. One special case is important: If the user generates an EOF at the start of the line, read will return with a zero count, since the line that the EOF terminated is empty. This looks like an end-of-file, and that’s why Ctrl-d can be thought of as an end-of-file "character" ================================= My testing indicates this is a workable solution. In addition, although it *should* be the default behavior, I have set the "terminal modes" string in the pty_req channel request to set VEOF (the character to be treated as EOF) to 0x04, and provided a method to configure this value. The changes I propose are in the attached diff. The documentation, added to the POD in Perl.pm, is as follows: ================================= * terminal_mode_string Specify the POSIX terminal mode string to send when use_pty is set. By default the only mode set is the VEOF character to 0x04 (opcode 5, value 0x00000004). See RFC 4254 section 8 for complete details on this value. * no_append_veof (SSH-2 only) Set this to 1 if you specified use_pty and do not want Ctrl-D (0x04) appended twice to the end of your input string. On most systems, these bytes cause the terminal driver to return "EOF" when standard input is read. Without them, many programs that read from standard input will hang after consuming all the data on STDIN. No other modifications are made to input data. If your data contains 0x04 bytes, you may need to escape them. Set this to 0 if you have raw terminal data to specify on standard input, and you have terminated it correctly. =================================
Subject: net-ssh-perl.patch
diff -rc ../Net-SSH-Perl-1.34-orig/lib/Net/SSH/Perl/SSH2.pm ./lib/Net/SSH/Perl/SSH2.pm *** ../Net-SSH-Perl-1.34-orig/lib/Net/SSH/Perl/SSH2.pm 2009-01-25 17:50:38.000000000 -0800 --- ./lib/Net/SSH/Perl/SSH2.pm 2011-01-05 18:18:52.162760000 -0800 *************** *** 135,144 **** $channel->register_handler(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, sub { my($channel, $packet) = @_; - $channel->{ssh}->debug("Sending command: $cmd"); ## Experimental pty support: ! if (0 and $ssh->{config}->get('use_pty')) { $ssh->debug("Requesting pty."); my $packet = $channel->request_start('pty-req', 0); --- 135,143 ---- $channel->register_handler(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, sub { my($channel, $packet) = @_; ## Experimental pty support: ! if ($ssh->{config}->get('use_pty')) { $ssh->debug("Requesting pty."); my $packet = $channel->request_start('pty-req', 0); *************** *** 159,174 **** if (!$foundsize) { $packet->put_int32(0) for 1..4; } ! $packet->put_str(""); ! $packet->send; } - $channel->{ssh}->debug("Sending command: $cmd"); my $r_packet = $channel->request_start("exec", 0); $r_packet->put_str($cmd); $r_packet->send; if (defined $stdin) { $channel->send_data($stdin); $channel->drain_outgoing; --- 158,195 ---- if (!$foundsize) { $packet->put_int32(0) for 1..4; } ! ! # Array used to build Pseudo-tty terminal modes; fat commas separate opcodes from values for clarity. ! ! my $terminal_mode_string; ! if(!defined($ssh->{config}->get('terminal_mode_string'))) { ! my @terminal_modes = ( ! 5 => 0,0,0,4, # VEOF => 0x04 (^d) ! 0 # string must end with a 0 opcode ! ); ! for my $char (@terminal_modes) { ! $terminal_mode_string .= chr($char); ! } ! } ! else { ! $terminal_mode_string = $ssh->{config}->get('terminal_mode_string'); ! } ! $packet->put_str($terminal_mode_string); ! $packet->send; } my $r_packet = $channel->request_start("exec", 0); $r_packet->put_str($cmd); $r_packet->send; if (defined $stdin) { + if($ssh->{config}->get('use_pty') && !$ssh->{config}->get('no_append_veof')) { + my $append_string = $ssh->{config}->get('stdin_append'); + if(!defined($append_string)) { + $append_string = chr(4) . chr(4); + } + $stdin .= $append_string; + } $channel->send_data($stdin); $channel->drain_outgoing; diff -rc ../Net-SSH-Perl-1.34-orig/lib/Net/SSH/Perl.pm ./lib/Net/SSH/Perl.pm *** ../Net-SSH-Perl-1.34-orig/lib/Net/SSH/Perl.pm 2009-02-01 17:18:27.000000000 -0800 --- ./lib/Net/SSH/Perl.pm 2011-01-05 18:18:31.806201000 -0800 *************** *** 9,19 **** use Net::SSH::Perl::Constants qw( :protocol :compat :hosts ); use Net::SSH::Perl::Cipher; use Net::SSH::Perl::Util qw( :hosts _read_yes_or_no ); use vars qw( $VERSION $CONFIG $HOSTNAME ); $CONFIG = {}; ! use Socket; use IO::Socket; use Fcntl; use Symbol; --- 9,20 ---- use Net::SSH::Perl::Constants qw( :protocol :compat :hosts ); use Net::SSH::Perl::Cipher; use Net::SSH::Perl::Util qw( :hosts _read_yes_or_no ); + use Data::Dumper; use vars qw( $VERSION $CONFIG $HOSTNAME ); $CONFIG = {}; ! use Socket qw(IPPROTO_TCP TCP_NODELAY); use IO::Socket; use Fcntl; use Symbol; *************** *** 232,237 **** --- 233,239 ---- for(; $p != $end; $p += $delta) { socket($sock, AF_INET, SOCK_STREAM, getprotobyname('tcp') || 0) || croak "Net::SSH: Can't create socket: $!"; + setsockopt($sock, IPPROTO_TCP, TCP_NODELAY, 1); last if not $p or bind($sock, sockaddr_in($p,$addr)); if ($! =~ /Address already in use/i) { close($sock) or warn qq{Could not close socket: $!\n}; *************** *** 346,351 **** --- 348,355 ---- sub check_host_key { my $ssh = shift; my($key, $host, $u_hostfile, $s_hostfile) = @_; + my $strict_host_key_checking = $ssh->{config}->get('strict_host_key_checking'); + $strict_host_key_checking ||= 'no'; $host ||= $ssh->{host}; $u_hostfile ||= $ssh->{config}->get('user_known_hosts'); $s_hostfile ||= $ssh->{config}->get('global_known_hosts'); *************** *** 359,379 **** $ssh->debug("Host '$host' is known and matches the host key."); } elsif ($status == HOST_NEW) { ! if ($ssh->{config}->get('interactive')) { my $prompt = qq(The authenticity of host '$host' can't be established. Key fingerprint is @{[ $key->fingerprint ]}. Are you sure you want to continue connecting (yes/no)?); unless (_read_yes_or_no($prompt, "yes")) { croak "Aborted by user!"; ! } ! } ! $ssh->debug("Permanently added '$host' to the list of known hosts."); ! _add_host_to_hostfile($host, $u_hostfile, $key); ! } else { ! croak "Host key for '$host' has changed!"; ! } } 1; --- 363,385 ---- $ssh->debug("Host '$host' is known and matches the host key."); } elsif ($status == HOST_NEW) { ! if ($strict_host_key_checking =~ /(ask|yes)/) { ! if (!$ssh->{config}->get('interactive')) { ! croak "Host key verification failed."; ! } my $prompt = qq(The authenticity of host '$host' can't be established. Key fingerprint is @{[ $key->fingerprint ]}. Are you sure you want to continue connecting (yes/no)?); unless (_read_yes_or_no($prompt, "yes")) { croak "Aborted by user!"; ! } ! } ! $ssh->debug("Permanently added '$host' to the list of known hosts."); ! _add_host_to_hostfile($host, $u_hostfile, $key); } else { ! croak "Host key for '$host' has changed!"; ! } } 1; *************** *** 587,592 **** --- 593,619 ---- The default is 1 if you're starting up a shell, and 0 otherwise. + =item * terminal_mode_string + + Specify the POSIX terminal mode string to send when use_pty is + set. By default the only mode set is the VEOF character to 0x04 + (opcode 5, value 0x00000004). See RFC 4254 section 8 for complete + details on this value. + + =item * no_append_veof + + (SSH-2 only) Set this to 1 if you specified use_pty and do not want + Ctrl-D (0x04) appended twice to the end of your input string. On most + systems, these bytes cause the terminal driver to return "EOF" when + standard input is read. Without them, many programs that read from + standard input will hang after consuming all the data on STDIN. + + No other modifications are made to input data. If your data contains + 0x04 bytes, you may need to escape them. + + Set this to 0 if you have raw terminal data to specify on standard + input, and you have terminated it correctly. + =item * options Used to specify additional options to the configuration settings;
Patch now tracked in a branch: https://github.com/renormalist/Net-SSH-Perl/tree/pty-support waiting for advice how to continue. Kind regards, Steffen -- Steffen Schwigon <ss5@renormalist.net> Dresden Perl Mongers <http://dresden-pm.org/>
Branch maintenance. It's now here: https://github.com/renormalist/Net-SSH-Perl/tree/rt64517-pty-support Steffen -- Steffen Schwigon <ss5@renormalist.net> Dresden Perl Mongers <http://dresden-pm.org/>
On Mon Dec 10 06:17:24 2012, SCHWIGON wrote: Show quoted text
Now part of v1.36, just uploaded to CPAN. Steffen -- Steffen Schwigon <ss5@renormalist.net> Dresden Perl Mongers <http://dresden-pm.org/>