* Aman Gupta <aman at tmm1.net>
**/
+#define _DEFAULT_SOURCE
#define _BSD_SOURCE /* For struct ip_mreq */
#include "collectd.h"
/* Re enable deprecation warnings */
# pragma GCC diagnostic warning "-Wdeprecated-declarations"
# endif
+# if GCRYPT_VERSION_NUMBER < 0x010600
GCRY_THREAD_OPTION_PTHREAD_IMPL;
+# endif
#endif
#ifndef IPV6_ADD_MEMBERSHIP
gcry_cipher_hd_t cypher;
unsigned char password_hash[32];
#endif
+ cdtime_t next_resolve_reconnect;
+ cdtime_t resolve_interval;
};
struct sockent_server
static char *send_buffer;
static char *send_buffer_ptr;
static int send_buffer_fill;
+static cdtime_t send_buffer_last_update;
static value_list_t send_buffer_vl = VALUE_LIST_STATIC;
static pthread_mutex_t send_buffer_lock = PTHREAD_MUTEX_INITIALIZER;
* above doesn't count, as it doesn't implicitly initalize Libgcrypt.
*
* tl;dr: keep all these gry_* statements in this exact order please. */
+# if GCRYPT_VERSION_NUMBER < 0x010600
gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
+# endif
gcry_check_version (NULL);
gcry_control (GCRYCTL_INIT_SECMEM, 32768);
gcry_control (GCRYCTL_INITIALIZATION_FINISHED);
} /* int parse_part_number */
static int parse_part_string (void **ret_buffer, size_t *ret_buffer_len,
- char *output, int output_len)
+ char *output, size_t const output_len)
{
char *buffer = *ret_buffer;
size_t buffer_len = *ret_buffer_len;
uint16_t tmp16;
- size_t header_size = 2 * sizeof (uint16_t);
+ size_t const header_size = 2 * sizeof (uint16_t);
uint16_t pkg_length;
+ size_t payload_size;
+
+ if (output_len <= 0)
+ return (EINVAL);
if (buffer_len < header_size)
{
memcpy ((void *) &tmp16, buffer, sizeof (tmp16));
buffer += sizeof (tmp16);
pkg_length = ntohs (tmp16);
+ payload_size = ((size_t) pkg_length) - header_size;
/* Check that packet fits in the input buffer */
if (pkg_length > buffer_len)
/* Check that the package data fits into the output buffer.
* The previous if-statement ensures that:
* `pkg_length > header_size' */
- if ((output_len < 0)
- || ((size_t) output_len < ((size_t) pkg_length - header_size)))
+ if (output_len < payload_size)
{
WARNING ("network plugin: parse_part_string: "
- "Output buffer too small.");
+ "Buffer too small: "
+ "Output buffer holds %zu bytes, "
+ "which is too small to hold the received "
+ "%zu byte string.",
+ output_len, payload_size);
return (-1);
}
/* All sanity checks successfull, let's copy the data over */
- output_len = pkg_length - header_size;
- memcpy ((void *) output, (void *) buffer, output_len);
- buffer += output_len;
+ memcpy ((void *) output, (void *) buffer, payload_size);
+ buffer += payload_size;
/* For some very weird reason '\0' doesn't do the trick on SPARC in
* this statement. */
- if (output[output_len - 1] != 0)
+ if (output[payload_size - 1] != 0)
{
WARNING ("network plugin: parse_part_string: "
"Received string does not end "
{
sockent_t *se;
- if ((type != SOCKENT_TYPE_CLIENT) || (type != SOCKENT_TYPE_SERVER))
+ if ((type != SOCKENT_TYPE_CLIENT) && (type != SOCKENT_TYPE_SERVER))
return (NULL);
se = malloc (sizeof (*se));
{
se->data.client.fd = -1;
se->data.client.addr = NULL;
+ se->data.client.resolve_interval = 0;
+ se->data.client.next_resolve_reconnect = 0;
#if HAVE_LIBGCRYPT
se->data.client.security_level = SECURITY_LEVEL_NONE;
se->data.client.username = NULL;
return (0);
} /* }}} int sockent_init_crypto */
+static int sockent_client_disconnect (sockent_t *se) /* {{{ */
+{
+ struct sockent_client *client;
+
+ if ((se == NULL) || (se->type != SOCKENT_TYPE_CLIENT))
+ return (EINVAL);
+
+ client = &se->data.client;
+ if (client->fd >= 0) /* connected */
+ {
+ close (client->fd);
+ client->fd = -1;
+ }
+
+ sfree (client->addr);
+ client->addrlen = 0;
+
+ return (0);
+} /* }}} int sockent_client_disconnect */
+
static int sockent_client_connect (sockent_t *se) /* {{{ */
{
static c_complain_t complaint = C_COMPLAIN_INIT_STATIC;
struct addrinfo ai_hints;
struct addrinfo *ai_list = NULL, *ai_ptr;
int status;
+ _Bool reconnect = 0;
+ cdtime_t now;
if ((se == NULL) || (se->type != SOCKENT_TYPE_CLIENT))
return (EINVAL);
client = &se->data.client;
- if (client->fd >= 0) /* already connected */
+
+ now = cdtime ();
+ if (client->resolve_interval != 0 && client->next_resolve_reconnect < now) {
+ DEBUG("network plugin: Reconnecting socket, resolve_interval = %lf, next_resolve_reconnect = %lf",
+ CDTIME_T_TO_DOUBLE(client->resolve_interval), CDTIME_T_TO_DOUBLE(client->next_resolve_reconnect));
+ reconnect = 1;
+ }
+
+ if (client->fd >= 0 && !reconnect) /* already connected and not stale*/
return (0);
memset (&ai_hints, 0, sizeof (ai_hints));
for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
{
+ if (client->fd >= 0) /* when we reconnect */
+ sockent_client_disconnect(se);
+
client->fd = socket (ai_ptr->ai_family,
ai_ptr->ai_socktype,
ai_ptr->ai_protocol);
freeaddrinfo (ai_list);
if (client->fd < 0)
return (-1);
- return (0);
-} /* }}} int sockent_client_connect */
-
-static int sockent_client_disconnect (sockent_t *se) /* {{{ */
-{
- struct sockent_client *client;
-
- if ((se == NULL) || (se->type != SOCKENT_TYPE_CLIENT))
- return (EINVAL);
-
- client = &se->data.client;
- if (client->fd >= 0) /* connected */
- {
- close (client->fd);
- client->fd = -1;
- }
-
- sfree (client->addr);
- client->addrlen = 0;
+ if (client->resolve_interval > 0)
+ client->next_resolve_reconnect = now + client->resolve_interval;
return (0);
-} /* }}} int sockent_client_disconnect */
+} /* }}} int sockent_client_connect */
/* Open the file descriptors for a initialized sockent structure. */
static int sockent_server_listen (sockent_t *se) /* {{{ */
memset (send_buffer, 0, network_config_packet_size);
send_buffer_ptr = send_buffer;
send_buffer_fill = 0;
+ send_buffer_last_update = 0;
memset (&send_buffer_vl, 0, sizeof (send_buffer_vl));
} /* int network_init_buffer */
/* status == bytes added to the buffer */
send_buffer_fill += status;
send_buffer_ptr += status;
+ send_buffer_last_update = cdtime();
stats_values_sent++;
}
tmp = (int) ci->values[0].value.number;
if ((tmp > 0) && (tmp <= 255))
network_config_ttl = tmp;
+ else {
+ WARNING ("network plugin: The `TimeToLive' must be between 1 and 255.");
+ return (-1);
+ }
return (0);
} /* }}} int network_config_set_ttl */
if (strcasecmp ("Interface", child->key) == 0)
network_config_set_interface (child,
&se->interface);
+ else if (strcasecmp ("ResolveInterval", child->key) == 0)
+ cf_util_get_cdtime(child, &se->data.client.resolve_interval);
else
{
WARNING ("network plugin: Option `%s' is not allowed here.",
{
int i;
+ /* The options need to be applied first */
+ for (i = 0; i < ci->children_num; i++)
+ {
+ oconfig_item_t *child = ci->children + i;
+ if (strcasecmp ("TimeToLive", child->key) == 0)
+ network_config_set_ttl (child);
+ }
+
for (i = 0; i < ci->children_num; i++)
{
oconfig_item_t *child = ci->children + i;
network_config_add_listen (child);
else if (strcasecmp ("Server", child->key) == 0)
network_config_add_server (child);
- else if (strcasecmp ("TimeToLive", child->key) == 0)
- network_config_set_ttl (child);
+ else if (strcasecmp ("TimeToLive", child->key) == 0) {
+ /* Handled earlier */
+ }
else if (strcasecmp ("MaxPacketSize", child->key) == 0)
network_config_set_buffer_size (child);
else if (strcasecmp ("Forward", child->key) == 0)
* just send the buffer if `flush' is called - if the requested value was in
* there, good. If not, well, then there is nothing to flush.. -octo
*/
-static int network_flush (__attribute__((unused)) cdtime_t timeout,
+static int network_flush (cdtime_t timeout,
__attribute__((unused)) const char *identifier,
__attribute__((unused)) user_data_t *user_data)
{
pthread_mutex_lock (&send_buffer_lock);
if (send_buffer_fill > 0)
- flush_buffer ();
-
+ {
+ if (timeout > 0)
+ {
+ cdtime_t now = cdtime ();
+ if ((send_buffer_last_update + timeout) > now)
+ {
+ pthread_mutex_unlock (&send_buffer_lock);
+ return (0);
+ }
+ }
+ flush_buffer ();
+ }
pthread_mutex_unlock (&send_buffer_lock);
return (0);