{GPL, other}: Relicense to MIT license.
[collectd.git] / src / apcups.c
index 60ba7f4..be7673c 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * collectd - src/apcups.c
- * Copyright (C) 2006-2007  Florian octo Forster
+ * Copyright (C) 2006-2012  Florian octo Forster
  * Copyright (C) 2006       Anthony Gialluca <tonyabg at charter.net>
  * Copyright (C) 2000-2004  Kern Sibbald
  * Copyright (C) 1996-1999  Andre M. Hedrick <andre at suse.com>
@@ -21,7 +21,7 @@
  *
  * Authors:
  *   Anthony Gialluca <tonyabg at charter.net>
- *   Florian octo Forster <octo at verplant.org>
+ *   Florian octo Forster <octo at collectd.org>
  **/
 
 #include "collectd.h"
@@ -70,16 +70,22 @@ struct apc_detail_s
 /* Default values for contacting daemon */
 static char *conf_host = NULL;
 static int   conf_port = NISPORT;
+/* Defaults to false for backwards compatibility. */
+static _Bool conf_report_seconds = 0;
 
 static int global_sockfd = -1;
 
+static int count_retries = 0;
+static int count_iterations = 0;
+static _Bool close_socket = 0;
+
 static const char *config_keys[] =
 {
        "Host",
        "Port",
-       NULL
+       "ReportSeconds"
 };
-static int config_keys_num = 2;
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
 
 static int net_shutdown (int *fd)
 {
@@ -266,6 +272,8 @@ static int apc_query_server (char *host, int port,
        char   *toksaveptr;
        char   *key;
        double  value;
+       _Bool retry = 1;
+       int status;
 
 #if APCMAIN
 # define PRINT_VALUE(name, val) printf("  Found property: name = %s; value = %f;\n", name, val)
@@ -273,21 +281,51 @@ static int apc_query_server (char *host, int port,
 # define PRINT_VALUE(name, val) /**/
 #endif
 
-       if (global_sockfd < 0)
+       while (retry)
        {
-               global_sockfd = net_open (host, port);
                if (global_sockfd < 0)
                {
-                       ERROR ("apcups plugin: Connecting to the "
-                                       "apcupsd failed.");
+                       global_sockfd = net_open (host, port);
+                       if (global_sockfd < 0)
+                       {
+                               ERROR ("apcups plugin: Connecting to the "
+                                               "apcupsd failed.");
+                               return (-1);
+                       }
+               }
+
+
+               status = net_send (&global_sockfd, "status", strlen ("status"));
+               if (status != 0)
+               {
+                       /* net_send is closing the socket on error. */
+                       assert (global_sockfd < 0);
+                       if (retry)
+                       {
+                               retry = 0;
+                               count_retries++;
+                               continue;
+                       }
+
+                       ERROR ("apcups plugin: Writing to the socket failed.");
                        return (-1);
                }
-       }
 
-       if (net_send (&global_sockfd, "status", 6) < 0)
+               break;
+       } /* while (retry) */
+
+        /* When collectd's collection interval is larger than apcupsd's
+         * timeout, we would have to retry / re-connect each iteration. Try to
+         * detect this situation and shut down the socket gracefully in that
+         * case. Otherwise, keep the socket open to avoid overhead. */
+       count_iterations++;
+       if ((count_iterations == 10) && (count_retries > 2))
        {
-               ERROR ("apcups plugin: Writing to the socket failed.");
-               return (-1);
+               NOTICE ("apcups plugin: There have been %i retries in the "
+                               "first %i iterations. Will close the socket "
+                               "in future iterations.",
+                               count_retries, count_iterations);
+               close_socket = 1;
        }
 
        while ((n = net_recv (&global_sockfd, recvline, sizeof (recvline) - 1)) > 0)
@@ -311,7 +349,7 @@ static int apc_query_server (char *host, int port,
 
                        if (strcmp ("LINEV", key) == 0)
                                apcups_detail->linev = value;
-                       else if (strcmp ("BATTV", key) == 0) 
+                       else if (strcmp ("BATTV", key) == 0)
                                apcups_detail->battv = value;
                        else if (strcmp ("ITEMP", key) == 0)
                                apcups_detail->itemp = value;
@@ -324,15 +362,27 @@ static int apc_query_server (char *host, int port,
                        else if (strcmp ("LINEFREQ", key) == 0)
                                apcups_detail->linefreq = value;
                        else if (strcmp ("TIMELEFT", key) == 0)
+                       {
+                               /* Convert minutes to seconds if requested by
+                                * the user. */
+                               if (conf_report_seconds)
+                                       value *= 60.0;
                                apcups_detail->timeleft = value;
+                       }
 
                        tokptr = strtok_r (NULL, ":", &toksaveptr);
                } /* while (tokptr != NULL) */
        }
-       
+       status = errno; /* save errno, net_shutdown() may re-set it. */
+
+       if (close_socket)
+               net_shutdown (&global_sockfd);
+
        if (n < 0)
        {
-               WARNING ("apcups plugin: Error reading from socket");
+               char errbuf[1024];
+               ERROR ("apcups plugin: Reading from socket failed: %s",
+                               sstrerror (status, errbuf, sizeof (errbuf)));
                return (-1);
        }
 
@@ -361,6 +411,13 @@ static int apcups_config (const char *key, const char *value)
                }
                conf_port = port_tmp;
        }
+       else if (strcasecmp (key, "ReportSeconds") == 0)
+       {
+               if (IS_TRUE (value))
+                       conf_report_seconds = 1;
+               else
+                       conf_report_seconds = 0;
+       }
        else
        {
                return (-1);
@@ -408,7 +465,7 @@ static int apcups_read (void)
        apcups_detail.battv    =   -1.0;
        apcups_detail.loadpct  =   -1.0;
        apcups_detail.bcharge  =   -1.0;
-       apcups_detail.timeleft =   -1.0;
+       apcups_detail.timeleft =    NAN;
        apcups_detail.itemp    = -300.0;
        apcups_detail.linefreq =   -1.0;