X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Fwrite_http.c;h=7f5943a2435f1f7c9dafa9221433dd2ef92f4e41;hb=d6021a800b12c89b5a78877af2c5b9abc1a8e609;hp=912c92dd432f6352fa3a5087c2f40b72d0dd5a7a;hpb=cf7c7d8538822a2ad09021adf1a919b7ce1892c0;p=collectd.git diff --git a/src/write_http.c b/src/write_http.c index 912c92dd..7f5943a2 100644 --- a/src/write_http.c +++ b/src/write_http.c @@ -28,6 +28,7 @@ #include "common.h" #include "utils_cache.h" #include "utils_parse_option.h" +#include "utils_format_json.h" #if HAVE_PTHREAD_H # include @@ -45,6 +46,14 @@ struct wh_callback_s char *user; char *pass; char *credentials; + int verify_peer; + int verify_host; + char *cacert; + int store_rates; + +#define WH_FORMAT_COMMAND 0 +#define WH_FORMAT_JSON 1 + int format; CURL *curl; char curl_errbuf[CURL_ERROR_SIZE]; @@ -52,7 +61,7 @@ struct wh_callback_s char send_buffer[4096]; size_t send_buffer_free; size_t send_buffer_fill; - time_t send_buffer_init_time; + cdtime_t send_buffer_init_time; pthread_mutex_t send_lock; }; @@ -63,7 +72,14 @@ static void wh_reset_buffer (wh_callback_t *cb) /* {{{ */ memset (cb->send_buffer, 0, sizeof (cb->send_buffer)); cb->send_buffer_free = sizeof (cb->send_buffer); cb->send_buffer_fill = 0; - cb->send_buffer_init_time = time (NULL); + cb->send_buffer_init_time = cdtime (); + + if (cb->format == WH_FORMAT_JSON) + { + format_json_initialize (cb->send_buffer, + &cb->send_buffer_fill, + &cb->send_buffer_free); + } } /* }}} wh_reset_buffer */ static int wh_send_buffer (wh_callback_t *cb) /* {{{ */ @@ -72,10 +88,10 @@ static int wh_send_buffer (wh_callback_t *cb) /* {{{ */ curl_easy_setopt (cb->curl, CURLOPT_POSTFIELDS, cb->send_buffer); status = curl_easy_perform (cb->curl); - if (status != 0) + if (status != CURLE_OK) { ERROR ("write_http plugin: curl_easy_perform failed with " - "staus %i: %s", + "status %i: %s", status, cb->curl_errbuf); } return (status); @@ -95,11 +111,16 @@ static int wh_callback_init (wh_callback_t *cb) /* {{{ */ return (-1); } + curl_easy_setopt (cb->curl, CURLOPT_NOSIGNAL, 1L); curl_easy_setopt (cb->curl, CURLOPT_USERAGENT, PACKAGE_NAME"/"PACKAGE_VERSION); headers = NULL; headers = curl_slist_append (headers, "Accept: */*"); - headers = curl_slist_append (headers, "Content-Type: text/plain"); + if (cb->format == WH_FORMAT_JSON) + headers = curl_slist_append (headers, "Content-Type: application/json"); + else + headers = curl_slist_append (headers, "Content-Type: text/plain"); + headers = curl_slist_append (headers, "Expect:"); curl_easy_setopt (cb->curl, CURLOPT_HTTPHEADER, headers); curl_easy_setopt (cb->curl, CURLOPT_ERRORBUFFER, cb->curl_errbuf); @@ -123,44 +144,84 @@ static int wh_callback_init (wh_callback_t *cb) /* {{{ */ ssnprintf (cb->credentials, credentials_size, "%s:%s", cb->user, (cb->pass == NULL) ? "" : cb->pass); curl_easy_setopt (cb->curl, CURLOPT_USERPWD, cb->credentials); - curl_easy_setopt (cb->curl, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST); + curl_easy_setopt (cb->curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY); } + curl_easy_setopt (cb->curl, CURLOPT_SSL_VERIFYPEER, (long) cb->verify_peer); + curl_easy_setopt (cb->curl, CURLOPT_SSL_VERIFYHOST, + cb->verify_host ? 2L : 0L); + if (cb->cacert != NULL) + curl_easy_setopt (cb->curl, CURLOPT_CAINFO, cb->cacert); + wh_reset_buffer (cb); return (0); } /* }}} int wh_callback_init */ -static int wh_flush_nolock (int timeout, wh_callback_t *cb) /* {{{ */ +static int wh_flush_nolock (cdtime_t timeout, wh_callback_t *cb) /* {{{ */ { int status; - DEBUG ("write_http plugin: wh_flush_nolock: timeout = %i; " + DEBUG ("write_http plugin: wh_flush_nolock: timeout = %.3f; " "send_buffer_fill = %zu;", - timeout, cb->send_buffer_fill); + CDTIME_T_TO_DOUBLE (timeout), + cb->send_buffer_fill); + /* timeout == 0 => flush unconditionally */ if (timeout > 0) { - time_t now; + cdtime_t now; - now = time (NULL); + now = cdtime (); if ((cb->send_buffer_init_time + timeout) > now) return (0); } - if (cb->send_buffer_fill <= 0) + if (cb->format == WH_FORMAT_COMMAND) { - cb->send_buffer_init_time = time (NULL); - return (0); + if (cb->send_buffer_fill <= 0) + { + cb->send_buffer_init_time = cdtime (); + return (0); + } + + status = wh_send_buffer (cb); + wh_reset_buffer (cb); } + else if (cb->format == WH_FORMAT_JSON) + { + if (cb->send_buffer_fill <= 2) + { + cb->send_buffer_init_time = cdtime (); + return (0); + } - status = wh_send_buffer (cb); - wh_reset_buffer (cb); + status = format_json_finalize (cb->send_buffer, + &cb->send_buffer_fill, + &cb->send_buffer_free); + if (status != 0) + { + ERROR ("write_http: wh_flush_nolock: " + "format_json_finalize failed."); + wh_reset_buffer (cb); + return (status); + } + + status = wh_send_buffer (cb); + wh_reset_buffer (cb); + } + else + { + ERROR ("write_http: wh_flush_nolock: " + "Unknown format: %i", + cb->format); + return (-1); + } return (status); } /* }}} wh_flush_nolock */ -static int wh_flush (int timeout, /* {{{ */ +static int wh_flush (cdtime_t timeout, /* {{{ */ const char *identifier __attribute__((unused)), user_data_t *user_data) { @@ -200,92 +261,18 @@ static void wh_callback_free (void *data) /* {{{ */ cb = data; - wh_flush_nolock (/* timeout = */ -1, cb); + wh_flush_nolock (/* timeout = */ 0, cb); curl_easy_cleanup (cb->curl); sfree (cb->location); sfree (cb->user); sfree (cb->pass); sfree (cb->credentials); + sfree (cb->cacert); sfree (cb); } /* }}} void wh_callback_free */ -static int wh_value_list_to_string (char *buffer, /* {{{ */ - size_t buffer_size, - const data_set_t *ds, const value_list_t *vl) -{ - size_t offset = 0; - int status; - int i; - - assert (0 == strcmp (ds->type, vl->type)); - - memset (buffer, 0, buffer_size); - -#define BUFFER_ADD(...) do { \ - status = ssnprintf (buffer + offset, buffer_size - offset, \ - __VA_ARGS__); \ - if (status < 1) \ - return (-1); \ - else if (((size_t) status) >= (buffer_size - offset)) \ - return (-1); \ - else \ - offset += ((size_t) status); \ -} while (0) - - BUFFER_ADD ("%lu", (unsigned long) vl->time); - - for (i = 0; i < ds->ds_num; i++) -{ - if (ds->ds[i].type == DS_TYPE_GAUGE) - BUFFER_ADD (":%f", vl->values[i].gauge); - else if (ds->ds[i].type == DS_TYPE_COUNTER) - BUFFER_ADD (":%llu", vl->values[i].counter); - else if (ds->ds[i].type == DS_TYPE_DERIVE) - BUFFER_ADD (":%"PRIi64, vl->values[i].derive); - else if (ds->ds[i].type == DS_TYPE_ABSOLUTE) - BUFFER_ADD (":%"PRIu64, vl->values[i].absolute); - else - { - ERROR ("write_http plugin: Unknown data source type: %i", - ds->ds[i].type); - return (-1); - } -} /* for ds->ds_num */ - -#undef BUFFER_ADD - -return (0); -} /* }}} int wh_value_list_to_string */ - -static int config_set_string (char **ret_string, /* {{{ */ - oconfig_item_t *ci) -{ - char *string; - - if ((ci->values_num != 1) - || (ci->values[0].type != OCONFIG_TYPE_STRING)) - { - WARNING ("write_http plugin: The `%s' config option " - "needs exactly one string argument.", ci->key); - return (-1); - } - - string = strdup (ci->values[0].value.string); - if (string == NULL) - { - ERROR ("write_http plugin: strdup failed."); - return (-1); - } - - if (*ret_string != NULL) - free (*ret_string); - *ret_string = string; - - return (0); -} /* }}} int config_set_string */ - static int wh_write_command (const data_set_t *ds, const value_list_t *vl, /* {{{ */ wh_callback_t *cb) { @@ -312,7 +299,7 @@ static int wh_write_command (const data_set_t *ds, const value_list_t *vl, /* {{ /* Convert the values to an ASCII representation and put that into * `values'. */ - status = wh_value_list_to_string (values, sizeof (values), ds, vl); + status = format_values (values, sizeof (values), ds, vl, cb->store_rates); if (status != 0) { ERROR ("write_http plugin: error with " "wh_value_list_to_string"); @@ -320,8 +307,10 @@ static int wh_write_command (const data_set_t *ds, const value_list_t *vl, /* {{ } command_len = (size_t) ssnprintf (command, sizeof (command), - "PUTVAL %s interval=%i %s\n", - key, vl->interval, values); + "PUTVAL %s interval=%.3f %s\r\n", + key, + CDTIME_T_TO_DOUBLE (vl->interval), + values); if (command_len >= sizeof (command)) { ERROR ("write_http plugin: Command buffer too small: " "Need %zu bytes.", command_len + 1); @@ -343,7 +332,7 @@ static int wh_write_command (const data_set_t *ds, const value_list_t *vl, /* {{ if (command_len >= cb->send_buffer_free) { - status = wh_flush_nolock (/* timeout = */ -1, cb); + status = wh_flush_nolock (/* timeout = */ 0, cb); if (status != 0) { pthread_mutex_unlock (&cb->send_lock); @@ -371,6 +360,60 @@ static int wh_write_command (const data_set_t *ds, const value_list_t *vl, /* {{ return (0); } /* }}} int wh_write_command */ +static int wh_write_json (const data_set_t *ds, const value_list_t *vl, /* {{{ */ + wh_callback_t *cb) +{ + int status; + + pthread_mutex_lock (&cb->send_lock); + + if (cb->curl == NULL) + { + status = wh_callback_init (cb); + if (status != 0) + { + ERROR ("write_http plugin: wh_callback_init failed."); + pthread_mutex_unlock (&cb->send_lock); + return (-1); + } + } + + status = format_json_value_list (cb->send_buffer, + &cb->send_buffer_fill, + &cb->send_buffer_free, + ds, vl, cb->store_rates); + if (status == (-ENOMEM)) + { + status = wh_flush_nolock (/* timeout = */ 0, cb); + if (status != 0) + { + wh_reset_buffer (cb); + pthread_mutex_unlock (&cb->send_lock); + return (status); + } + + status = format_json_value_list (cb->send_buffer, + &cb->send_buffer_fill, + &cb->send_buffer_free, + ds, vl, cb->store_rates); + } + if (status != 0) + { + pthread_mutex_unlock (&cb->send_lock); + return (status); + } + + DEBUG ("write_http plugin: <%s> buffer %zu/%zu (%g%%)", + cb->location, + cb->send_buffer_fill, sizeof (cb->send_buffer), + 100.0 * ((double) cb->send_buffer_fill) / ((double) sizeof (cb->send_buffer))); + + /* Check if we have enough space for this command. */ + pthread_mutex_unlock (&cb->send_lock); + + return (0); +} /* }}} int wh_write_json */ + static int wh_write (const data_set_t *ds, const value_list_t *vl, /* {{{ */ user_data_t *user_data) { @@ -382,10 +425,83 @@ static int wh_write (const data_set_t *ds, const value_list_t *vl, /* {{{ */ cb = user_data->data; - status = wh_write_command (ds, vl, cb); + if (cb->format == WH_FORMAT_JSON) + status = wh_write_json (ds, vl, cb); + else + status = wh_write_command (ds, vl, cb); + return (status); } /* }}} int wh_write */ +static int config_set_string (char **ret_string, /* {{{ */ + oconfig_item_t *ci) +{ + char *string; + + if ((ci->values_num != 1) + || (ci->values[0].type != OCONFIG_TYPE_STRING)) + { + WARNING ("write_http plugin: The `%s' config option " + "needs exactly one string argument.", ci->key); + return (-1); + } + + string = strdup (ci->values[0].value.string); + if (string == NULL) + { + ERROR ("write_http plugin: strdup failed."); + return (-1); + } + + if (*ret_string != NULL) + free (*ret_string); + *ret_string = string; + + return (0); +} /* }}} int config_set_string */ + +static int config_set_boolean (int *dest, oconfig_item_t *ci) /* {{{ */ +{ + if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN)) + { + WARNING ("write_http plugin: The `%s' config option " + "needs exactly one boolean argument.", ci->key); + return (-1); + } + + *dest = ci->values[0].value.boolean ? 1 : 0; + + return (0); +} /* }}} int config_set_boolean */ + +static int config_set_format (wh_callback_t *cb, /* {{{ */ + oconfig_item_t *ci) +{ + char *string; + + if ((ci->values_num != 1) + || (ci->values[0].type != OCONFIG_TYPE_STRING)) + { + WARNING ("write_http plugin: The `%s' config option " + "needs exactly one string argument.", ci->key); + return (-1); + } + + string = ci->values[0].value.string; + if (strcasecmp ("Command", string) == 0) + cb->format = WH_FORMAT_COMMAND; + else if (strcasecmp ("JSON", string) == 0) + cb->format = WH_FORMAT_JSON; + else + { + ERROR ("write_http plugin: Invalid format string: %s", + string); + return (-1); + } + + return (0); +} /* }}} int config_set_string */ + static int wh_config_url (oconfig_item_t *ci) /* {{{ */ { wh_callback_t *cb; @@ -399,6 +515,15 @@ static int wh_config_url (oconfig_item_t *ci) /* {{{ */ return (-1); } memset (cb, 0, sizeof (*cb)); + cb->location = NULL; + cb->user = NULL; + cb->pass = NULL; + cb->credentials = NULL; + cb->verify_peer = 1; + cb->verify_host = 1; + cb->cacert = NULL; + cb->format = WH_FORMAT_COMMAND; + cb->curl = NULL; pthread_mutex_init (&cb->send_lock, /* attr = */ NULL); @@ -414,6 +539,16 @@ static int wh_config_url (oconfig_item_t *ci) /* {{{ */ config_set_string (&cb->user, child); else if (strcasecmp ("Password", child->key) == 0) config_set_string (&cb->pass, child); + else if (strcasecmp ("VerifyPeer", child->key) == 0) + config_set_boolean (&cb->verify_peer, child); + else if (strcasecmp ("VerifyHost", child->key) == 0) + config_set_boolean (&cb->verify_host, child); + else if (strcasecmp ("CACert", child->key) == 0) + config_set_string (&cb->cacert, child); + else if (strcasecmp ("Format", child->key) == 0) + config_set_format (cb, child); + else if (strcasecmp ("StoreRates", child->key) == 0) + config_set_boolean (&cb->store_rates, child); else { ERROR ("write_http plugin: Invalid configuration "