X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=src%2Fnetwork.c;h=a659189f3e7960d312d0d608f9f8af55d1e3c5bb;hb=acbd25344a59c091b087311804d375b759077e32;hp=4516331e9096507ae12eeb89c670d2ee242c58a6;hpb=634504760b46b852ec2b812a7b68277e9c005f1b;p=collectd.git diff --git a/src/network.c b/src/network.c index 4516331e..a659189f 100644 --- a/src/network.c +++ b/src/network.c @@ -1,6 +1,6 @@ /** * collectd - src/network.c - * Copyright (C) 2005,2006 Florian octo Forster + * Copyright (C) 2005-2007 Florian octo Forster * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -23,7 +23,7 @@ #include "plugin.h" #include "common.h" #include "configfile.h" -#include "utils_debug.h" +#include "utils_avltree.h" #include "network.h" @@ -57,7 +57,7 @@ # endif #endif /* !IP_ADD_MEMBERSHIP */ -#define BUFF_SIZE 4096 +#define BUFF_SIZE 1024 /* * Private data types @@ -98,6 +98,21 @@ struct part_string_s }; typedef struct part_string_s part_string_t; +/* 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-------------------------------+-------------------------------+ + * ! Type ! Length ! + * +-------------------------------+-------------------------------+ + * : (Length - 4 == 2 || 4 || 8) Bytes : + * +---------------------------------------------------------------+ + */ +struct part_number_s +{ + part_header_t *head; + uint64_t *value; +}; +typedef struct part_number_s part_number_t; + /* 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-------------------------------+-------------------------------+ @@ -126,14 +141,16 @@ typedef struct part_values_s part_values_t; */ static const char *config_keys[] = { + "CacheFlush", "Listen", "Server", "TimeToLive", - NULL + "Forward" }; -static int config_keys_num = 3; +static int config_keys_num = STATIC_ARRAY_SIZE (config_keys); static int network_config_ttl = 0; +static int network_config_forward = 0; static sockent_t *sending_sockets = NULL; @@ -142,9 +159,135 @@ static int listen_sockets_num = 0; static pthread_t listen_thread = 0; static int listen_loop = 0; +static char send_buffer[BUFF_SIZE]; +static char *send_buffer_ptr; +static int send_buffer_fill; +static value_list_t send_buffer_vl = VALUE_LIST_STATIC; +static char send_buffer_type[DATA_MAX_NAME_LEN]; +static pthread_mutex_t send_buffer_lock = PTHREAD_MUTEX_INITIALIZER; + +static avl_tree_t *cache_tree = NULL; +static pthread_mutex_t cache_lock = PTHREAD_MUTEX_INITIALIZER; +static time_t cache_flush_last; +static int cache_flush_interval = 1800; + /* * Private functions */ +static int cache_flush (void) +{ + char **keys = NULL; + int keys_num = 0; + + char **tmp; + int i; + + char *key; + time_t *value; + avl_iterator_t *iter; + + time_t curtime = time (NULL); + + iter = avl_get_iterator (cache_tree); + while (avl_iterator_next (iter, (void *) &key, (void *) &value) == 0) + { + if ((curtime - *value) <= cache_flush_interval) + continue; + tmp = (char **) realloc (keys, + (keys_num + 1) * sizeof (char *)); + if (tmp == NULL) + { + sfree (keys); + avl_iterator_destroy (iter); + ERROR ("network plugin: cache_flush: realloc" + " failed."); + return (-1); + } + keys = tmp; + keys[keys_num] = key; + keys_num++; + } /* while (avl_iterator_next) */ + avl_iterator_destroy (iter); + + for (i = 0; i < keys_num; i++) + { + if (avl_remove (cache_tree, keys[i], (void *) &key, + (void *) &value) != 0) + { + WARNING ("network plugin: cache_flush: avl_remove" + " (%s) failed.", keys[i]); + continue; + } + + sfree (key); + sfree (value); + } + + sfree (keys); + + DEBUG ("network plugin: cache_flush: Removed %i %s", + keys_num, (keys_num == 1) ? "entry" : "entries"); + cache_flush_last = curtime; + return (0); +} /* int cache_flush */ + +static int cache_check (const char *type, const value_list_t *vl) +{ + char key[1024]; + time_t *value = NULL; + int retval = -1; + + if (cache_tree == NULL) + return (-1); + + if (format_name (key, sizeof (key), vl->host, vl->plugin, + vl->plugin_instance, type, vl->type_instance)) + return (-1); + + pthread_mutex_lock (&cache_lock); + + if (avl_get (cache_tree, key, (void *) &value) == 0) + { + if (*value < vl->time) + { + *value = vl->time; + retval = 0; + } + else + { + DEBUG ("network plugin: cache_check: *value = %i >= vl->time = %i", + (int) *value, (int) vl->time); + retval = 1; + } + } + else + { + char *key_copy = strdup (key); + value = malloc (sizeof (time_t)); + if ((key_copy != NULL) && (value != NULL)) + { + *value = vl->time; + avl_insert (cache_tree, key_copy, value); + retval = 0; + } + else + { + sfree (key_copy); + sfree (value); + } + } + + if ((time (NULL) - cache_flush_last) > cache_flush_interval) + cache_flush (); + + pthread_mutex_unlock (&cache_lock); + + DEBUG ("network plugin: cache_check: key = %s; time = %i; retval = %i", + key, (int) vl->time, retval); + + return (retval); +} /* int cache_check */ + static int write_part_values (char **ret_buffer, int *ret_buffer_len, const data_set_t *ds, const value_list_t *vl) { @@ -183,15 +326,33 @@ static int write_part_values (char **ret_buffer, int *ret_buffer_len, return (0); } /* int write_part_values */ +static int write_part_number (char **ret_buffer, int *ret_buffer_len, + int type, uint64_t value) +{ + part_number_t pn; + + if (*ret_buffer_len < 12) + return (-1); + + pn.head = (part_header_t *) *ret_buffer; + pn.value = (uint64_t *) (pn.head + 1); + + pn.head->type = htons (type); + pn.head->length = htons (12); + *pn.value = htonll (value); + + *ret_buffer = (char *) (pn.value + 1); + *ret_buffer_len -= 12; + + return (0); +} /* int write_part_number */ + static int write_part_string (char **ret_buffer, int *ret_buffer_len, int type, const char *str, int str_len) { part_string_t ps; int len; - if (str_len < 1) - return (-1); - len = 4 + str_len + 1; if (*ret_buffer_len < len) return (-1); @@ -201,10 +362,11 @@ static int write_part_string (char **ret_buffer, int *ret_buffer_len, ps.value = (char *) (ps.head + 1); ps.head->type = htons ((uint16_t) type); - ps.head->length = htons ((uint16_t) str_len + 4); - memcpy (ps.value, str, str_len); + ps.head->length = htons ((uint16_t) str_len + 5); + if (str_len > 0) + memcpy (ps.value, str, str_len); ps.value[str_len] = '\0'; - *ret_buffer = (void *) (ps.value + str_len); + *ret_buffer = (void *) (ps.value + (str_len + 1)); return (0); } /* int write_part_string */ @@ -214,83 +376,124 @@ static int parse_part_values (void **ret_buffer, int *ret_buffer_len, { char *buffer = *ret_buffer; int buffer_len = *ret_buffer_len; - part_values_t *pvalues; + part_values_t pv; int i; + uint16_t h_length; + uint16_t h_type; + uint16_t h_num; + if (buffer_len < (15)) { - DBG ("packet is too short"); + DEBUG ("network plugin: packet is too short: buffer_len = %i", + buffer_len); return (-1); } - pvalues = (part_values_t *) malloc (sizeof (part_values_t)); - if (pvalues == NULL) - return (-1); + pv.head = (part_header_t *) buffer; + h_length = ntohs (pv.head->length); + h_type = ntohs (pv.head->type); + + assert (h_type == TYPE_VALUES); - pvalues->head = (part_header_t *) buffer; - assert (pvalues->head->type == htons (TYPE_VALUES)); + pv.num_values = (uint16_t *) (pv.head + 1); + h_num = ntohs (*pv.num_values); - pvalues->num_values = (uint16_t *) (buffer + 4); - if (ntohs (*pvalues->num_values) - != ((ntohs (pvalues->head->length) - 6) / 9)) + if (h_num != ((h_length - 6) / 9)) { - DBG ("`length' and `num of values' don't match"); - free (pvalues); + DEBUG ("`length' and `num of values' don't match"); return (-1); } - pvalues->values_types = (uint8_t *) (buffer + 6); - pvalues->values = (value_t *) (buffer + 6 + *pvalues->num_values); - - for (i = 0; i < *pvalues->num_values; i++) - if (pvalues->values_types[i] == DS_TYPE_COUNTER) - pvalues->values[i].counter = ntohll (pvalues->values[i].counter); + pv.values_types = (uint8_t *) (pv.num_values + 1); + pv.values = (value_t *) (pv.values_types + h_num); - *ret_buffer = (void *) buffer; - *ret_buffer_len = buffer_len - pvalues->head->length; - *ret_num_values = *pvalues->num_values; - *ret_values = pvalues->values; + for (i = 0; i < h_num; i++) + if (pv.values_types[i] == DS_TYPE_COUNTER) + pv.values[i].counter = ntohll (pv.values[i].counter); - free (pvalues); + *ret_buffer = (void *) (pv.values + h_num); + *ret_buffer_len = buffer_len - h_length; + *ret_num_values = h_num; + *ret_values = pv.values; return (0); } /* int parse_part_values */ +static int parse_part_number (void **ret_buffer, int *ret_buffer_len, + uint64_t *value) +{ + part_number_t pn; + uint16_t len; + + pn.head = (part_header_t *) *ret_buffer; + pn.value = (uint64_t *) (pn.head + 1); + + len = ntohs (pn.head->length); + if (len != 12) + return (-1); + if (len > *ret_buffer_len) + return (-1); + *value = ntohll (*pn.value); + + *ret_buffer = (void *) (pn.value + 1); + *ret_buffer_len -= len; + + return (0); +} /* int parse_part_number */ + static int parse_part_string (void **ret_buffer, int *ret_buffer_len, char *output, int output_len) { char *buffer = *ret_buffer; int buffer_len = *ret_buffer_len; - part_string_t part_string; + part_string_t ps; + + uint16_t h_length; + uint16_t h_type; - part_string.head = (part_header_t *) buffer; - if (buffer_len < part_string.head->length) + DEBUG ("network plugin: parse_part_string: ret_buffer = %p;" + " ret_buffer_len = %i; output = %p; output_len = %i;", + *ret_buffer, *ret_buffer_len, + (void *) output, output_len); + + ps.head = (part_header_t *) buffer; + + h_length = ntohs (ps.head->length); + h_type = ntohs (ps.head->type); + + DEBUG ("network plugin: parse_part_string: length = %hu; type = %hu;", + h_length, h_type); + + if (buffer_len < h_length) { - DBG ("packet is too short"); + DEBUG ("packet is too short"); return (-1); } - assert ((part_string.head->type == htons (TYPE_HOST)) - || (part_string.head->type == htons (TYPE_PLUGIN)) - || (part_string.head->type == htons (TYPE_PLUGIN_INSTANCE)) - || (part_string.head->type == htons (TYPE_TYPE)) - || (part_string.head->type == htons (TYPE_TYPE_INSTANCE))); - - part_string.value = buffer + 4; - if (part_string.value[part_string.head->length - 5] != '\0') + assert ((h_type == TYPE_HOST) + || (h_type == TYPE_PLUGIN) + || (h_type == TYPE_PLUGIN_INSTANCE) + || (h_type == TYPE_TYPE) + || (h_type == TYPE_TYPE_INSTANCE)); + + ps.value = buffer + 4; + if (ps.value[h_length - 5] != '\0') { - DBG ("String does not end with a nullbyte"); + DEBUG ("String does not end with a nullbyte"); return (-1); } - if (output_len < (part_string.head->length - 4)) + if (output_len < (h_length - 4)) { - DBG ("output buffer is too small"); + DEBUG ("output buffer is too small"); return (-1); } - strcpy (output, part_string.value); + strcpy (output, ps.value); + + DEBUG ("network plugin: parse_part_string: output = %s", output); - *ret_buffer = (void *) (buffer + part_string.head->length); - *ret_buffer_len = buffer_len - part_string.head->length; + *ret_buffer = (void *) (buffer + h_length); + *ret_buffer_len = buffer_len - h_length; return (0); } /* int parse_part_string */ @@ -300,59 +503,102 @@ static int parse_packet (void *buffer, int buffer_len) part_header_t *header; int status; - value_list_t vl; + value_list_t vl = VALUE_LIST_INIT; char type[DATA_MAX_NAME_LEN]; + DEBUG ("network plugin: parse_packet: buffer = %p; buffer_len = %i;", + buffer, buffer_len); + memset (&vl, '\0', sizeof (vl)); memset (&type, '\0', sizeof (type)); + status = 0; - while (buffer_len > sizeof (part_header_t)) + while ((status == 0) && (buffer_len > sizeof (part_header_t))) { header = (part_header_t *) buffer; - if (header->length > buffer_len) + if (ntohs (header->length) > buffer_len) + break; + /* Assure that this loop terminates eventually */ + if (ntohs (header->length) < 4) break; - if (header->type == TYPE_VALUES) + if (ntohs (header->type) == TYPE_VALUES) { status = parse_part_values (&buffer, &buffer_len, &vl.values, &vl.values_len); - if ((status == 0) + if (status != 0) + { + DEBUG ("parse_part_values failed."); + break; + } + + if ((vl.time > 0) && (strlen (vl.host) > 0) && (strlen (vl.plugin) > 0) - && (strlen (type) > 0)) + && (strlen (type) > 0) + && (cache_check (type, &vl) == 0)) + { + DEBUG ("network plugin: parse_packet:" + " dispatching values"); plugin_dispatch_values (type, &vl); + } + else + { + DEBUG ("network plugin: parse_packet:" + " NOT dispatching values"); + } + } + else if (ntohs (header->type) == TYPE_TIME) + { + uint64_t tmp = 0; + status = parse_part_number (&buffer, &buffer_len, &tmp); + if (status == 0) + vl.time = (time_t) tmp; } - else if (header->type == TYPE_HOST) + else if (ntohs (header->type) == TYPE_INTERVAL) + { + uint64_t tmp = 0; + status = parse_part_number (&buffer, &buffer_len, &tmp); + if (status == 0) + vl.interval = (int) tmp; + } + else if (ntohs (header->type) == TYPE_HOST) { status = parse_part_string (&buffer, &buffer_len, vl.host, sizeof (vl.host)); + DEBUG ("network plugin: parse_packet: vl.host = %s", vl.host); } - else if (header->type == TYPE_PLUGIN) + else if (ntohs (header->type) == TYPE_PLUGIN) { status = parse_part_string (&buffer, &buffer_len, vl.plugin, sizeof (vl.plugin)); + DEBUG ("network plugin: parse_packet: vl.plugin = %s", vl.plugin); } - else if (header->type == TYPE_PLUGIN_INSTANCE) + else if (ntohs (header->type) == TYPE_PLUGIN_INSTANCE) { status = parse_part_string (&buffer, &buffer_len, vl.plugin_instance, sizeof (vl.plugin_instance)); + DEBUG ("network plugin: parse_packet: vl.plugin_instance = %s", vl.plugin_instance); } - else if (header->type == TYPE_TYPE) + else if (ntohs (header->type) == TYPE_TYPE) { status = parse_part_string (&buffer, &buffer_len, type, sizeof (type)); + DEBUG ("network plugin: parse_packet: type = %s", type); } - else if (header->type == TYPE_TYPE_INSTANCE) + else if (ntohs (header->type) == TYPE_TYPE_INSTANCE) { status = parse_part_string (&buffer, &buffer_len, vl.type_instance, sizeof (vl.type_instance)); + DEBUG ("network plugin: parse_packet: vl.type_instance = %s", vl.type_instance); } else { - DBG ("Unknown part type: 0x%0hx", header->type); - buffer = ((char *) buffer) + header->length; + DEBUG ("network plugin: parse_packet: Unknown part" + " type: 0x%0hx", ntohs (header->type)); + buffer = ((char *) buffer) + ntohs (header->length); } } /* while (buffer_len > sizeof (part_header_t)) */ @@ -385,7 +631,7 @@ static int network_set_ttl (const sockent_t *se, const struct addrinfo *ai) if ((network_config_ttl < 1) || (network_config_ttl > 255)) return (-1); - DBG ("ttl = %i", network_config_ttl); + DEBUG ("ttl = %i", network_config_ttl); if (ai->ai_family == AF_INET) { @@ -401,7 +647,9 @@ static int network_set_ttl (const sockent_t *se, const struct addrinfo *ai) &network_config_ttl, sizeof (network_config_ttl)) == -1) { - syslog (LOG_ERR, "setsockopt: %s", strerror (errno)); + char errbuf[1024]; + ERROR ("setsockopt: %s", + sstrerror (errno, errbuf, sizeof (errbuf))); return (-1); } } @@ -420,7 +668,10 @@ static int network_set_ttl (const sockent_t *se, const struct addrinfo *ai) &network_config_ttl, sizeof (network_config_ttl)) == -1) { - syslog (LOG_ERR, "setsockopt: %s", strerror (errno)); + char errbuf[1024]; + ERROR ("setsockopt: %s", + sstrerror (errno, errbuf, + sizeof (errbuf))); return (-1); } } @@ -430,13 +681,25 @@ static int network_set_ttl (const sockent_t *se, const struct addrinfo *ai) static int network_bind_socket (const sockent_t *se, const struct addrinfo *ai) { - int loop = 1; + int loop = 0; + int yes = 1; + + /* allow multiple sockets to use the same PORT number */ + if (setsockopt(se->fd, SOL_SOCKET, SO_REUSEADDR, + &yes, sizeof(yes)) == -1) { + char errbuf[1024]; + ERROR ("setsockopt: %s", + sstrerror (errno, errbuf, sizeof (errbuf))); + return (-1); + } - DBG ("fd = %i; calling `bind'", se->fd); + DEBUG ("fd = %i; calling `bind'", se->fd); if (bind (se->fd, ai->ai_addr, ai->ai_addrlen) == -1) { - syslog (LOG_ERR, "bind: %s", strerror (errno)); + char errbuf[1024]; + ERROR ("bind: %s", + sstrerror (errno, errbuf, sizeof (errbuf))); return (-1); } @@ -447,7 +710,7 @@ static int network_bind_socket (const sockent_t *se, const struct addrinfo *ai) { struct ip_mreq mreq; - DBG ("fd = %i; IPv4 multicast address found", se->fd); + DEBUG ("fd = %i; IPv4 multicast address found", se->fd); mreq.imr_multiaddr.s_addr = addr->sin_addr.s_addr; mreq.imr_interface.s_addr = htonl (INADDR_ANY); @@ -455,14 +718,20 @@ static int network_bind_socket (const sockent_t *se, const struct addrinfo *ai) if (setsockopt (se->fd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof (loop)) == -1) { - syslog (LOG_ERR, "setsockopt: %s", strerror (errno)); + char errbuf[1024]; + ERROR ("setsockopt: %s", + sstrerror (errno, errbuf, + sizeof (errbuf))); return (-1); } if (setsockopt (se->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof (mreq)) == -1) { - syslog (LOG_ERR, "setsockopt: %s", strerror (errno)); + char errbuf[1024]; + ERROR ("setsockopt: %s", + sstrerror (errno, errbuf, + sizeof (errbuf))); return (-1); } } @@ -475,7 +744,7 @@ static int network_bind_socket (const sockent_t *se, const struct addrinfo *ai) { struct ipv6_mreq mreq; - DBG ("fd = %i; IPv6 multicast address found", se->fd); + DEBUG ("fd = %i; IPv6 multicast address found", se->fd); memcpy (&mreq.ipv6mr_multiaddr, &addr->sin6_addr, @@ -495,14 +764,20 @@ static int network_bind_socket (const sockent_t *se, const struct addrinfo *ai) if (setsockopt (se->fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &loop, sizeof (loop)) == -1) { - syslog (LOG_ERR, "setsockopt: %s", strerror (errno)); + char errbuf[1024]; + ERROR ("setsockopt: %s", + sstrerror (errno, errbuf, + sizeof (errbuf))); return (-1); } if (setsockopt (se->fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof (mreq)) == -1) { - syslog (LOG_ERR, "setsockopt: %s", strerror (errno)); + char errbuf[1024]; + ERROR ("setsockopt: %s", + sstrerror (errno, errbuf, + sizeof (errbuf))); return (-1); } } @@ -522,7 +797,7 @@ static sockent_t *network_create_socket (const char *node, sockent_t *se_head = NULL; sockent_t *se_tail = NULL; - DBG ("node = %s, service = %s", node, service); + DEBUG ("node = %s, service = %s", node, service); memset (&ai_hints, '\0', sizeof (ai_hints)); ai_hints.ai_flags = 0; @@ -539,11 +814,12 @@ static sockent_t *network_create_socket (const char *node, ai_return = getaddrinfo (node, service, &ai_hints, &ai_list); if (ai_return != 0) { - syslog (LOG_ERR, "getaddrinfo (%s, %s): %s", + char errbuf[1024]; + ERROR ("getaddrinfo (%s, %s): %s", (node == NULL) ? "(null)" : node, (service == NULL) ? "(null)" : service, (ai_return == EAI_SYSTEM) - ? strerror (errno) + ? sstrerror (errno, errbuf, sizeof (errbuf)) : gai_strerror (ai_return)); return (NULL); } @@ -554,13 +830,19 @@ static sockent_t *network_create_socket (const char *node, if ((se = (sockent_t *) malloc (sizeof (sockent_t))) == NULL) { - syslog (LOG_EMERG, "malloc: %s", strerror (errno)); + char errbuf[1024]; + ERROR ("malloc: %s", + sstrerror (errno, errbuf, + sizeof (errbuf))); continue; } if ((se->addr = (struct sockaddr_storage *) malloc (sizeof (struct sockaddr_storage))) == NULL) { - syslog (LOG_EMERG, "malloc: %s", strerror (errno)); + char errbuf[1024]; + ERROR ("malloc: %s", + sstrerror (errno, errbuf, + sizeof (errbuf))); free (se); continue; } @@ -577,7 +859,10 @@ static sockent_t *network_create_socket (const char *node, if (se->fd == -1) { - syslog (LOG_ERR, "socket: %s", strerror (errno)); + char errbuf[1024]; + ERROR ("socket: %s", + sstrerror (errno, errbuf, + sizeof (errbuf))); free (se->addr); free (se); continue; @@ -587,6 +872,7 @@ static sockent_t *network_create_socket (const char *node, { if (network_bind_socket (se, ai_ptr) != 0) { + close (se->fd); free (se->addr); free (se); continue; @@ -726,7 +1012,7 @@ int network_receive (void) if (listen_sockets_num == 0) { - syslog (LOG_ERR, "network: Failed to open a listening socket."); + ERROR ("network: Failed to open a listening socket."); return (-1); } @@ -736,10 +1022,11 @@ int network_receive (void) if (status <= 0) { + char errbuf[1024]; if (errno == EINTR) continue; - syslog (LOG_ERR, "poll failed: %s", - strerror (errno)); + ERROR ("poll failed: %s", + sstrerror (errno, errbuf, sizeof (errbuf))); return (-1); } @@ -754,7 +1041,10 @@ int network_receive (void) 0 /* no flags */); if (buffer_len < 0) { - syslog (LOG_ERR, "recv failed: %s", strerror (errno)); + char errbuf[1024]; + ERROR ("recv failed: %s", + sstrerror (errno, errbuf, + sizeof (errbuf))); return (-1); } @@ -767,131 +1057,174 @@ int network_receive (void) static void *receive_thread (void *arg) { - return ((void *) network_receive ()); + return (network_receive () ? (void *) 1 : (void *) 0); } /* void *receive_thread */ -#if 0 -int network_send (char *type, char *inst, char *value) +static void network_send_buffer (const char *buffer, int buffer_len) { - char buf[BUFF_SIZE]; - int buflen; - sockent_t *se; - - int ret; int status; - DBG ("type = %s, inst = %s, value = %s", type, inst, value); - - assert (operating_mode == MODE_CLIENT); - - buflen = snprintf (buf, BUFF_SIZE, "%s %s %s", type, inst, value); - if ((buflen >= BUFF_SIZE) || (buflen < 1)) - { - syslog (LOG_WARNING, "network_send: snprintf failed.."); - return (-1); - } - buf[buflen] = '\0'; - buflen++; - - if (socklist_head == NULL) - network_create_default_socket (0 /* listen == false */); + DEBUG ("network plugin: network_send_buffer: buffer_len = %i", buffer_len); - ret = 0; - for (se = socklist_head; se != NULL; se = se->next) + for (se = sending_sockets; se != NULL; se = se->next) { - while (1) + while (42) { - status = sendto (se->fd, buf, buflen, 0, + status = sendto (se->fd, buffer, buffer_len, 0 /* no flags */, (struct sockaddr *) se->addr, se->addrlen); - - if (status == -1) + if (status < 0) { + char errbuf[1024]; if (errno == EINTR) - { - DBG ("sendto was interrupted"); continue; - } - else - { - syslog (LOG_ERR, "sendto: %s", strerror (errno)); - ret = -1; - break; - } + ERROR ("network plugin: sendto failed: %s", + sstrerror (errno, errbuf, + sizeof (errbuf))); + break; } - else if (ret >= 0) - ret++; + break; - } - } + } /* while (42) */ + } /* for (sending_sockets) */ +} /* void network_send_buffer */ - if (ret == 0) - syslog (LOG_WARNING, "Message wasn't sent to anybody.."); +static int add_to_buffer (char *buffer, int buffer_size, + value_list_t *vl_def, char *type_def, + const data_set_t *ds, const value_list_t *vl) +{ + char *buffer_orig = buffer; - return (ret); -} /* int network_send */ -#endif + if (strcmp (vl_def->host, vl->host) != 0) + { + if (write_part_string (&buffer, &buffer_size, TYPE_HOST, + vl->host, strlen (vl->host)) != 0) + return (-1); + strcpy (vl_def->host, vl->host); + } -static int network_write (const data_set_t *ds, const value_list_t *vl) -{ - char buf[BUFF_SIZE]; - char *buf_ptr; - int buf_len; + if (vl_def->time != vl->time) + { + if (write_part_number (&buffer, &buffer_size, TYPE_TIME, + (uint64_t) vl->time)) + return (-1); + vl_def->time = vl->time; + } - sockent_t *se; + if (vl_def->interval != vl->interval) + { + if (write_part_number (&buffer, &buffer_size, TYPE_INTERVAL, + (uint64_t) vl->interval)) + return (-1); + vl_def->interval = vl->interval; + } - DBG ("host = %s; plugin = %s; plugin_instance = %s; type = %s; type_instance = %s;", - vl->host, vl->plugin, vl->plugin_instance, ds->type, vl->type_instance); + if (strcmp (vl_def->plugin, vl->plugin) != 0) + { + if (write_part_string (&buffer, &buffer_size, TYPE_PLUGIN, + vl->plugin, strlen (vl->plugin)) != 0) + return (-1); + strcpy (vl_def->plugin, vl->plugin); + } - buf_len = sizeof (buf); - buf_ptr = buf; - if (write_part_string (&buf_ptr, &buf_len, TYPE_HOST, - vl->host, strlen (vl->host)) != 0) - return (-1); - if (write_part_string (&buf_ptr, &buf_len, TYPE_PLUGIN, - vl->plugin, strlen (vl->plugin)) != 0) - return (-1); - if (strlen (vl->plugin_instance) > 0) - if (write_part_string (&buf_ptr, &buf_len, TYPE_PLUGIN_INSTANCE, + if (strcmp (vl_def->plugin_instance, vl->plugin_instance) != 0) + { + if (write_part_string (&buffer, &buffer_size, TYPE_PLUGIN_INSTANCE, vl->plugin_instance, strlen (vl->plugin_instance)) != 0) return (-1); - if (write_part_string (&buf_ptr, &buf_len, TYPE_TYPE, - ds->type, strlen (ds->type)) != 0) - return (-1); - if (strlen (vl->type_instance) > 0) - if (write_part_string (&buf_ptr, &buf_len, TYPE_PLUGIN_INSTANCE, + strcpy (vl_def->plugin_instance, vl->plugin_instance); + } + + if (strcmp (type_def, ds->type) != 0) + { + if (write_part_string (&buffer, &buffer_size, TYPE_TYPE, + ds->type, strlen (ds->type)) != 0) + return (-1); + strcpy (type_def, ds->type); + } + + if (strcmp (vl_def->type_instance, vl->type_instance) != 0) + { + if (write_part_string (&buffer, &buffer_size, TYPE_TYPE_INSTANCE, vl->type_instance, strlen (vl->type_instance)) != 0) return (-1); + strcpy (vl_def->type_instance, vl->type_instance); + } - write_part_values (&buf_ptr, &buf_len, ds, vl); + if (write_part_values (&buffer, &buffer_size, ds, vl) != 0) + return (-1); - buf_len = sizeof (buf) - buf_len; + return (buffer - buffer_orig); +} /* int add_to_buffer */ - for (se = sending_sockets; se != NULL; se = se->next) +static void flush_buffer (void) +{ + DEBUG ("network plugin: flush_buffer: send_buffer_fill = %i", + send_buffer_fill); + + network_send_buffer (send_buffer, send_buffer_fill); + send_buffer_ptr = send_buffer; + send_buffer_fill = 0; + memset (&send_buffer_vl, '\0', sizeof (send_buffer_vl)); + memset (send_buffer_type, '\0', sizeof (send_buffer_type)); +} + +static int network_write (const data_set_t *ds, const value_list_t *vl) +{ + int status; + + /* If the value is already in the cache, we have received it via the + * network. We write it again if forwarding is activated. It's then in + * the cache and should we receive it again we will ignore it. */ + status = cache_check (ds->type, vl); + if ((network_config_forward == 0) + && (status != 0)) + return (0); + + pthread_mutex_lock (&send_buffer_lock); + + status = add_to_buffer (send_buffer_ptr, + sizeof (send_buffer) - send_buffer_fill, + &send_buffer_vl, send_buffer_type, + ds, vl); + if (status >= 0) { - int status; + /* status == bytes added to the buffer */ + send_buffer_fill += status; + send_buffer_ptr += status; + } + else + { + flush_buffer (); - while (42) + status = add_to_buffer (send_buffer_ptr, + sizeof (send_buffer) - send_buffer_fill, + &send_buffer_vl, send_buffer_type, + ds, vl); + + if (status >= 0) { - status = sendto (se->fd, buf, buf_len, 0 /* no flags */, - (struct sockaddr *) se->addr, se->addrlen); - if (status < 0) - { - if (errno == EINTR) - continue; - syslog (LOG_ERR, "network: sendto failed: %s", - strerror (errno)); - break; - } + send_buffer_fill += status; + send_buffer_ptr += status; + } + } - break; - } /* while (42) */ - } /* for (sending_sockets) */ + if (status < 0) + { + ERROR ("network plugin: Unable to append to the " + "buffer for some weird reason"); + } + else if ((sizeof (send_buffer) - send_buffer_fill) < 15) + { + flush_buffer (); + } - return 0; -} + pthread_mutex_unlock (&send_buffer_lock); + + return ((status < 0) ? -1 : 0); +} /* int network_write */ static int network_config (const char *key, const char *val) { @@ -914,7 +1247,11 @@ static int network_config (const char *key, const char *val) && (fields_num != 2)) return (1); else if (fields_num == 2) + { + if ((service = strchr (fields[1], '.')) != NULL) + *service = '\0'; service = fields[1]; + } node = fields[0]; if (strcasecmp ("Listen", key) == 0) @@ -930,31 +1267,79 @@ static int network_config (const char *key, const char *val) else return (1); } + else if (strcasecmp ("Forward", key) == 0) + { + if ((strcasecmp ("true", val) == 0) + || (strcasecmp ("yes", val) == 0) + || (strcasecmp ("on", val) == 0)) + network_config_forward = 1; + else + network_config_forward = 0; + } + else if (strcasecmp ("CacheFlush", key) == 0) + { + int tmp = atoi (val); + if (tmp > 0) + cache_flush_interval = tmp; + else return (1); + } else { return (-1); } return (0); -} +} /* int network_config */ static int network_shutdown (void) { - DBG ("Shutting down."); - listen_loop++; - pthread_kill (listen_thread, SIGTERM); - pthread_join (listen_thread, NULL /* no return value */); + if (listen_thread != (pthread_t) 0) + { + pthread_kill (listen_thread, SIGTERM); + pthread_join (listen_thread, NULL /* no return value */); + listen_thread = (pthread_t) 0; + } - listen_thread = 0; + if (send_buffer_fill > 0) + flush_buffer (); + + if (cache_tree != NULL) + { + void *key; + void *value; + + while (avl_pick (cache_tree, &key, &value) == 0) + { + sfree (key); + sfree (value); + } + avl_destroy (cache_tree); + cache_tree = NULL; + } + + /* TODO: Close `sending_sockets' */ + + plugin_unregister_config ("network"); + plugin_unregister_init ("network"); + plugin_unregister_write ("network"); + plugin_unregister_shutdown ("network"); return (0); -} +} /* int network_shutdown */ static int network_init (void) { plugin_register_shutdown ("network", network_shutdown); + send_buffer_ptr = send_buffer; + send_buffer_fill = 0; + memset (&send_buffer_vl, '\0', sizeof (send_buffer_vl)); + memset (send_buffer_type, '\0', sizeof (send_buffer_type)); + + cache_tree = avl_create ((int (*) (const void *, const void *)) strcmp); + cache_flush_last = time (NULL); + /* setup socket(s) and so on */ if (sending_sockets != NULL) plugin_register_write ("network", network_write); @@ -967,8 +1352,12 @@ static int network_init (void) receive_thread, NULL /* no argument */); if (status != 0) - syslog (LOG_ERR, "network: pthread_create failed: %s", - strerror (errno)); + { + char errbuf[1024]; + ERROR ("network: pthread_create failed: %s", + sstrerror (errno, errbuf, + sizeof (errbuf))); + } } return (0); } /* int network_init */ @@ -978,4 +1367,4 @@ void module_register (void) plugin_register_config ("network", network_config, config_keys, config_keys_num); plugin_register_init ("network", network_init); -} +} /* void module_register */