Dear all, I have modified nginx.c using code from apache.c to enable multi-instance for nginx instance similar to apache plugin. The config syntax, therefore, is the same as apache plugin <Instance "name">nginx config ....</Instance> Please kindly review the patch. Thank you very much. Regards,msongd
diff -pur collectd-5.6.1/src/nginx.c collectd-5.6.1-p1/src/nginx.c --- collectd-5.6.1/src/nginx.c 2016-10-07 21:01:01.054990030 +0700 +++ collectd-5.6.1-p1/src/nginx.c 2016-10-20 13:45:27.564051653 +0700 @@ -33,278 +33,415 @@ #include <curl/curl.h> -static char *url = NULL; -static char *user = NULL; -static char *pass = NULL; -static char *verify_peer = NULL; -static char *verify_host = NULL; -static char *cacert = NULL; -static char *timeout = NULL; - -static CURL *curl = NULL; - -static char nginx_buffer[16384]; -static size_t nginx_buffer_len = 0; -static char nginx_curl_error[CURL_ERROR_SIZE]; - -static const char *config_keys[] = -{ - "URL", - "User", - "Password", - "VerifyPeer", - "VerifyHost", - "CACert", - "Timeout" -}; -static int config_keys_num = STATIC_ARRAY_SIZE (config_keys); - -static size_t nginx_curl_callback (void *buf, size_t size, size_t nmemb, - void __attribute__((unused)) *stream) +struct nginx_s { - size_t len = size * nmemb; + char *name; + char *host; + char *url; + char *user; + char *pass; + _Bool verify_peer; + _Bool verify_host; + char *cacert; + char *nginx_buffer; + char nginx_curl_error[CURL_ERROR_SIZE]; + size_t nginx_buffer_size ; + size_t nginx_buffer_fill ; + int timeout ; + CURL *curl; +}; /* nginx_s */ - /* Check if the data fits into the memory. If not, truncate it. */ - if ((nginx_buffer_len + len) >= sizeof (nginx_buffer)) - { - assert (sizeof (nginx_buffer) > nginx_buffer_len); - len = (sizeof (nginx_buffer) - 1) - nginx_buffer_len; - } +typedef struct nginx_s nginx_t; - if (len == 0) - return (len); +static void nginx_free (void *arg) +{ + nginx_t *st = arg; - memcpy (&nginx_buffer[nginx_buffer_len], buf, len); - nginx_buffer_len += len; - nginx_buffer[nginx_buffer_len] = 0; + if (st == NULL) + return; - return (len); -} + sfree (st->name); + sfree (st->host); + sfree (st->url); + sfree (st->user); + sfree (st->pass); + sfree (st->cacert); + sfree (st->nginx_buffer); + if (st->curl) { + curl_easy_cleanup(st->curl); + st->curl = NULL; + } + sfree (st); +} /* nginx_free */ -static int config_set (char **var, const char *value) +static size_t nginx_curl_callback (void *buf, size_t size, size_t nmemb, + void *user_data) { - if (*var != NULL) - { - free (*var); - *var = NULL; - } - - if ((*var = strdup (value)) == NULL) - return (1); - else - return (0); + size_t len = size * nmemb; + nginx_t *st; + + st = user_data; + if (st == NULL) + { + ERROR ("nginx plugin: nginx_curl_callback: " + "user_data pointer is NULL."); + return (0); + } + + if (len == 0) + return (len); + + if ((st->nginx_buffer_fill + len) >= st->nginx_buffer_size) + { + char *temp; + + temp = realloc (st->nginx_buffer, + st->nginx_buffer_fill + len + 1); + if (temp == NULL) + { + ERROR ("nginx plugin: realloc failed."); + return (0); + } + st->nginx_buffer = temp; + st->nginx_buffer_size = st->nginx_buffer_fill + len + 1; + } + + memcpy (st->nginx_buffer + st->nginx_buffer_fill, (char *) buf, len); + st->nginx_buffer_fill += len; + st->nginx_buffer[st->nginx_buffer_fill] = 0; + + return (len); } -static int config (const char *key, const char *value) +/* initialize curl for each host */ +static int init_host (nginx_t *st) /* {{{ */ { - if (strcasecmp (key, "url") == 0) - return (config_set (&url, value)); - else if (strcasecmp (key, "user") == 0) - return (config_set (&user, value)); - else if (strcasecmp (key, "password") == 0) - return (config_set (&pass, value)); - else if (strcasecmp (key, "verifypeer") == 0) - return (config_set (&verify_peer, value)); - else if (strcasecmp (key, "verifyhost") == 0) - return (config_set (&verify_host, value)); - else if (strcasecmp (key, "cacert") == 0) - return (config_set (&cacert, value)); - else if (strcasecmp (key, "timeout") == 0) - return (config_set (&timeout, value)); - else - return (-1); -} /* int config */ + assert (st->url != NULL); + /* (Assured by `config_add') */ -static int init (void) -{ - if (curl != NULL) - curl_easy_cleanup (curl); + if (st->curl != NULL) + { + curl_easy_cleanup (st->curl); + st->curl = NULL; + } + + if ((st->curl = curl_easy_init ()) == NULL) + { + ERROR ("nginx plugin: init_host: `curl_easy_init' failed."); + return (-1); + } + + curl_easy_setopt (st->curl, CURLOPT_NOSIGNAL, 1L); + curl_easy_setopt (st->curl, CURLOPT_WRITEFUNCTION, nginx_curl_callback); + curl_easy_setopt (st->curl, CURLOPT_WRITEDATA, st); - if ((curl = curl_easy_init ()) == NULL) - { - ERROR ("nginx plugin: curl_easy_init failed."); - return (-1); - } - - curl_easy_setopt (curl, CURLOPT_NOSIGNAL, 1L); - curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, nginx_curl_callback); - curl_easy_setopt (curl, CURLOPT_USERAGENT, COLLECTD_USERAGENT); - curl_easy_setopt (curl, CURLOPT_ERRORBUFFER, nginx_curl_error); + curl_easy_setopt (st->curl, CURLOPT_USERAGENT, COLLECTD_USERAGENT); + curl_easy_setopt (st->curl, CURLOPT_ERRORBUFFER, st->nginx_curl_error); - if (user != NULL) - { + if (st->user != NULL) + { #ifdef HAVE_CURLOPT_USERNAME - curl_easy_setopt (curl, CURLOPT_USERNAME, user); - curl_easy_setopt (curl, CURLOPT_PASSWORD, (pass == NULL) ? "" : pass); + curl_easy_setopt (st->curl, CURLOPT_USERNAME, st->user); + curl_easy_setopt (st->curl, CURLOPT_PASSWORD, + (st->pass == NULL) ? "" : st->pass); #else - static char credentials[1024]; - int status = ssnprintf (credentials, sizeof (credentials), - "%s:%s", user, pass == NULL ? "" : pass); - if ((status < 0) || ((size_t) status >= sizeof (credentials))) - { - ERROR ("nginx plugin: Credentials would have been truncated."); - return (-1); - } + static char credentials[1024]; + int status; + + status = ssnprintf (credentials, sizeof (credentials), "%s:%s", + st->user, (st->pass == NULL) ? "" : st->pass); + if ((status < 0) || ((size_t) status >= sizeof (credentials))) + { + ERROR ("nginx plugin: init_host: Returning an error " + "because the credentials have been " + "truncated."); + curl_easy_cleanup (st->curl); + st->curl = NULL; + return (-1); + } - curl_easy_setopt (curl, CURLOPT_USERPWD, credentials); + curl_easy_setopt (st->curl, CURLOPT_USERPWD, credentials); #endif - } + } - if (url != NULL) - { - curl_easy_setopt (curl, CURLOPT_URL, url); - } - - curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, 1L); - curl_easy_setopt (curl, CURLOPT_MAXREDIRS, 50L); - - if ((verify_peer == NULL) || IS_TRUE (verify_peer)) - { - curl_easy_setopt (curl, CURLOPT_SSL_VERIFYPEER, 1L); - } - else - { - curl_easy_setopt (curl, CURLOPT_SSL_VERIFYPEER, 0L); - } - - if ((verify_host == NULL) || IS_TRUE (verify_host)) - { - curl_easy_setopt (curl, CURLOPT_SSL_VERIFYHOST, 2L); - } - else - { - curl_easy_setopt (curl, CURLOPT_SSL_VERIFYHOST, 0L); - } - - if (cacert != NULL) - { - curl_easy_setopt (curl, CURLOPT_CAINFO, cacert); - } + curl_easy_setopt (st->curl, CURLOPT_URL, st->url); + curl_easy_setopt (st->curl, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt (st->curl, CURLOPT_MAXREDIRS, 50L); + + curl_easy_setopt (st->curl, CURLOPT_SSL_VERIFYPEER, + (long) st->verify_peer); + curl_easy_setopt (st->curl, CURLOPT_SSL_VERIFYHOST, + st->verify_host ? 2L : 0L); + if (st->cacert != NULL) + curl_easy_setopt (st->curl, CURLOPT_CAINFO, st->cacert); #ifdef HAVE_CURLOPT_TIMEOUT_MS - if (timeout != NULL) - { - curl_easy_setopt (curl, CURLOPT_TIMEOUT_MS, atol(timeout)); - } - else - { - curl_easy_setopt (curl, CURLOPT_TIMEOUT_MS, (long) CDTIME_T_TO_MS(plugin_get_interval())); - } + if (st->timeout >= 0) + curl_easy_setopt (st->curl, CURLOPT_TIMEOUT_MS, (long) st->timeout); + else + curl_easy_setopt (st->curl, CURLOPT_TIMEOUT_MS, (long) CDTIME_T_TO_MS(plugin_get_interval())); #endif - return (0); -} /* void init */ + return (0); +} /* }}} int init_host */ + +static void submit_value (const char *type, const char *type_instance, + value_t value, nginx_t *st) +{ + value_list_t vl = VALUE_LIST_INIT; + + vl.values = &value; + vl.values_len = 1; + + sstrncpy (vl.host, (st->host != NULL) ? st->host : hostname_g, + sizeof (vl.host)); + + sstrncpy (vl.plugin, "nginx", sizeof (vl.plugin)); + if (st->name != NULL) + sstrncpy (vl.plugin_instance, st->name, + sizeof (vl.plugin_instance)); + + sstrncpy (vl.type, type, sizeof (vl.type)); + if (type_instance != NULL) + sstrncpy (vl.type_instance, type_instance, + sizeof (vl.type_instance)); -static void submit (const char *type, const char *inst, long long value) + plugin_dispatch_values (&vl); +} /* void submit_value */ + +static void submit_derive (const char *type, const char *type_instance, + derive_t c, nginx_t *st) +{ + value_t v; + v.derive = c; + submit_value (type, type_instance, v, st); +} /* void submit_derive */ + +static void submit_gauge (const char *type, const char *type_instance, + gauge_t g, nginx_t *st) +{ + value_t v; + v.gauge = g; + submit_value (type, type_instance, v, st); +} /* void submit_gauge */ + +static int nginx_read_host (user_data_t *user_data) /* {{{ */ +{ + char *ptr; + char *saveptr; + char *lines[16]; + int lines_num = 0; + + char *fields[16]; + int fields_num; + + nginx_t *st; + + st = user_data->data; + + int status; + + assert (st->url != NULL); + /* (Assured by `config_add') */ + + if (st->curl == NULL) + { + status = init_host (st); + if (status != 0) + return (-1); + } + assert (st->curl != NULL); + + st->nginx_buffer_fill = 0; + if (curl_easy_perform (st->curl) != CURLE_OK) + { + ERROR ("nginx: curl_easy_perform failed: %s", + st->nginx_curl_error); + return (-1); + } + + ptr = st->nginx_buffer; + saveptr = NULL; + while ((lines[lines_num] = strtok_r (ptr, "\n\r", &saveptr)) != NULL) + { + ptr = NULL; + lines_num++; + + if (lines_num >= 16) + break; + } + + /* + * Active connections: 291 + * server accepts handled requests + * 16630948 16630948 31070465 + * Reading: 6 Writing: 179 Waiting: 106 + */ + for (int i = 0; i < lines_num; i++) + { + fields_num = strsplit (lines[i], fields, + (sizeof (fields) / sizeof (fields[0]))); + + if (fields_num == 3) + { + if ((strcmp (fields[0], "Active") == 0) + && (strcmp (fields[1], "connections:") == 0)) + { + submit_gauge("nginx_connections", "active", atoll (fields[2]), st); + } + else if ((atoll (fields[0]) != 0) + && (atoll (fields[1]) != 0) + && (atoll (fields[2]) != 0)) + { + submit_derive("connections", "accepted", atoll(fields[0]), st); + submit_derive("connections", "handled", atoll(fields[1]), st); + submit_derive("nginx_requests", NULL, atoll(fields[2]), st); + } + } + else if (fields_num == 6) + { + if ((strcmp (fields[0], "Reading:") == 0) + && (strcmp (fields[2], "Writing:") == 0) + && (strcmp (fields[4], "Waiting:") == 0)) + { + submit_gauge("nginx_connections", "reading", atoll (fields[1]), st); + submit_gauge("nginx_connections", "writing", atoll (fields[3]), st); + submit_gauge("nginx_connections", "waiting", atoll (fields[5]), st); + } + } + } + + st->nginx_buffer_fill = 0; + + return (0); +} /* }}} int nginx_read_host */ + +static int config_add (oconfig_item_t *ci) +{ + nginx_t *st; + int status; + + st = calloc (1, sizeof (*st)); + if (st == NULL) + { + ERROR ("nginx plugin: calloc failed."); + return (-1); + } + + st->timeout = -1; + + status = cf_util_get_string (ci, &st->name); + if (status != 0) + { + sfree (st); + return (status); + } + assert (st->name != NULL); + + for (int i = 0; i < ci->children_num; i++) + { + oconfig_item_t *child = ci->children + i; + + if (strcasecmp ("URL", child->key) == 0) + status = cf_util_get_string (child, &st->url); + else if (strcasecmp ("Host", child->key) == 0) + status = cf_util_get_string (child, &st->host); + else if (strcasecmp ("User", child->key) == 0) + status = cf_util_get_string (child, &st->user); + else if (strcasecmp ("Password", child->key) == 0) + status = cf_util_get_string (child, &st->pass); + else if (strcasecmp ("VerifyPeer", child->key) == 0) + status = cf_util_get_boolean (child, &st->verify_peer); + else if (strcasecmp ("VerifyHost", child->key) == 0) + status = cf_util_get_boolean (child, &st->verify_host); + else if (strcasecmp ("CACert", child->key) == 0) + status = cf_util_get_string (child, &st->cacert); + else if (strcasecmp ("Timeout", child->key) == 0) + status = cf_util_get_int (child, &st->timeout); + else + { + WARNING ("nginx plugin: Option `%s' not allowed here.", + child->key); + status = -1; + } + + if (status != 0) + break; + } + + /* Check if struct is complete.. */ + if ((status == 0) && (st->url == NULL)) + { + ERROR ("nginx plugin: Instance `%s': " + "No URL has been configured.", + st->name); + status = -1; + } + + if (status == 0) + { + user_data_t ud = { + .data = st, + .free_func = nginx_free + }; + + char callback_name[3*DATA_MAX_NAME_LEN]; + + ssnprintf (callback_name, sizeof (callback_name), + "nginx/%s/%s", + (st->host != NULL) ? st->host : hostname_g, + (st->name != NULL) ? st->name : "default"); + + status = plugin_register_complex_read (/* group = */ NULL, + /* name = */ callback_name, + /* callback = */ nginx_read_host, + /* interval = */ 0, + /* user_data = */ &ud); + } + + if (status != 0) + { + nginx_free (st); + return (-1); + } + + return (0); +} /* int config_add */ + +static int config (oconfig_item_t *ci) { - value_t values[1]; - value_list_t vl = VALUE_LIST_INIT; + int status = 0; + + for (int i = 0; i < ci->children_num; i++) + { + oconfig_item_t *child = ci->children + i; + + if (strcasecmp ("Instance", child->key) == 0) + config_add (child); + else + WARNING ("nginx plugin: The configuration option " + "\"%s\" is not allowed here. Did you " + "forget to add an <Instance /> block " + "around the configuration?", + child->key); + } /* for (ci->children) */ - if (strcmp (type, "nginx_connections") == 0) - values[0].gauge = value; - else if (strcmp (type, "nginx_requests") == 0) - values[0].derive = value; - else if (strcmp (type, "connections") == 0) - values[0].derive = value; - else - return; - - vl.values = values; - vl.values_len = 1; - sstrncpy (vl.host, hostname_g, sizeof (vl.host)); - sstrncpy (vl.plugin, "nginx", sizeof (vl.plugin)); - sstrncpy (vl.plugin_instance, "", sizeof (vl.plugin_instance)); - sstrncpy (vl.type, type, sizeof (vl.type)); - - if (inst != NULL) - sstrncpy (vl.type_instance, inst, sizeof (vl.type_instance)); - - plugin_dispatch_values (&vl); -} /* void submit */ - -static int nginx_read (void) -{ - char *ptr; - char *lines[16]; - int lines_num = 0; - char *saveptr; - - char *fields[16]; - int fields_num; - - if (curl == NULL) - return (-1); - if (url == NULL) - return (-1); - - nginx_buffer_len = 0; - if (curl_easy_perform (curl) != CURLE_OK) - { - WARNING ("nginx plugin: curl_easy_perform failed: %s", nginx_curl_error); - return (-1); - } - - ptr = nginx_buffer; - saveptr = NULL; - while ((lines[lines_num] = strtok_r (ptr, "\n\r", &saveptr)) != NULL) - { - ptr = NULL; - lines_num++; - - if (lines_num >= 16) - break; - } - - /* - * Active connections: 291 - * server accepts handled requests - * 16630948 16630948 31070465 - * Reading: 6 Writing: 179 Waiting: 106 - */ - for (int i = 0; i < lines_num; i++) - { - fields_num = strsplit (lines[i], fields, - (sizeof (fields) / sizeof (fields[0]))); - - if (fields_num == 3) - { - if ((strcmp (fields[0], "Active") == 0) - && (strcmp (fields[1], "connections:") == 0)) - { - submit ("nginx_connections", "active", atoll (fields[2])); - } - else if ((atoll (fields[0]) != 0) - && (atoll (fields[1]) != 0) - && (atoll (fields[2]) != 0)) - { - submit ("connections", "accepted", atoll (fields[0])); - submit ("connections", "handled", atoll (fields[1])); - submit ("nginx_requests", NULL, atoll (fields[2])); - } - } - else if (fields_num == 6) - { - if ((strcmp (fields[0], "Reading:") == 0) - && (strcmp (fields[2], "Writing:") == 0) - && (strcmp (fields[4], "Waiting:") == 0)) - { - submit ("nginx_connections", "reading", atoll (fields[1])); - submit ("nginx_connections", "writing", atoll (fields[3])); - submit ("nginx_connections", "waiting", atoll (fields[5])); - } - } - } + return (status); +} /* int config */ - nginx_buffer_len = 0; - return (0); -} /* int nginx_read */ +static int nginx_init (void) /* {{{ */ +{ + /* Call this while collectd is still single-threaded to avoid + * initialization issues in libgcrypt. */ + curl_global_init (CURL_GLOBAL_SSL); + return (0); +} /* }}} int nginx_init */ void module_register (void) { - plugin_register_config ("nginx", config, config_keys, config_keys_num); - plugin_register_init ("nginx", init); - plugin_register_read ("nginx", nginx_read); + plugin_register_complex_config ("nginx", config); + plugin_register_init ("nginx", nginx_init); } /* void module_register */ /*
_______________________________________________ collectd mailing list collectd@verplant.org https://mailman.verplant.org/listinfo/collectd