From 852982580b27fdc9e90d03215913b6652dccdfa9 Mon Sep 17 00:00:00 2001 From: David Golden Date: Fri, 25 Dec 2015 20:44:44 -0500 Subject: Refactor syswrite with timeout --- lib/Net/Cmd.pm | 109 ++++++++++++++++++++++++++++++--------------------------- 1 file changed, 57 insertions(+), 52 deletions(-) diff --git a/lib/Net/Cmd.pm b/lib/Net/Cmd.pm index e9d0d25..bd4857b 100644 --- a/lib/Net/Cmd.pm +++ b/lib/Net/Cmd.pm @@ -18,6 +18,7 @@ use warnings; use Carp; use Exporter; use Symbol 'gensym'; +use Errno 'EINTR'; BEGIN { if ($^O eq 'os390') { @@ -189,7 +190,57 @@ sub set_status { 1; } +sub _syswrite_with_timeout { + my $cmd = shift; + my $line = shift; + + my $len = length($line); + my $offset = 0; + my $win = ""; + vec($win, fileno($cmd), 1) = 1; + my $timeout = $cmd->timeout || undef; + my $initial = time; + my $pending = $timeout; + + local $SIG{PIPE} = 'IGNORE' unless $^O eq 'MacOS'; + + while ($len) { + my $wout; + my $nfound = select(undef, $wout = $win, undef, $pending); + if ((defined $nfound and $nfound > 0) or -f $cmd) # -f for testing on win32 + { + my $w = syswrite($cmd, $line, $len, $offset); + if (! defined($w) ) { + my $err = $!; + $cmd->close; + $cmd->_set_status_closed($err); + return; + } + $len -= $w; + $offset += $w; + } + elsif ($nfound == -1) { + if ( $! == EINTR ) { + if ( defined($timeout) ) { + redo if ($pending = $timeout - ( time - $initial ) ) > 0; + $cmd->_set_status_timeout; + return; + } + redo; + } + my $err = $!; + $cmd->close; + $cmd->_set_status_closed($err); + return; + } + else { + $cmd->_set_status_timeout; + return; + } + } + return 1; +} sub _set_status_timeout { my $cmd = shift; @@ -201,11 +252,12 @@ sub _set_status_timeout { sub _set_status_closed { my $cmd = shift; + my $err = shift; my $pkg = ref($cmd) || $cmd; $cmd->set_status($cmd->DEF_REPLY_CODE, "[$pkg] Connection closed"); carp(ref($cmd) . ": " . (caller(1))[3] - . "(): unexpected EOF on command channel: $!") if $cmd->debug; + . "(): unexpected EOF on command channel: $err") if $cmd->debug; } sub _is_closed { @@ -463,33 +515,8 @@ sub datasend { ${*$cmd}{'net_cmd_last_ch'} = substr($line, -1, 1); - my $len = length($line); - my $offset = 0; - my $win = ""; - vec($win, fileno($cmd), 1) = 1; - my $timeout = $cmd->timeout || undef; - - local $SIG{PIPE} = 'IGNORE' unless $^O eq 'MacOS'; - - while ($len) { - my $wout; - my $s = select(undef, $wout = $win, undef, $timeout); - if ((defined $s and $s > 0) or -f $cmd) # -f for testing on win32 - { - my $w = syswrite($cmd, $line, $len, $offset); - unless (defined($w) && $w == $len) { - $cmd->close; - $cmd->_set_status_closed; - return; - } - $len -= $w; - $offset += $w; - } - else { - $cmd->_set_status_timeout; - return; - } - } + $cmd->_syswrite_with_timeout($line) + or return; 1; } @@ -511,30 +538,8 @@ sub rawdatasend { print STDERR $b, join("\n$b", split(/\n/, $line)), "\n"; } - my $len = length($line); - my $offset = 0; - my $win = ""; - vec($win, fileno($cmd), 1) = 1; - my $timeout = $cmd->timeout || undef; - - local $SIG{PIPE} = 'IGNORE' unless $^O eq 'MacOS'; - while ($len) { - my $wout; - if (select(undef, $wout = $win, undef, $timeout) > 0) { - my $w = syswrite($cmd, $line, $len, $offset); - unless (defined($w) && $w == $len) { - $cmd->close; - $cmd->_set_status_closed; - return; - } - $len -= $w; - $offset += $w; - } - else { - $cmd->_set_status_timeout; - return; - } - } + $cmd->_syswrite_with_timeout($line) + or return; 1; } -- cgit v1.2.3-24-ge0c7 From 0543d6c234ef5287a87137be740cc7319ecae0df Mon Sep 17 00:00:00 2001 From: David Golden Date: Fri, 25 Dec 2015 21:47:18 -0500 Subject: Make other syswrites use _syswrite_with_timeout Two other methods in Net::Cmd did syswrite() calls, both without a loop or timeout. This replaces those with a call to the new, common _syswrite_with_timeout() method, which does both correctly and restarts after EINTR. --- lib/Net/Cmd.pm | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/lib/Net/Cmd.pm b/lib/Net/Cmd.pm index bd4857b..3c9ca59 100644 --- a/lib/Net/Cmd.pm +++ b/lib/Net/Cmd.pm @@ -279,8 +279,6 @@ sub command { if (exists ${*$cmd}{'net_cmd_last_ch'}); if (scalar(@_)) { - local $SIG{PIPE} = 'IGNORE' unless $^O eq 'MacOS'; - my $str = join( " ", map { @@ -292,17 +290,13 @@ sub command { $str = $cmd->toascii($str) if $tr; $str .= "\015\012"; - my $len = length $str; - my $swlen; - $cmd->debug_print(1, $str) if ($cmd->debug); - unless (defined($swlen = syswrite($cmd,$str,$len)) && $swlen == $len) { - $cmd->close; - $cmd->_set_status_closed; - return $cmd; - } + # though documented to return undef on failure, the legacy behavior + # was to turn $cmd even on failure, so this odd construct does that + $cmd->_syswrite_with_timeout($str) + or return $cmd; } $cmd; @@ -563,19 +557,11 @@ sub dataend { $tosend .= ".\015\012"; - local $SIG{PIPE} = 'IGNORE' unless $^O eq 'MacOS'; - $cmd->debug_print(1, ".\n") if ($cmd->debug); - my $len = length $tosend; - my $w = syswrite($cmd, $tosend, $len); - unless (defined($w) && $w == $len) - { - $cmd->close; - $cmd->_set_status_closed; - return 0; - } + $cmd->_syswrite_with_timeout($tosend) + or return 0; delete ${*$cmd}{'net_cmd_last_ch'}; -- cgit v1.2.3-24-ge0c7 From 4f5bbdbf1e88e40825d64842b38ff0139d761d5e Mon Sep 17 00:00:00 2001 From: David Golden Date: Sat, 26 Dec 2015 21:29:17 -0500 Subject: Fix typo --- lib/Net/Cmd.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Net/Cmd.pm b/lib/Net/Cmd.pm index 3c9ca59..fe4492b 100644 --- a/lib/Net/Cmd.pm +++ b/lib/Net/Cmd.pm @@ -294,7 +294,7 @@ sub command { if ($cmd->debug); # though documented to return undef on failure, the legacy behavior - # was to turn $cmd even on failure, so this odd construct does that + # was to return $cmd even on failure, so this odd construct does that $cmd->_syswrite_with_timeout($str) or return $cmd; } -- cgit v1.2.3-24-ge0c7