Code

1. Keeping the socket open on my system, when apcupsd is shutdown, collectd dies...
[collectd.git] / src / apcups.c
index e1b0178492e7dfe5bc65852126b1da271b3730be..b9a500d00f64769b08d66d77f8e467330daf84d8 100644 (file)
@@ -34,6 +34,7 @@
 #include "plugin.h"      /* plugin_register, plugin_submit */
 #include "configfile.h"  /* cf_register */
 #include "utils_debug.h"
+#include <errno.h>
 
 #if HAVE_SYS_TYPES_H
 # include <sys/types.h>
@@ -44,6 +45,7 @@
 #if HAVE_NETDB_H
 # include <netdb.h>
 #endif
+
 #if HAVE_NETINET_IN_H
 # include <netinet/in.h>
 #endif
 #define MODULE_NAME "apcups"
 
 /* Default values for contacting daemon */
-static char *global_host = NULL;
+static char *global_host = "localhost";
 static int   global_port = NISPORT;
 
 /* 
  * The following are only if not compiled to test the module with its own main.
 */
-#if APCMAIN
+#if !APCMAIN
 static char *bvolt_file_template = "apcups/voltage-%s.rrd";
 static char *bvolt_ds_def[] = 
 {
@@ -74,7 +76,7 @@ static int bvolt_ds_num = 1;
 static char *load_file_template = "apcups/charge_percent.rrd";
 static char *load_ds_def[] = 
 {
-       "DS:percent:GAUGE:"COLLECTD_HEARTBEAT":0:100",
+       "DS:percent:GAUGE:"COLLECTD_HEARTBEAT":0:110",
 };
 static int load_ds_num = 1;
 
@@ -136,49 +138,65 @@ struct apc_detail_s
  * It is possible that the total bytes require in several
  * read requests
  */
-static int read_nbytes (int fd, char *ptr, int nbytes)
+static int read_nbytes (int *fd, char *ptr, int nbytes)
 {
-       int nleft, nread;
+       int nleft;
+       int nread;
 
        nleft = nbytes;
+       nread = -1;
+
+       assert (*fd >= 0);
+
+       while ((nleft > 0) && (nread != 0))
+       {
+               nread = read (*fd, ptr, nleft);
 
-       while (nleft > 0) {
-               do {
-                       nread = read (fd, ptr, nleft);
-               } while (nread == -1 && (errno == EINTR || errno == EAGAIN));
+               if (nread == -1 && (errno == EINTR || errno == EAGAIN))
+                       continue;
 
-               if (nread <= 0) {
-                       return (nread);           /* error, or EOF */
+               if (nread == -1)
+               {
+                       *fd = -1;
+                       syslog (LOG_ERR, "apcups plugin: write failed: %s", strerror (errno));
+                       return (-1);
                }
 
                nleft -= nread;
                ptr += nread;
        }
 
-       return (nbytes - nleft);        /* return >= 0 */
+       return (nbytes - nleft);
 }
 
 /*
  * Write nbytes to the network.
  * It may require several writes.
  */
-static int write_nbytes (int fd, void *buf, int buflen)
+static int write_nbytes (int *fd, void *buf, int buflen)
 {
        int nleft;
        int nwritten;
        char *ptr;
 
+       assert (buflen > 0);
+       assert (*fd >= 0);
+
        ptr = (char *) buf;
 
        nleft = buflen;
        while (nleft > 0)
        {
-               nwritten = write (fd, ptr, nleft);
+               nwritten = write (*fd, ptr, nleft);
 
-               if (nwritten <= 0)
+               if ((nwritten == -1) && ((errno == EAGAIN) || (errno == EINTR)))
+                       continue;
+
+               if (nwritten == -1)
                {
                        syslog (LOG_ERR, "Writing to socket failed: %s", strerror (errno));
-                       return (nwritten);
+                       *fd = -1;
+                       return (-1);
                }
 
                nleft -= nwritten;
@@ -190,13 +208,17 @@ static int write_nbytes (int fd, void *buf, int buflen)
 }
 
 /* Close the network connection */
-static void net_close (int sockfd)
+static void net_close (int *fd)
 {
        short pktsiz = 0;
 
+       assert (*fd >= 0);
+
        /* send EOF sentinel */
-       write_nbytes (sockfd, &pktsiz, sizeof (short));
-       close (sockfd);
+       write_nbytes (fd, &pktsiz, sizeof (short));
+
+       close (*fd);
+       *fd = -1;
 }
 
 
@@ -271,7 +293,7 @@ static int net_open (char *host, char *service, int port)
  * Returns -1 on hard end of file (i.e. network connection close)
  * Returns -2 on error
  */
-static int net_recv (int sockfd, char *buf, int buflen)
+static int net_recv (int *sockfd, char *buf, int buflen)
 {
        int   nbytes;
        short pktsiz;
@@ -310,11 +332,13 @@ static int net_recv (int sockfd, char *buf, int buflen)
  * Returns zero on success
  * Returns non-zero on error
  */
-static int net_send (int sockfd, char *buff, int len)
+static int net_send (int *sockfd, char *buff, int len)
 {
        int rc;
        short packet_size;
 
+       assert (len > 0);
+
        /* send short containing size of data packet */
        packet_size = htons ((short) len);
 
@@ -334,14 +358,14 @@ static int net_send (int sockfd, char *buff, int len)
 static int apc_query_server (char *host, int port,
                struct apc_detail_s *apcups_detail)
 {
-       int     sockfd;
        int     n;
        char    recvline[1024];
        char   *tokptr;
        char   *key;
        double  value;
 
-       static int complain = 0;
+       static int sockfd   = -1;
+       static unsigned int complain = 0;
 
 #if APCMAIN
 # define PRINT_VALUE(name, val) printf("  Found property: name = %s; value = %f;\n", name, val)
@@ -349,28 +373,33 @@ static int apc_query_server (char *host, int port,
 # define PRINT_VALUE(name, val) /**/
 #endif
 
-       /* TODO: Keep the socket open, if possible */
-       if ((sockfd = net_open (host, NULL, port)) < 0)
+       if (sockfd < 0)
        {
-               /* Complain once every six hours. */
-               int complain_step = 21600 / atoi (COLLECTD_STEP);
+               if ((sockfd = net_open (host, NULL, port)) < 0)
+               {
+                       /* Complain first time and once every six hours. */
+                       int complain_step = 21600 / atoi (COLLECTD_STEP);
 
-               if ((complain % complain_step) == 0)
-                       syslog (LOG_ERR, "apcups plugin: Connecting to the apcupsd failed.");
-               complain++;
+                       if (complain == 0 || (complain % complain_step) == 0)
+                               syslog (LOG_ERR, "apcups plugin: Connecting to the apcupsd failed.");
+                       complain++;
 
-               return (-1);
+                       return (-1);
+               } else {
+                       if(complain > 1)
+                               syslog (LOG_ERR, "apcups plugin: Connection re-established to the apcupsd.");
+               }
+               complain = 0;
        }
-       complain = 0;
 
-       if (net_send (sockfd, "status", 6) < 0)
+       if (net_send (&sockfd, "status", 6) < 0)
        {
                syslog (LOG_ERR, "apcups plugin: Writing to the socket failed.");
                return (-1);
        }
 
        /* XXX: Do we read `n' or `n-1' bytes? */
-       while ((n = net_recv (sockfd, recvline, sizeof (recvline) - 1)) > 0)
+       while ((n = net_recv (&sockfd, recvline, sizeof (recvline) - 1)) > 0)
        {
                assert (n < sizeof (recvline));
                recvline[n] = '\0';
@@ -403,24 +432,21 @@ static int apc_query_server (char *host, int port,
                                apcups_detail->linefreq = value;
                        else if (strcmp ("TIMELEFT", tokptr) == 0)
                                apcups_detail->timeleft = value;
-                       else
-                       {
-                               syslog (LOG_WARNING, "apcups plugin: Received unknown property from apcupsd: `%s' = %f",
-                                               key, value);
-                       }
 
                        tokptr = strtok (NULL, ":");
                } /* while (tokptr != NULL) */
        }
-
-       net_close (sockfd);
-
+       
        if (n < 0)
        {
                syslog (LOG_WARNING, "apcups plugin: Error reading from socket");
                return (-1);
+       } else {
+               /* close the opened socket */
+               net_close(&sockfd);
        }
 
+
        return (0);
 }
 
@@ -434,19 +460,17 @@ int main (int argc, char **argv)
        /* we are not really going to use this */
        struct apc_detail_s apcups_detail;
 
-       openlog ("apcups", LOG_PID | LOG_NDELAY | LOG_LOCAL1);
+       openlog ("apcups", LOG_PID | LOG_NDELAY | LOG_LOCAL1, LOG_USER);
 
-       if (!*host || strcmp (host, "0.0.0.0") == 0)
-               host = "localhost";
+       if (global_host == NULL || strcmp (global_host, "0.0.0.0") == 0)
+               global_host = "localhost";
 
-       if(do_apc_status (host, port, &apcups_detail) < 0)
+       if(apc_query_server (global_host, global_port, &apcups_detail) < 0)
        {
                printf("apcups: Failed...\n");
                return(-1);
        }
 
-       apc_query_server (global_host, global_port, &apcups_detail);
-
        return 0;
 }
 #else