From d7af9ef00abb1b022ad743048731664c6c3e0e19 Mon Sep 17 00:00:00 2001 From: Guillem Jover Date: Fri, 7 Apr 2017 16:45:23 +0200 Subject: [PATCH] Collectd::Unixsock: Make code robust against socket errors and disconnections We should check the errors on socket operations, and in case of EPIPE, which happens on socket disconnection, such as when the server gets restarted, we should try to reconnect and retry the last operation to avoid losing data. --- bindings/perl/lib/Collectd/Unixsock.pm | 56 ++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 7 deletions(-) diff --git a/bindings/perl/lib/Collectd/Unixsock.pm b/bindings/perl/lib/Collectd/Unixsock.pm index 4ec7993d0..28dbd57e6 100644 --- a/bindings/perl/lib/Collectd/Unixsock.pm +++ b/bindings/perl/lib/Collectd/Unixsock.pm @@ -57,6 +57,7 @@ use strict; use warnings; use Carp qw(cluck confess carp croak); +use POSIX; use IO::Socket::UNIX; use Scalar::Util qw( looks_like_number ); @@ -146,6 +147,24 @@ sub _escape_argument return "\"$arg\""; } +# Handle socket errors. +sub _socket_error { + my ($self, $where) = @_; + + # If the peer has reset the connection, try to reconnect, + # otherwise fail. + if ($! == EPIPE) { + _debug "^^ error on $where: $!; reconnecting\n"; + $self->destroy; + $self->{sock} = _create_socket ($self->{path}) or return 1; + return; + } else { + carp ("error on $where: $!; aborting action\n"); + $self->{error} = $!; + return 1; + } +} + # Send a command on a socket, including any required argument escaping. # Return a single line of result. sub _socket_command { @@ -160,9 +179,16 @@ sub _socket_command { $command .= "\n"; } _debug "-> $command"; - $fh->print($command); + while (not $fh->print($command)) { + return if $self->_socket_error ('print'); + $fh = $self->{sock}; + } - my $response = $fh->getline; + my $response; + while (not defined ($response = $fh->getline)) { + return if $self->_socket_error ('getline'); + $fh = $self->{sock}; + } chomp $response; _debug "<- $response\n"; return $response; @@ -185,7 +211,11 @@ sub _socket_chat for (1 .. $nresults) { - my $entry = $fh->getline; + my $entry; + while (not defined($entry = $fh->getline)) { + return if $self->_socket_error ('getline'); + $fh = $self->{sock}; + } chomp $entry; _debug "<- $entry\n"; $callback->($entry, $cbdata); @@ -207,9 +237,15 @@ sub _send_message warn "Collectd::Unixsock->_send_message(\$msg): message is too long!" if length($msg) > 1024; _debug "-> $msg"; - $fh->print($msg); + while (not $fh->print($msg)) { + return if $self->_socket_error ('print'); + $fh = $self->{sock}; + } - $msg = <$fh>; + while (not defined ($msg = <$fh>)) { + return if $self->_socket_error ('readline'); + $fh = $self->{sock}; + } chomp ($msg); _debug "<- $msg\n"; @@ -396,7 +432,10 @@ sub listval_filter for (1 .. $nresults) { - $msg = <$fh>; + while (not defined ($msg = <$fh>)) { + return if $self->_socket_error ('readline'); + $fh = $self->{sock}; + } chomp $msg; _debug "<- $msg\n"; next unless $msg =~ $pattern; @@ -438,7 +477,10 @@ sub listval for (1 .. $nresults) { - $msg = <$fh>; + while (not defined ($msg = <$fh>)) { + return if $self->_socket_error ('readline'); + $fh = $self->{sock}; + } chomp $msg; _debug "<- $msg\n"; -- 2.47.2