diff options
-rw-r--r-- | lib/Net/Cmd.pm | 68 |
1 files changed, 45 insertions, 23 deletions
diff --git a/lib/Net/Cmd.pm b/lib/Net/Cmd.pm index 0b1bbe1..bdb2028 100644 --- a/lib/Net/Cmd.pm +++ b/lib/Net/Cmd.pm @@ -16,7 +16,7 @@ use warnings; use Carp; use Exporter; use Symbol 'gensym'; -use Errno 'EINTR'; +use Errno qw(EINTR EAGAIN EWOULDBLOCK); BEGIN { if ($^O eq 'os390') { @@ -317,6 +317,19 @@ sub unsupported { 0; } +# only call this after EAGAIN or EWOULDBLOCK +# returns 1 if ready, 0 on timeout, -1 on error (sets $!) +sub _wait_read { + my ($cmd, $timeout) = @_; + + vec(my $rin, fileno($cmd), 1) = 1; + + if (eval { $cmd->is_SSL && $cmd->want_write }) { + select(undef, $rin, undef, $timeout); + } else { + select($rin, undef, undef, $timeout); + } +} sub getline { my $cmd = shift; @@ -331,34 +344,43 @@ sub getline { return if $cmd->_is_closed; - my $fd = fileno($cmd); - my $rin = ""; - vec($rin, $fd, 1) = 1; - - until (scalar(@{${*$cmd}{'net_cmd_lines'}})) { - my $timeout = $cmd->timeout || undef; - my $rout; - - my $select_ret = select($rout = $rin, undef, undef, $timeout); - if ($select_ret > 0) { - unless (sysread($cmd, $partial, 1024, length($partial))) { - my $err = $!; - $cmd->close; - $cmd->_set_status_closed($err); - return; + my $was_blocking = $cmd->blocking; + $cmd->blocking(0); + my $timeout = $cmd->timeout || undef; + my $err; + + until ($err || scalar(@{${*$cmd}{'net_cmd_lines'}})) { + my $r = sysread($cmd, $partial, 1024, length($partial)); + if (defined($r)) { + if ($r > 0) { + my @buf = split(/\015?\012/, $partial, -1); ## break into lines + $partial = pop @buf; + push(@{${*$cmd}{'net_cmd_lines'}}, map {"$_\n"} @buf); + } else { + $err = 'EOF from peer'; } + } + elsif ($! == EAGAIN || $! == EWOULDBLOCK) { + my $select_ret = $cmd->_wait_read($timeout); + redo if $select_ret > 0; + $err = 'timeout'; # ignore '$!' for backwards compatibility + } + else { + $err = $!; + } + } - my @buf = split(/\015?\012/, $partial, -1); ## break into lines - - $partial = pop @buf; - - push(@{${*$cmd}{'net_cmd_lines'}}, map {"$_\n"} @buf); + $cmd->blocking($was_blocking); + if ($err) { + if ($err eq 'timeout') { + $cmd->_set_status_timeout; } else { - $cmd->_set_status_timeout; - return; + $cmd->close; + $cmd->_set_status_closed($err); } + return; } ${*$cmd}{'net_cmd_partial'} = $partial; |