about summary refs log tree commit
diff options
context:
space:
mode:
-rw-r--r--lib/Net/Cmd.pm68
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;