X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Fnetwork.c;h=34a16690953048a589f0f5c0f82ee45e8207c9dd;hb=1d3482c65a00342de681e777c74108734ece0eab;hp=cf2a01799cbb1723c710357be176810aac053fb6;hpb=5661326c8102fa9035b870316e9f152f43aca31c;p=collectd.git diff --git a/src/network.c b/src/network.c index cf2a0179..34a16690 100644 --- a/src/network.c +++ b/src/network.c @@ -23,7 +23,7 @@ #include "plugin.h" #include "common.h" #include "configfile.h" -#include "utils_debug.h" +#include "utils_avltree.h" #include "network.h" @@ -136,70 +136,253 @@ struct part_values_s }; typedef struct part_values_s part_values_t; +struct receive_list_entry_s +{ + char data[BUFF_SIZE]; + int data_len; + struct receive_list_entry_s *next; +}; +typedef struct receive_list_entry_s receive_list_entry_t; + /* * Private variables */ 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; +static receive_list_entry_t *receive_list_head = NULL; +static receive_list_entry_t *receive_list_tail = NULL; +static pthread_mutex_t receive_list_lock = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t receive_list_cond = PTHREAD_COND_INITIALIZER; + static struct pollfd *listen_sockets = NULL; static int listen_sockets_num = 0; -static pthread_t listen_thread = 0; + static int listen_loop = 0; +static pthread_t receive_thread_id = 0; +static pthread_t dispatch_thread_id = 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_INIT; +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 c_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; + c_avl_iterator_t *iter; + + time_t curtime = time (NULL); + + iter = c_avl_get_iterator (cache_tree); + while (c_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); + c_avl_iterator_destroy (iter); + ERROR ("network plugin: cache_flush: realloc" + " failed."); + return (-1); + } + keys = tmp; + keys[keys_num] = key; + keys_num++; + } /* while (c_avl_iterator_next) */ + c_avl_iterator_destroy (iter); + + for (i = 0; i < keys_num; i++) + { + if (c_avl_remove (cache_tree, keys[i], (void *) &key, + (void *) &value) != 0) + { + WARNING ("network plugin: cache_flush: c_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 (c_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; + c_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) { - part_values_t pv; + char *packet_ptr; + int packet_len; + int num_values; + + part_header_t pkg_ph; + uint16_t pkg_num_values; + uint8_t *pkg_values_types; + value_t *pkg_values; + + int offset; int i; - i = 6 + (9 * vl->values_len); - if (*ret_buffer_len < i) + num_values = vl->values_len; + packet_len = sizeof (part_header_t) + sizeof (uint16_t) + + (num_values * sizeof (uint8_t)) + + (num_values * sizeof (value_t)); + + if (*ret_buffer_len < packet_len) return (-1); - *ret_buffer_len -= i; - pv.head = (part_header_t *) *ret_buffer; - pv.num_values = (uint16_t *) (pv.head + 1); - pv.values_types = (uint8_t *) (pv.num_values + 1); - pv.values = (value_t *) (pv.values_types + vl->values_len); - *ret_buffer = (void *) (pv.values + vl->values_len); + pkg_values_types = (uint8_t *) malloc (num_values * sizeof (uint8_t)); + if (pkg_values_types == NULL) + { + ERROR ("network plugin: write_part_values: malloc failed."); + return (-1); + } - pv.head->type = htons (TYPE_VALUES); - pv.head->length = htons (6 + (9 * vl->values_len)); - *pv.num_values = htons ((uint16_t) vl->values_len); - - for (i = 0; i < vl->values_len; i++) + pkg_values = (value_t *) malloc (num_values * sizeof (value_t)); + if (pkg_values == NULL) + { + free (pkg_values_types); + ERROR ("network plugin: write_part_values: malloc failed."); + return (-1); + } + + pkg_ph.type = htons (TYPE_VALUES); + pkg_ph.length = htons (packet_len); + + pkg_num_values = htons ((uint16_t) vl->values_len); + + for (i = 0; i < num_values; i++) { if (ds->ds[i].type == DS_TYPE_COUNTER) { - pv.values_types[i] = DS_TYPE_COUNTER; - pv.values[i].counter = htonll (vl->values[i].counter); + pkg_values_types[i] = DS_TYPE_COUNTER; + pkg_values[i].counter = htonll (vl->values[i].counter); } else { - pv.values_types[i] = DS_TYPE_GAUGE; - pv.values[i].gauge = vl->values[i].gauge; + pkg_values_types[i] = DS_TYPE_GAUGE; + pkg_values[i].gauge = htond (vl->values[i].gauge); } - } /* for (values) */ + } + + /* + * Use `memcpy' to write everything to the buffer, because the pointer + * may be unaligned and some architectures, such as SPARC, can't handle + * that. + */ + packet_ptr = *ret_buffer; + offset = 0; + memcpy (packet_ptr + offset, &pkg_ph, sizeof (pkg_ph)); + offset += sizeof (pkg_ph); + memcpy (packet_ptr + offset, &pkg_num_values, sizeof (pkg_num_values)); + offset += sizeof (pkg_num_values); + memcpy (packet_ptr + offset, pkg_values_types, num_values * sizeof (uint8_t)); + offset += num_values * sizeof (uint8_t); + memcpy (packet_ptr + offset, pkg_values, num_values * sizeof (value_t)); + offset += num_values * sizeof (value_t); + + assert (offset == packet_len); + + *ret_buffer = packet_ptr + packet_len; + *ret_buffer_len -= packet_len; + + free (pkg_values_types); + free (pkg_values); return (0); } /* int write_part_values */ @@ -207,20 +390,34 @@ static int write_part_values (char **ret_buffer, int *ret_buffer_len, static int write_part_number (char **ret_buffer, int *ret_buffer_len, int type, uint64_t value) { - part_number_t pn; + char *packet_ptr; + int packet_len; - if (*ret_buffer_len < 12) + part_header_t pkg_head; + uint64_t pkg_value; + + int offset; + + packet_len = sizeof (pkg_head) + sizeof (pkg_value); + + if (*ret_buffer_len < packet_len) return (-1); - pn.head = (part_header_t *) *ret_buffer; - pn.value = (uint64_t *) (pn.head + 1); + pkg_head.type = htons (type); + pkg_head.length = htons (packet_len); + pkg_value = htonll (value); - pn.head->type = htons (type); - pn.head->length = htons (12); - *pn.value = htonll (value); + packet_ptr = *ret_buffer; + offset = 0; + memcpy (packet_ptr + offset, &pkg_head, sizeof (pkg_head)); + offset += sizeof (pkg_head); + memcpy (packet_ptr + offset, &pkg_value, sizeof (pkg_value)); + offset += sizeof (pkg_value); - *ret_buffer = (char *) (pn.value + 1); - *ret_buffer_len -= 12; + assert (offset == packet_len); + + *ret_buffer = packet_ptr + packet_len; + *ret_buffer_len -= packet_len; return (0); } /* int write_part_number */ @@ -228,23 +425,36 @@ static int write_part_number (char **ret_buffer, int *ret_buffer_len, 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; + char *buffer; + int buffer_len; + + uint16_t pkg_type; + uint16_t pkg_length; + + int offset; - len = 4 + str_len + 1; - if (*ret_buffer_len < len) + buffer_len = 2 * sizeof (uint16_t) + str_len + 1; + if (*ret_buffer_len < buffer_len) return (-1); - *ret_buffer_len -= len; - ps.head = (part_header_t *) *ret_buffer; - ps.value = (char *) (ps.head + 1); + pkg_type = htons (type); + pkg_length = htons (buffer_len); - ps.head->type = htons ((uint16_t) type); - 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 + 1)); + buffer = *ret_buffer; + offset = 0; + memcpy (buffer + offset, (void *) &pkg_type, sizeof (pkg_type)); + offset += sizeof (pkg_type); + memcpy (buffer + offset, (void *) &pkg_length, sizeof (pkg_length)); + offset += sizeof (pkg_length); + memcpy (buffer + offset, str, str_len); + offset += str_len; + memset (buffer + offset, '\0', 1); + offset += 1; + + assert (offset == buffer_len); + + *ret_buffer = buffer + buffer_len; + *ret_buffer_len -= buffer_len; return (0); } /* int write_part_string */ @@ -254,45 +464,88 @@ 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 pv; + + uint16_t tmp16; + size_t exp_size; int i; - uint16_t h_length; - uint16_t h_type; - uint16_t h_num; + uint16_t pkg_length; + uint16_t pkg_type; + uint16_t pkg_numval; + + uint8_t *pkg_types; + value_t *pkg_values; if (buffer_len < (15)) { - DBG ("packet is too short: buffer_len = %i", buffer_len); + DEBUG ("network plugin: packet is too short: buffer_len = %i", + buffer_len); return (-1); } - pv.head = (part_header_t *) buffer; - h_length = ntohs (pv.head->length); - h_type = ntohs (pv.head->type); + memcpy ((void *) &tmp16, buffer, sizeof (tmp16)); + buffer += sizeof (tmp16); + pkg_type = ntohs (tmp16); + + memcpy ((void *) &tmp16, buffer, sizeof (tmp16)); + buffer += sizeof (tmp16); + pkg_length = ntohs (tmp16); - assert (h_type == TYPE_VALUES); + memcpy ((void *) &tmp16, buffer, sizeof (tmp16)); + buffer += sizeof (tmp16); + pkg_numval = ntohs (tmp16); - pv.num_values = (uint16_t *) (pv.head + 1); - h_num = ntohs (*pv.num_values); + assert (pkg_type == TYPE_VALUES); - if (h_num != ((h_length - 6) / 9)) + exp_size = 3 * sizeof (uint16_t) + + pkg_numval * (sizeof (uint8_t) + sizeof (value_t)); + if (buffer_len < exp_size) { - DBG ("`length' and `num of values' don't match"); + WARNING ("network plugin: parse_part_values: " + "Packet too short: " + "Chunk of size %u expected, " + "but buffer has only %i bytes left.", + (unsigned int) exp_size, buffer_len); return (-1); } - pv.values_types = (uint8_t *) (pv.num_values + 1); - pv.values = (value_t *) (pv.values_types + h_num); + if (pkg_length != exp_size) + { + WARNING ("network plugin: parse_part_values: " + "Length and number of values " + "in the packet don't match."); + return (-1); + } + + pkg_types = (uint8_t *) malloc (pkg_numval * sizeof (uint8_t)); + pkg_values = (value_t *) malloc (pkg_numval * sizeof (value_t)); + if ((pkg_types == NULL) || (pkg_values == NULL)) + { + sfree (pkg_types); + sfree (pkg_values); + ERROR ("network plugin: parse_part_values: malloc failed."); + return (-1); + } + + memcpy ((void *) pkg_types, (void *) buffer, pkg_numval * sizeof (uint8_t)); + buffer += pkg_numval * sizeof (uint8_t); + memcpy ((void *) pkg_values, (void *) buffer, pkg_numval * sizeof (value_t)); + buffer += pkg_numval * sizeof (value_t); + + for (i = 0; i < pkg_numval; i++) + { + if (pkg_types[i] == DS_TYPE_COUNTER) + pkg_values[i].counter = ntohll (pkg_values[i].counter); + else if (pkg_types[i] == DS_TYPE_GAUGE) + pkg_values[i].gauge = ntohd (pkg_values[i].gauge); + } - for (i = 0; i < h_num; i++) - if (pv.values_types[i] == DS_TYPE_COUNTER) - pv.values[i].counter = ntohll (pv.values[i].counter); + *ret_buffer = buffer; + *ret_buffer_len = buffer_len - pkg_length; + *ret_num_values = pkg_numval; + *ret_values = pkg_values; - *ret_buffer = (void *) (pv.values + h_num); - *ret_buffer_len = buffer_len - h_length; - *ret_num_values = h_num; - *ret_values = pv.values; + sfree (pkg_types); return (0); } /* int parse_part_values */ @@ -300,21 +553,40 @@ static int parse_part_values (void **ret_buffer, int *ret_buffer_len, static int parse_part_number (void **ret_buffer, int *ret_buffer_len, uint64_t *value) { - part_number_t pn; - uint16_t len; + char *buffer = *ret_buffer; + int buffer_len = *ret_buffer_len; - pn.head = (part_header_t *) *ret_buffer; - pn.value = (uint64_t *) (pn.head + 1); + uint16_t tmp16; + uint64_t tmp64; + size_t exp_size = 2 * sizeof (uint16_t) + sizeof (uint64_t); - len = ntohs (pn.head->length); - if (len != 12) - return (-1); - if (len > *ret_buffer_len) + uint16_t pkg_length; + uint16_t pkg_type; + + if (buffer_len < exp_size) + { + WARNING ("network plugin: parse_part_number: " + "Packet too short: " + "Chunk of size %u expected, " + "but buffer has only %i bytes left.", + (unsigned int) exp_size, buffer_len); return (-1); - *value = ntohll (*pn.value); + } + + memcpy ((void *) &tmp16, buffer, sizeof (tmp16)); + buffer += sizeof (tmp16); + pkg_type = ntohs (tmp16); - *ret_buffer = (void *) (pn.value + 1); - *ret_buffer_len -= len; + memcpy ((void *) &tmp16, buffer, sizeof (tmp16)); + buffer += sizeof (tmp16); + pkg_length = ntohs (tmp16); + + memcpy ((void *) &tmp64, buffer, sizeof (tmp64)); + buffer += sizeof (tmp64); + *value = ntohll (tmp64); + + *ret_buffer = buffer; + *ret_buffer_len = buffer_len - pkg_length; return (0); } /* int parse_part_number */ @@ -324,64 +596,92 @@ static int parse_part_string (void **ret_buffer, int *ret_buffer_len, { char *buffer = *ret_buffer; int buffer_len = *ret_buffer_len; - part_string_t ps; - uint16_t h_length; - uint16_t h_type; + uint16_t tmp16; + size_t header_size = 2 * sizeof (uint16_t); - DBG ("ret_buffer = %p; ret_buffer_len = %i; output = %p; output_len = %i;", - *ret_buffer, *ret_buffer_len, - (void *) output, output_len); + uint16_t pkg_length; + uint16_t pkg_type; - ps.head = (part_header_t *) buffer; + if (buffer_len < header_size) + { + WARNING ("network plugin: parse_part_string: " + "Packet too short: " + "Chunk of at least size %u expected, " + "but buffer has only %i bytes left.", + (unsigned int) header_size, buffer_len); + return (-1); + } - h_length = ntohs (ps.head->length); - h_type = ntohs (ps.head->type); + memcpy ((void *) &tmp16, buffer, sizeof (tmp16)); + buffer += sizeof (tmp16); + pkg_type = ntohs (tmp16); - DBG ("length = %hu; type = %hu;", h_length, h_type); + memcpy ((void *) &tmp16, buffer, sizeof (tmp16)); + buffer += sizeof (tmp16); + pkg_length = ntohs (tmp16); - if (buffer_len < h_length) + /* Check that packet fits in the input buffer */ + if (pkg_length > buffer_len) { - DBG ("packet is too short"); + WARNING ("network plugin: parse_part_string: " + "Packet too big: " + "Chunk of size %hu received, " + "but buffer has only %i bytes left.", + pkg_length, buffer_len); return (-1); } - 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') + + /* Check that pkg_length is in the valid range */ + if (pkg_length <= header_size) { - DBG ("String does not end with a nullbyte"); + WARNING ("network plugin: parse_part_string: " + "Packet too short: " + "Header claims this packet is only %hu " + "bytes long.", pkg_length); return (-1); } - if (output_len < (h_length - 4)) + /* Check that the package data fits into the output buffer. + * The previous if-statement ensures that: + * `pkg_length > header_size' */ + if ((pkg_length - header_size) > output_len) { - DBG ("output buffer is too small"); + WARNING ("network plugin: parse_part_string: " + "Output buffer too small."); return (-1); } - strcpy (output, ps.value); - DBG ("output = %s", output); + /* 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; - *ret_buffer = (void *) (buffer + h_length); - *ret_buffer_len = buffer_len - h_length; + /* For some very weird reason '\0' doesn't do the trick on SPARC in + * this statement. */ + if (output[output_len - 1] != 0) + { + WARNING ("network plugin: parse_part_string: " + "Received string does not end " + "with a NULL-byte."); + return (-1); + } + + *ret_buffer = buffer; + *ret_buffer_len = buffer_len - pkg_length; return (0); } /* int parse_part_string */ static int parse_packet (void *buffer, int buffer_len) { - part_header_t *header; int status; value_list_t vl = VALUE_LIST_INIT; char type[DATA_MAX_NAME_LEN]; - DBG ("buffer = %p; buffer_len = %i;", buffer, buffer_len); + DEBUG ("network plugin: parse_packet: buffer = %p; buffer_len = %i;", + buffer, buffer_len); memset (&vl, '\0', sizeof (vl)); memset (&type, '\0', sizeof (type)); @@ -389,75 +689,97 @@ static int parse_packet (void *buffer, int buffer_len) while ((status == 0) && (buffer_len > sizeof (part_header_t))) { - header = (part_header_t *) buffer; + uint16_t pkg_length; + uint16_t pkg_type; + + memcpy ((void *) &pkg_type, + (void *) buffer, + sizeof (pkg_type)); + memcpy ((void *) &pkg_length, + (void *) (buffer + sizeof (pkg_type)), + sizeof (pkg_length)); + + pkg_length = ntohs (pkg_length); + pkg_type = ntohs (pkg_type); - if (ntohs (header->length) > buffer_len) + if (pkg_length > buffer_len) + break; + /* Ensure that this loop terminates eventually */ + if (pkg_length < (2 * sizeof (uint16_t))) break; - if (header->type == htons (TYPE_VALUES)) + if (pkg_type == TYPE_VALUES) { status = parse_part_values (&buffer, &buffer_len, &vl.values, &vl.values_len); if (status != 0) - { - DBG ("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)) { - DBG ("dispatching values"); plugin_dispatch_values (type, &vl); } else { - DBG ("NOT dispatching values"); + DEBUG ("network plugin: parse_packet:" + " NOT dispatching values"); } + + sfree (vl.values); } - else if (header->type == ntohs (TYPE_TIME)) + else if (pkg_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 == ntohs (TYPE_HOST)) + else if (pkg_type == TYPE_INTERVAL) + { + uint64_t tmp = 0; + status = parse_part_number (&buffer, &buffer_len, &tmp); + if (status == 0) + vl.interval = (int) tmp; + } + else if (pkg_type == TYPE_HOST) { status = parse_part_string (&buffer, &buffer_len, vl.host, sizeof (vl.host)); } - else if (header->type == ntohs (TYPE_PLUGIN)) + else if (pkg_type == TYPE_PLUGIN) { status = parse_part_string (&buffer, &buffer_len, vl.plugin, sizeof (vl.plugin)); } - else if (header->type == ntohs (TYPE_PLUGIN_INSTANCE)) + else if (pkg_type == TYPE_PLUGIN_INSTANCE) { status = parse_part_string (&buffer, &buffer_len, vl.plugin_instance, sizeof (vl.plugin_instance)); } - else if (header->type == ntohs (TYPE_TYPE)) + else if (pkg_type == TYPE_TYPE) { status = parse_part_string (&buffer, &buffer_len, type, sizeof (type)); } - else if (header->type == ntohs (TYPE_TYPE_INSTANCE)) + else if (pkg_type == TYPE_TYPE_INSTANCE) { status = parse_part_string (&buffer, &buffer_len, vl.type_instance, sizeof (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%04hx", pkg_type); + buffer = ((char *) buffer) + pkg_length; } } /* while (buffer_len > sizeof (part_header_t)) */ - return (0); + return (status); } /* int parse_packet */ static void free_sockent (sockent_t *se) @@ -486,7 +808,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) { @@ -502,7 +824,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); } } @@ -521,7 +845,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); } } @@ -531,13 +858,15 @@ 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; - 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); } @@ -548,7 +877,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); @@ -556,14 +885,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); } } @@ -576,7 +911,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, @@ -596,14 +931,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); } } @@ -623,7 +964,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; @@ -640,11 +981,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); } @@ -655,13 +997,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; } @@ -678,7 +1026,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; @@ -815,7 +1166,38 @@ static int network_add_sending_socket (const char *node, const char *service) return (0); } /* int network_get_listen_socket */ -int network_receive (void) +static void *dispatch_thread (void *arg) +{ + while (42) + { + receive_list_entry_t *ent; + + /* Lock and wait for more data to come in */ + pthread_mutex_lock (&receive_list_lock); + while ((listen_loop == 0) + && (receive_list_head == NULL)) + pthread_cond_wait (&receive_list_cond, &receive_list_lock); + + /* Remove the head entry and unlock */ + ent = receive_list_head; + if (ent != NULL) + receive_list_head = ent->next; + pthread_mutex_unlock (&receive_list_lock); + + /* Check whether we are supposed to exit. We do NOT check `listen_loop' + * because we dispatch all missing packets before shutting down. */ + if (ent == NULL) + break; + + parse_packet (ent->data, ent->data_len); + + sfree (ent); + } /* while (42) */ + + return (NULL); +} /* void *receive_thread */ + +static int network_receive (void) { char buffer[BUFF_SIZE]; int buffer_len; @@ -828,7 +1210,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); } @@ -838,15 +1220,18 @@ 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); } for (i = 0; (i < listen_sockets_num) && (status > 0); i++) { + receive_list_entry_t *ent; + if ((listen_sockets[i].revents & (POLLIN | POLLPRI)) == 0) continue; status--; @@ -856,11 +1241,42 @@ 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); } - parse_packet (buffer, buffer_len); + ent = malloc (sizeof (receive_list_entry_t)); + if (ent == NULL) + { + ERROR ("network plugin: malloc failed."); + return (-1); + } + memset (ent, '\0', sizeof (receive_list_entry_t)); + + /* Hopefully this be optimized out by the compiler. It + * might help prevent stupid bugs in the future though. + */ + assert (sizeof (ent->data) == sizeof (buffer)); + + memcpy (ent->data, buffer, buffer_len); + ent->data_len = buffer_len; + + pthread_mutex_lock (&receive_list_lock); + if (receive_list_head == NULL) + { + receive_list_head = ent; + receive_list_tail = ent; + } + else + { + receive_list_tail->next = ent; + receive_list_tail = ent; + } + pthread_cond_signal (&receive_list_cond); + pthread_mutex_unlock (&receive_list_lock); } /* for (listen_sockets) */ } /* while (listen_loop == 0) */ @@ -877,7 +1293,7 @@ static void network_send_buffer (const char *buffer, int buffer_len) sockent_t *se; int status; - DBG ("buffer_len = %i", buffer_len); + DEBUG ("network plugin: network_send_buffer: buffer_len = %i", buffer_len); for (se = sending_sockets; se != NULL; se = se->next) { @@ -887,10 +1303,12 @@ static void network_send_buffer (const char *buffer, int buffer_len) (struct sockaddr *) se->addr, se->addrlen); if (status < 0) { + char errbuf[1024]; if (errno == EINTR) continue; - syslog (LOG_ERR, "network plugin: sendto failed: %s", - strerror (errno)); + ERROR ("network plugin: sendto failed: %s", + sstrerror (errno, errbuf, + sizeof (errbuf))); break; } @@ -903,13 +1321,14 @@ 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; + 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); - DBG ("host = %s", vl->host); } if (vl_def->time != vl->time) @@ -918,7 +1337,14 @@ static int add_to_buffer (char *buffer, int buffer_size, (uint64_t) vl->time)) return (-1); vl_def->time = vl->time; - DBG ("time = %u", (unsigned int) vl->time); + } + + 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; } if (strcmp (vl_def->plugin, vl->plugin) != 0) @@ -927,7 +1353,6 @@ static int add_to_buffer (char *buffer, int buffer_size, vl->plugin, strlen (vl->plugin)) != 0) return (-1); strcpy (vl_def->plugin, vl->plugin); - DBG ("plugin = %s", vl->plugin); } if (strcmp (vl_def->plugin_instance, vl->plugin_instance) != 0) @@ -937,7 +1362,6 @@ static int add_to_buffer (char *buffer, int buffer_size, strlen (vl->plugin_instance)) != 0) return (-1); strcpy (vl_def->plugin_instance, vl->plugin_instance); - DBG ("plugin_instance = %s", vl->plugin_instance); } if (strcmp (type_def, ds->type) != 0) @@ -946,27 +1370,28 @@ static int add_to_buffer (char *buffer, int buffer_size, ds->type, strlen (ds->type)) != 0) return (-1); strcpy (type_def, ds->type); - DBG ("type = %s", ds->type); } if (strcmp (vl_def->type_instance, vl->type_instance) != 0) { - if (write_part_string (&buffer, &buffer_size, TYPE_PLUGIN_INSTANCE, + 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); - DBG ("type_instance = %s", vl->type_instance); } if (write_part_values (&buffer, &buffer_size, ds, vl) != 0) return (-1); - return (buffer_size); + return (buffer - buffer_orig); } /* int add_to_buffer */ 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; @@ -977,13 +1402,24 @@ static void flush_buffer (void) static int network_write (const data_set_t *ds, const value_list_t *vl) { int status; - /* TODO: lock buffer */ + + /* 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) { + /* status == bytes added to the buffer */ send_buffer_fill += status; send_buffer_ptr += status; } @@ -1005,14 +1441,15 @@ static int network_write (const data_set_t *ds, const value_list_t *vl) if (status < 0) { - syslog (LOG_ERR, "network plugin: Unable to append to the " + ERROR ("network plugin: Unable to append to the " "buffer for some weird reason"); } else if ((sizeof (send_buffer) - send_buffer_fill) < 15) { flush_buffer (); } - /* TODO: unlock buffer */ + + pthread_mutex_unlock (&send_buffer_lock); return ((status < 0) ? -1 : 0); } /* int network_write */ @@ -1038,7 +1475,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) @@ -1054,26 +1495,71 @@ 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 */); + /* Kill the listening thread */ + if (receive_thread_id != (pthread_t) 0) + { + pthread_kill (receive_thread_id, SIGTERM); + pthread_join (receive_thread_id, NULL /* no return value */); + receive_thread_id = (pthread_t) 0; + } + + /* Shutdown the dispatching thread */ + if (dispatch_thread_id != (pthread_t) 0) + pthread_cond_broadcast (&receive_list_cond); + + if (send_buffer_fill > 0) + flush_buffer (); + + if (cache_tree != NULL) + { + void *key; + void *value; + + while (c_avl_pick (cache_tree, &key, &value) == 0) + { + sfree (key); + sfree (value); + } + c_avl_destroy (cache_tree); + cache_tree = NULL; + } + + /* TODO: Close `sending_sockets' */ - listen_thread = 0; + 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) { @@ -1084,20 +1570,40 @@ static int network_init (void) memset (&send_buffer_vl, '\0', sizeof (send_buffer_vl)); memset (send_buffer_type, '\0', sizeof (send_buffer_type)); + cache_tree = c_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); - if ((listen_sockets_num != 0) && (listen_thread == 0)) + if ((listen_sockets_num != 0) && (receive_thread_id == 0)) { int status; - status = pthread_create (&listen_thread, NULL /* no attributes */, - receive_thread, NULL /* no argument */); + status = pthread_create (&dispatch_thread_id, + NULL /* no attributes */, + dispatch_thread, + NULL /* no argument */); + if (status != 0) + { + char errbuf[1024]; + ERROR ("network: pthread_create failed: %s", + sstrerror (errno, errbuf, + sizeof (errbuf))); + } + status = pthread_create (&receive_thread_id, + NULL /* no attributes */, + 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 */ @@ -1107,4 +1613,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 */