X-Git-Url: https://git.octo.it/?p=collectd.git;a=blobdiff_plain;f=src%2Fapcups.c;h=406c164a2b7e96781093c85b30524e3937d481e2;hp=eb1f0622989b9793acc66cde043cb43e484808b3;hb=1159cb5d383c55a80a0db100b8f7aadcf44740a5;hpb=68d6aff96c029c9a455b7a0146c788af08f9ff22 diff --git a/src/apcups.c b/src/apcups.c index eb1f0622..406c164a 100644 --- a/src/apcups.c +++ b/src/apcups.c @@ -1,6 +1,6 @@ /* * collectd - src/apcups.c - * Copyright (C) 2006-2012 Florian octo Forster + * Copyright (C) 2006-2015 Florian octo Forster * Copyright (C) 2006 Anthony Gialluca * Copyright (C) 2000-2004 Kern Sibbald * Copyright (C) 1996-1999 Andre M. Hedrick @@ -25,90 +25,82 @@ **/ #include "collectd.h" -#include "common.h" /* rrd_update_file */ -#include "plugin.h" /* plugin_register, plugin_submit */ -#include "configfile.h" /* cf_register */ + +#include "common.h" /* rrd_update_file */ +#include "plugin.h" /* plugin_register, plugin_submit */ #if HAVE_SYS_TYPES_H -# include -#endif -#if HAVE_SYS_SOCKET_H -# include +#include #endif #if HAVE_NETDB_H -# include +#include #endif #if HAVE_NETINET_IN_H -# include +#include #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 */ -typedef struct -{ - gauge_t linev; - gauge_t loadpct; - gauge_t bcharge; - gauge_t timeleft; - gauge_t outputv; - gauge_t itemp; - gauge_t battv; - gauge_t linefreq; +typedef struct { + 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) -{ - uint16_t packet_size = 0; +static int net_shutdown(int *fd) { + uint16_t packet_size = 0; - if ((fd == NULL) || (*fd < 0)) - return (EINVAL); + if ((fd == NULL) || (*fd < 0)) + return EINVAL; - swrite (*fd, (void *) &packet_size, sizeof (packet_size)); - close (*fd); - *fd = -1; + (void)swrite(*fd, (void *)&packet_size, sizeof(packet_size)); + close(*fd); + *fd = -1; - return (0); + return 0; } /* int net_shutdown */ /* Close the network connection */ -static int apcups_shutdown (void) -{ - if (global_sockfd < 0) - return (0); +static int apcups_shutdown(void) { + if (global_sockfd < 0) + return 0; - net_shutdown (&global_sockfd); - return (0); + net_shutdown(&global_sockfd); + return 0; } /* int apcups_shutdown */ /* @@ -116,70 +108,56 @@ static int apcups_shutdown (void) * Returns -1 on error * Returns socket file descriptor otherwise */ -static int net_open (char *host, int port) -{ - 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)); - - /* 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); - if (status != 0) - { - char errbuf[1024]; - INFO ("apcups plugin: getaddrinfo failed: %s", - (status == EAI_SYSTEM) - ? sstrerror (errno, errbuf, sizeof (errbuf)) - : gai_strerror (status)); - return (-1); - } - - /* Create socket */ - sd = -1; - for (ai_list = ai_return; ai_list != NULL; ai_list = ai_list->ai_next) - { - sd = socket (ai_list->ai_family, ai_list->ai_socktype, ai_list->ai_protocol); - if (sd >= 0) - break; - } - /* `ai_list' still holds the current description of the socket.. */ - - if (sd < 0) - { - DEBUG ("apcups plugin: Unable to open a socket"); - freeaddrinfo (ai_return); - return (-1); - } - - status = connect (sd, ai_list->ai_addr, ai_list->ai_addrlen); - - freeaddrinfo (ai_return); - - if (status != 0) /* `connect(2)' failed */ - { - char errbuf[1024]; - INFO ("apcups plugin: connect failed: %s", - sstrerror (errno, errbuf, sizeof (errbuf))); - close (sd); - return (-1); - } - - DEBUG ("apcups plugin: Done opening a socket %i", sd); - - return (sd); -} /* int net_open (char *host, char *service, int port) */ +static int net_open(char const *node, char const *service) { + int sd; + int status; + struct addrinfo *ai_return; + struct addrinfo *ai_list; + + /* TODO: Change this to `AF_UNSPEC' if apcupsd can handle IPv6 */ + struct addrinfo ai_hints = {.ai_family = AF_INET, .ai_socktype = SOCK_STREAM}; + + status = getaddrinfo(node, service, &ai_hints, &ai_return); + if (status != 0) { + char errbuf[1024]; + INFO("apcups plugin: getaddrinfo failed: %s", + (status == EAI_SYSTEM) ? sstrerror(errno, errbuf, sizeof(errbuf)) + : gai_strerror(status)); + return -1; + } + + /* Create socket */ + sd = -1; + for (ai_list = ai_return; ai_list != NULL; ai_list = ai_list->ai_next) { + sd = socket(ai_list->ai_family, ai_list->ai_socktype, ai_list->ai_protocol); + if (sd >= 0) + break; + } + /* `ai_list' still holds the current description of the socket.. */ + + if (sd < 0) { + DEBUG("apcups plugin: Unable to open a socket"); + freeaddrinfo(ai_return); + return -1; + } + + status = connect(sd, ai_list->ai_addr, ai_list->ai_addrlen); + + freeaddrinfo(ai_return); + + if (status != 0) /* `connect(2)' failed */ + { + char errbuf[1024]; + INFO("apcups plugin: connect failed: %s", + sstrerror(errno, errbuf, sizeof(errbuf))); + close(sd); + return -1; + } + + DEBUG("apcups plugin: Done opening a socket %i", sd); + + return sd; +} /* int net_open */ /* * Receive a message from the other end. Each message consists of @@ -190,41 +168,37 @@ static int net_open (char *host, 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) -{ - uint16_t packet_size; - - /* get data size -- in short */ - if (sread (*sockfd, (void *) &packet_size, sizeof (packet_size)) != 0) - { - close (*sockfd); - *sockfd = -1; - return (-1); - } - - packet_size = ntohs (packet_size); - if (packet_size > buflen) - { - ERROR ("apcups plugin: Received %"PRIu16" bytes of payload " - "but have only %i bytes of buffer available.", - packet_size, buflen); - close (*sockfd); - *sockfd = -1; - return (-2); - } - - if (packet_size == 0) - return (0); - - /* now read the actual data */ - if (sread (*sockfd, (void *) buf, packet_size) != 0) - { - close (*sockfd); - *sockfd = -1; - return (-1); - } - - return ((int) packet_size); +static int net_recv(int *sockfd, char *buf, int buflen) { + uint16_t packet_size; + + /* get data size -- in short */ + if (sread(*sockfd, (void *)&packet_size, sizeof(packet_size)) != 0) { + close(*sockfd); + *sockfd = -1; + return -1; + } + + packet_size = ntohs(packet_size); + if (packet_size > buflen) { + ERROR("apcups plugin: Received %" PRIu16 " bytes of payload " + "but have only %i bytes of buffer available.", + packet_size, buflen); + close(*sockfd); + *sockfd = -1; + return -2; + } + + if (packet_size == 0) + return 0; + + /* now read the actual data */ + if (sread(*sockfd, (void *)buf, packet_size) != 0) { + close(*sockfd); + *sockfd = -1; + return -1; + } + + return (int)packet_size; } /* static int net_recv (int *sockfd, char *buf, int buflen) */ /* @@ -234,265 +208,245 @@ 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) -{ - uint16_t packet_size; - - assert (len > 0); - assert (*sockfd >= 0); - - /* send short containing size of data packet */ - packet_size = htons ((uint16_t) len); - - if (swrite (*sockfd, (void *) &packet_size, sizeof (packet_size)) != 0) - { - close (*sockfd); - *sockfd = -1; - return (-1); - } - - /* send data packet */ - if (swrite (*sockfd, (void *) buff, len) != 0) - { - close (*sockfd); - *sockfd = -1; - return (-2); - } - - return (0); +static int net_send(int *sockfd, const char *buff, int len) { + uint16_t packet_size; + + assert(len > 0); + assert(*sockfd >= 0); + + /* send short containing size of data packet */ + packet_size = htons((uint16_t)len); + + if (swrite(*sockfd, (void *)&packet_size, sizeof(packet_size)) != 0) { + close(*sockfd); + *sockfd = -1; + return -1; + } + + /* send data packet */ + if (swrite(*sockfd, (void *)buff, len) != 0) { + close(*sockfd); + *sockfd = -1; + return -2; + } + + return 0; } /* Get and print status from apcupsd NIS server */ -static int apc_query_server (char *host, int port, - apc_detail_t *apcups_detail) -{ - int n; - char recvline[1024]; - char *tokptr; - char *toksaveptr; - _Bool retry = 1; - int status; +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; + int try = 0; + int status; #if APCMAIN -# define PRINT_VALUE(name, val) printf(" Found property: name = %s; value = %f;\n", name, val) +#define PRINT_VALUE(name, val) \ + printf(" Found property: name = %s; value = %f;\n", name, val) #else -# define PRINT_VALUE(name, val) /**/ +#define PRINT_VALUE(name, val) /**/ #endif - while (retry) - { - if (global_sockfd < 0) - { - 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); - } - - 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)) - { - 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) - { - assert ((unsigned int)n < sizeof (recvline)); - recvline[n] = '\0'; + while (1) { + if (global_sockfd < 0) { + global_sockfd = net_open(node, service); + 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 closes the socket on error. */ + assert(global_sockfd < 0); + if (try == 0) { + try++; + count_retries++; + continue; + } + + ERROR("apcups plugin: Writing to the socket failed."); + return -1; + } + + break; + } /* while (1) */ + + /* 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)) { + NOTICE("apcups plugin: There have been %i retries in the " + "first %i iterations. Will close the socket " + "in future iterations.", + count_retries, count_iterations); + conf_persistent_conn = 0; + } + + while ((n = net_recv(&global_sockfd, recvline, sizeof(recvline) - 1)) > 0) { + assert((size_t)n < sizeof(recvline)); + recvline[n] = 0; #if APCMAIN - printf ("net_recv = `%s';\n", recvline); + printf("net_recv = `%s';\n", recvline); #endif /* if APCMAIN */ - toksaveptr = NULL; - tokptr = strtok_r (recvline, " :\t", &toksaveptr); - while (tokptr != NULL) - { - char *key = tokptr; - if ((tokptr = strtok_r (NULL, " :\t", &toksaveptr)) == NULL) - continue; - - gauge_t value; - if (strtogauge (tokptr, &value) != 0) - continue; - - PRINT_VALUE (key, value); - - if (strcmp ("LINEV", key) == 0) - apcups_detail->linev = value; - else if (strcmp ("BATTV", key) == 0) - apcups_detail->battv = value; - else if (strcmp ("ITEMP", key) == 0) - apcups_detail->itemp = value; - else if (strcmp ("LOADPCT", key) == 0) - apcups_detail->loadpct = value; - else if (strcmp ("BCHARGE", key) == 0) - apcups_detail->bcharge = value; - else if (strcmp ("OUTPUTV", key) == 0) - apcups_detail->outputv = value; - 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) - { - char errbuf[1024]; - ERROR ("apcups plugin: Reading from socket failed: %s", - sstrerror (status, errbuf, sizeof (errbuf))); - return (-1); - } - - return (0); + toksaveptr = NULL; + tokptr = strtok_r(recvline, " :\t", &toksaveptr); + while (tokptr != NULL) { + char *key = tokptr; + if ((tokptr = strtok_r(NULL, " :\t", &toksaveptr)) == NULL) + continue; + + gauge_t value; + if (strtogauge(tokptr, &value) != 0) + continue; + + PRINT_VALUE(key, value); + + if (strcmp("LINEV", key) == 0) + apcups_detail->linev = value; + else if (strcmp("BATTV", key) == 0) + apcups_detail->battv = value; + else if (strcmp("ITEMP", key) == 0) + apcups_detail->itemp = value; + else if (strcmp("LOADPCT", key) == 0) + apcups_detail->loadpct = value; + else if (strcmp("BCHARGE", key) == 0) + apcups_detail->bcharge = value; + else if (strcmp("OUTPUTV", key) == 0) + apcups_detail->outputv = value; + 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 (!conf_persistent_conn) + net_shutdown(&global_sockfd); + + if (n < 0) { + char errbuf[1024]; + ERROR("apcups plugin: Reading from socket failed: %s", + sstrerror(status, errbuf, sizeof(errbuf))); + return -1; + } + + return 0; } -static int apcups_config (const char *key, const char *value) -{ - 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) - { - int port_tmp = atoi (value); - if (port_tmp < 1 || port_tmp > 65535) - { - WARNING ("apcups plugin: Invalid port: %i", port_tmp); - return (1); - } - 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); - } - return (0); +static int apcups_config(oconfig_item_t *ci) { + _Bool persistent_conn_set = 0; + + for (int i = 0; i < ci->children_num; i++) { + 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; + } else + ERROR("apcups plugin: Unknown config option \"%s\".", child->key); + } + + 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(const char *type, const char *type_inst, + gauge_t value) { + if (isnan(value)) + return; + + value_list_t vl = VALUE_LIST_INIT; + vl.values = &(value_t){.gauge = value}; + vl.values_len = 1; + sstrncpy(vl.plugin, "apcups", sizeof(vl.plugin)); + 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_generic (char *type, char *type_inst, gauge_t value) -{ - value_t values[1]; - value_list_t vl = VALUE_LIST_INIT; - - if (isnan (value)) - return; - - values[0].gauge = value; +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); + apc_submit_generic("voltage", "battery", apcups_detail->battv); + apc_submit_generic("charge", "", apcups_detail->bcharge); + apc_submit_generic("percent", "load", apcups_detail->loadpct); + apc_submit_generic("timeleft", "", apcups_detail->timeleft); + apc_submit_generic("temperature", "", apcups_detail->itemp); + apc_submit_generic("frequency", "input", apcups_detail->linefreq); +} - vl.values = values; - 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)); +static int apcups_read(void) { + 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, conf_service, &apcups_detail); + + if (status != 0) { + DEBUG("apcups plugin: apc_query_server (\"%s\", \"%s\") = %d", + conf_node, conf_service, status); + return status; + } + + apc_submit(&apcups_detail); + + return 0; +} /* apcups_read */ - plugin_dispatch_values (&vl); -} +static int apcups_init(void) { + if (conf_node == NULL) + conf_node = APCUPS_DEFAULT_NODE; -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); - apc_submit_generic ("voltage", "battery", apcups_detail->battv); - apc_submit_generic ("charge", "", apcups_detail->bcharge); - apc_submit_generic ("percent", "load", apcups_detail->loadpct); - apc_submit_generic ("timeleft", "", apcups_detail->timeleft); - apc_submit_generic ("temperature", "", apcups_detail->itemp); - apc_submit_generic ("frequency", "input", apcups_detail->linefreq); -} + if (conf_service == NULL) + conf_service = APCUPS_DEFAULT_SERVICE; -static int apcups_read (void) -{ - 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_host == NULL - ? APCUPS_DEFAULT_HOST - : conf_host, - conf_port, &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 (status); - } - - apc_submit (&apcups_detail); - - return (0); -} /* apcups_read */ + return 0; +} /* apcups_init */ -void module_register (void) -{ - plugin_register_config ("apcups", apcups_config, config_keys, - config_keys_num); - plugin_register_read ("apcups", apcups_read); - plugin_register_shutdown ("apcups", apcups_shutdown); +void module_register(void) { + plugin_register_complex_config("apcups", apcups_config); + plugin_register_init("apcups", apcups_init); + plugin_register_read("apcups", apcups_read); + plugin_register_shutdown("apcups", apcups_shutdown); } /* void module_register */