/*
* collectd - src/apcups.c
- * Copyright (C) 2006-2012 Florian octo Forster
+ * Copyright (C) 2006-2015 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>
**/
#include "collectd.h"
+
#include "common.h" /* rrd_update_file */
#include "plugin.h" /* plugin_register, plugin_submit */
-#include "configfile.h" /* cf_register */
#if HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
-#if HAVE_SYS_SOCKET_H
-# include <sys/socket.h>
-#endif
#if HAVE_NETDB_H
# include <netdb.h>
#endif
# include <netinet/in.h>
#endif
-#define NISPORT 3551
-#define MAXSTRING 256
-#define MODULE_NAME "apcups"
+#ifndef APCUPS_SERVER_TIMEOUT
+# define APCUPS_SERVER_TIMEOUT 15.0
+#endif
+
+#ifndef APCUPS_DEFAULT_NODE
+# define APCUPS_DEFAULT_NODE "localhost"
+#endif
-#define APCUPS_DEFAULT_HOST "localhost"
+#ifndef APCUPS_DEFAULT_SERVICE
+# define APCUPS_DEFAULT_SERVICE "3551"
+#endif
/*
* Private data types
*/
-struct apc_detail_s
+typedef struct
{
- double linev;
- double loadpct;
- double bcharge;
- double timeleft;
- double outputv;
- double itemp;
- double battv;
- double linefreq;
-};
+ gauge_t linev;
+ gauge_t loadpct;
+ gauge_t bcharge;
+ gauge_t timeleft;
+ gauge_t outputv;
+ gauge_t itemp;
+ gauge_t battv;
+ gauge_t linefreq;
+} apc_detail_t;
/*
* Private variables
*/
/* Default values for contacting daemon */
-static char *conf_host = NULL;
-static int conf_port = NISPORT;
+static char *conf_node = NULL;
+static char *conf_service = NULL;
/* Defaults to false for backwards compatibility. */
static _Bool conf_report_seconds = 0;
+static _Bool conf_persistent_conn = 1;
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",
- "ReportSeconds"
-};
-static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
static int net_shutdown (int *fd)
{
if ((fd == NULL) || (*fd < 0))
return (EINVAL);
- swrite (*fd, (void *) &packet_size, sizeof (packet_size));
+ (void)swrite (*fd, (void *) &packet_size, sizeof (packet_size));
close (*fd);
*fd = -1;
* Returns -1 on error
* Returns socket file descriptor otherwise
*/
-static int net_open (char *host, int port)
+static int net_open (char const *node, char const *service)
{
int sd;
int status;
- char port_str[8];
- struct addrinfo ai_hints;
struct addrinfo *ai_return;
struct addrinfo *ai_list;
- assert ((port > 0x00000000) && (port <= 0x0000FFFF));
+ /* TODO: Change this to `AF_UNSPEC' if apcupsd can handle IPv6 */
+ struct addrinfo ai_hints = {
+ .ai_family = AF_INET,
+ .ai_socktype = SOCK_STREAM
+ };
- /* Convert the port to a string */
- ssnprintf (port_str, sizeof (port_str), "%i", port);
-
- /* Resolve name */
- memset ((void *) &ai_hints, '\0', sizeof (ai_hints));
- ai_hints.ai_family = AF_INET; /* XXX: Change this to `AF_UNSPEC' if apcupsd can handle IPv6 */
- ai_hints.ai_socktype = SOCK_STREAM;
-
- status = getaddrinfo (host, port_str, &ai_hints, &ai_return);
+ status = getaddrinfo (node, service, &ai_hints, &ai_return);
if (status != 0)
{
char errbuf[1024];
DEBUG ("apcups plugin: Done opening a socket %i", sd);
return (sd);
-} /* int net_open (char *host, char *service, int port) */
+} /* int net_open */
/*
* Receive a message from the other end. Each message consists of
* 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, const char *buff, int len)
{
uint16_t packet_size;
}
/* Get and print status from apcupsd NIS server */
-static int apc_query_server (char *host, int port,
- struct apc_detail_s *apcups_detail)
+static int apc_query_server (char const *node, char const *service,
+ apc_detail_t *apcups_detail)
{
int n;
char recvline[1024];
char *tokptr;
char *toksaveptr;
- char *key;
- double value;
_Bool retry = 1;
int status;
{
if (global_sockfd < 0)
{
- global_sockfd = net_open (host, port);
+ global_sockfd = net_open (node, service);
if (global_sockfd < 0)
{
ERROR ("apcups plugin: Connecting to the "
"first %i iterations. Will close the socket "
"in future iterations.",
count_retries, count_iterations);
- close_socket = 1;
+ conf_persistent_conn = 0;
}
while ((n = net_recv (&global_sockfd, recvline, sizeof (recvline) - 1)) > 0)
{
- assert ((unsigned int)n < sizeof (recvline));
- recvline[n] = '\0';
+ assert ((size_t)n < sizeof (recvline));
+ recvline[n] = 0;
#if APCMAIN
printf ("net_recv = `%s';\n", recvline);
#endif /* if APCMAIN */
tokptr = strtok_r (recvline, " :\t", &toksaveptr);
while (tokptr != NULL)
{
- key = tokptr;
+ char *key = tokptr;
if ((tokptr = strtok_r (NULL, " :\t", &toksaveptr)) == NULL)
continue;
- value = atof (tokptr);
+
+ gauge_t value;
+ if (strtogauge (tokptr, &value) != 0)
+ continue;
PRINT_VALUE (key, value);
}
status = errno; /* save errno, net_shutdown() may re-set it. */
- if (close_socket)
+ if (!conf_persistent_conn)
net_shutdown (&global_sockfd);
if (n < 0)
return (0);
}
-static int apcups_config (const char *key, const char *value)
+static int apcups_config (oconfig_item_t *ci)
{
- if (strcasecmp (key, "host") == 0)
- {
- if (conf_host != NULL)
- {
- free (conf_host);
- conf_host = NULL;
- }
- if ((conf_host = strdup (value)) == NULL)
- return (1);
- }
- else if (strcasecmp (key, "Port") == 0)
+ _Bool persistent_conn_set = 0;
+
+ for (int i = 0; i < ci->children_num; i++)
{
- int port_tmp = atoi (value);
- if (port_tmp < 1 || port_tmp > 65535)
- {
- WARNING ("apcups plugin: Invalid port: %i", port_tmp);
- return (1);
+ oconfig_item_t *child = ci->children + i;
+
+ if (strcasecmp (child->key, "Host") == 0)
+ cf_util_get_string (child, &conf_node);
+ else if (strcasecmp (child->key, "Port") == 0)
+ cf_util_get_service (child, &conf_service);
+ else if (strcasecmp (child->key, "ReportSeconds") == 0)
+ cf_util_get_boolean (child, &conf_report_seconds);
+ else if (strcasecmp (child->key, "PersistentConnection") == 0) {
+ cf_util_get_boolean (child, &conf_persistent_conn);
+ persistent_conn_set = 1;
}
- conf_port = port_tmp;
- }
- else if (strcasecmp (key, "ReportSeconds") == 0)
- {
- if (IS_TRUE (value))
- conf_report_seconds = 1;
else
- conf_report_seconds = 0;
+ ERROR ("apcups plugin: Unknown config option \"%s\".", child->key);
}
- else
- {
- return (-1);
+
+ if (!persistent_conn_set) {
+ double interval = CDTIME_T_TO_DOUBLE(plugin_get_interval());
+ if (interval > APCUPS_SERVER_TIMEOUT) {
+ NOTICE ("apcups plugin: Plugin poll interval set to %.3f seconds. "
+ "Apcupsd NIS socket timeout is %.3f seconds, "
+ "PersistentConnection disabled by default.",
+ interval, APCUPS_SERVER_TIMEOUT);
+ conf_persistent_conn = 0;
+ }
}
+
return (0);
-}
+} /* int apcups_config */
-static void apc_submit_generic (char *type, char *type_inst, double value)
+static void apc_submit_generic (const char *type, const char *type_inst, gauge_t value)
{
- value_t values[1];
- value_list_t vl = VALUE_LIST_INIT;
-
- values[0].gauge = value;
+ if (isnan (value))
+ return;
- vl.values = values;
+ value_list_t vl = VALUE_LIST_INIT;
+ vl.values = &(value_t) { .gauge = value };
vl.values_len = 1;
- sstrncpy (vl.host, hostname_g, sizeof (vl.host));
sstrncpy (vl.plugin, "apcups", sizeof (vl.plugin));
- sstrncpy (vl.plugin_instance, "", sizeof (vl.plugin_instance));
sstrncpy (vl.type, type, sizeof (vl.type));
sstrncpy (vl.type_instance, type_inst, sizeof (vl.type_instance));
plugin_dispatch_values (&vl);
}
-static void apc_submit (struct apc_detail_s *apcups_detail)
+static void apc_submit (apc_detail_t const *apcups_detail)
{
apc_submit_generic ("voltage", "input", apcups_detail->linev);
apc_submit_generic ("voltage", "output", apcups_detail->outputv);
static int apcups_read (void)
{
- struct apc_detail_s apcups_detail;
- int status;
-
- apcups_detail.linev = -1.0;
- apcups_detail.outputv = -1.0;
- apcups_detail.battv = -1.0;
- apcups_detail.loadpct = -1.0;
- apcups_detail.bcharge = -1.0;
- apcups_detail.timeleft = NAN;
- apcups_detail.itemp = -300.0;
- apcups_detail.linefreq = -1.0;
-
- status = apc_query_server (conf_host == NULL
- ? APCUPS_DEFAULT_HOST
- : conf_host,
- conf_port, &apcups_detail);
-
- /*
- * if we did not connect then do not bother submitting
- * zeros. We want rrd files to have NAN.
- */
+ apc_detail_t apcups_detail = {
+ .linev = NAN,
+ .outputv = NAN,
+ .battv = NAN,
+ .loadpct = NAN,
+ .bcharge = NAN,
+ .timeleft = NAN,
+ .itemp = NAN,
+ .linefreq = NAN,
+ };
+
+ int status = apc_query_server (conf_node == NULL
+ ? APCUPS_DEFAULT_NODE
+ : conf_node,
+ conf_service, &apcups_detail);
if (status != 0)
{
- DEBUG ("apcups plugin: apc_query_server (%s, %i) = %i",
- conf_host == NULL
- ? APCUPS_DEFAULT_HOST
- : conf_host,
- conf_port, status);
- return (-1);
+ DEBUG ("apcups plugin: apc_query_server (\"%s\", \"%s\") = %d",
+ conf_node == NULL ? APCUPS_DEFAULT_NODE : conf_node,
+ conf_service, status);
+ return (status);
}
apc_submit (&apcups_detail);
void module_register (void)
{
- plugin_register_config ("apcups", apcups_config, config_keys,
- config_keys_num);
+ plugin_register_complex_config ("apcups", apcups_config);
plugin_register_read ("apcups", apcups_read);
plugin_register_shutdown ("apcups", apcups_shutdown);
} /* void module_register */