ceph plugin: Rewrite the BUFFER_ADD() macro.
[collectd.git] / src / ceph.c
index 5b83adc..5b79239 100644 (file)
@@ -27,6 +27,7 @@
 #define _BSD_SOURCE
 
 #include "collectd.h"
+
 #include "common.h"
 #include "plugin.h"
 
@@ -37,6 +38,9 @@
 #if HAVE_YAJL_YAJL_VERSION_H
 #include <yajl/yajl_version.h>
 #endif
+#ifdef HAVE_SYS_CAPABILITY_H
+# include <sys/capability.h>
+#endif
 
 #include <limits.h>
 #include <poll.h>
@@ -45,7 +49,6 @@
 #include <stdlib.h>
 #include <string.h>
 #include <strings.h>
-#include <sys/socket.h>
 #include <sys/time.h>
 #include <sys/types.h>
 #include <sys/un.h>
@@ -97,7 +100,7 @@ enum ceph_dset_type_d
 };
 
 /** Valid types for ceph defined in types.db */
-const char * ceph_dset_types [CEPH_DSET_TYPES_NUM] =
+static const char * const ceph_dset_types [CEPH_DSET_TYPES_NUM] =
                                    {"ceph_latency", "ceph_bytes", "ceph_rate"};
 
 /******* ceph_daemon *******/
@@ -135,11 +138,10 @@ struct yajl_struct
 {
     node_handler_t handler;
     void * handler_arg;
-    struct {
-      char key[DATA_MAX_NAME_LEN];
-      int key_len;
-    } state[YAJL_MAX_DEPTH];
-    int depth;
+
+    char *key;
+    char *stack[YAJL_MAX_DEPTH];
+    size_t depth;
 };
 typedef struct yajl_struct yajl_struct;
 
@@ -165,7 +167,7 @@ static int convert_special_metrics = 1;
 static struct ceph_daemon **g_daemons = NULL;
 
 /** Number of elements in g_daemons */
-static int g_num_daemons = 0;
+static size_t g_num_daemons = 0;
 
 /**
  * A set of data that we build up in memory while parsing the JSON.
@@ -260,76 +262,80 @@ static int ceph_cb_boolean(void *ctx, int bool_val)
 
 #define BUFFER_ADD(dest, src) do { \
     size_t dest_size = sizeof (dest); \
-    strncat ((dest), (src), dest_size - strlen (dest)); \
+    size_t dest_len = strlen (dest); \
+    if (dest_size > dest_len) { \
+        sstrncpy ((dest) + dest_len, (src), dest_size - dest_len); \
+    } \
     (dest)[dest_size - 1] = 0; \
 } while (0)
 
 static int
 ceph_cb_number(void *ctx, const char *number_val, yajl_len_t number_len)
 {
-    yajl_struct *yajl = (yajl_struct*)ctx;
+    yajl_struct *state = (yajl_struct*) ctx;
     char buffer[number_len+1];
-    int i, status;
-    char key[2 * DATA_MAX_NAME_LEN];
+    char key[2 * DATA_MAX_NAME_LEN] = { 0 };
     _Bool latency_type = 0;
+    int status;
 
     memcpy(buffer, number_val, number_len);
-    buffer[sizeof(buffer) - 1] = 0;
+    buffer[sizeof(buffer) - 1] = '\0';
 
-    sstrncpy (key, yajl->state[0].key, sizeof (key));
-    for (i = 1; i < yajl->depth; i++)
+    for (size_t i = 0; i < state->depth; i++)
     {
-        /* Special case for latency metrics. */
-        if ((i == yajl->depth-1)
-                && ((strcmp(yajl->state[i].key,"avgcount") == 0)
-                    || (strcmp(yajl->state[i].key,"sum") == 0)))
-        {
-            /* Super-special case for filestore:JournalWrBytes. For some
-             * reason, Ceph schema encodes this as a count/sum pair while all
-             * other "Bytes" data (excluding used/capacity bytes for OSD space)
-             * uses a single "Derive" type. To spare further confusion, keep
-             * this KPI as the same type of other "Bytes". Instead of keeping
-             * an "average" or "rate", use the "sum" in the pair and assign
-             * that to the derive value. */
-            if (convert_special_metrics && (i >= 2)
-                    && (strcmp("filestore", yajl->state[i-2].key) == 0)
-                    && (strcmp("journal_wr_bytes", yajl->state[i-1].key) == 0)
-                    && (strcmp("avgcount", yajl->state[i].key) == 0))
-            {
-                DEBUG("ceph plugin: Skipping avgcount for filestore.JournalWrBytes");
-                yajl->depth -= 1;
-                return CEPH_CB_CONTINUE;
-            }
+        if (state->stack[i] == NULL)
+            continue;
 
-            /* Don't add "avgcount" or "sum" to the key just yet. If the
-             * handler function signals RETRY_AVGCOUNT, we'll add it and try
-             * again. */
-            latency_type = 1;
-            break;
-        }
+        if (strlen (key) != 0)
+            BUFFER_ADD (key, ".");
+        BUFFER_ADD (key, state->stack[i]);
+    }
+
+    /* Special case for latency metrics. */
+    if ((strcmp ("avgcount", state->key) == 0)
+        || (strcmp ("sum", state->key) == 0))
+    {
+        latency_type = 1;
 
+        /* Super-special case for filestore.journal_wr_bytes.avgcount: For
+         * some reason, Ceph schema encodes this as a count/sum pair while all
+         * other "Bytes" data (excluding used/capacity bytes for OSD space) uses
+         * a single "Derive" type. To spare further confusion, keep this KPI as
+         * the same type of other "Bytes". Instead of keeping an "average" or
+         * "rate", use the "sum" in the pair and assign that to the derive
+         * value. */
+        if (convert_special_metrics && (state->depth >= 2)
+            && (strcmp("filestore", state->stack[state->depth - 2]) == 0)
+            && (strcmp("journal_wr_bytes", state->stack[state->depth - 1]) == 0)
+            && (strcmp("avgcount", state->key) == 0))
+        {
+            DEBUG("ceph plugin: Skipping avgcount for filestore.JournalWrBytes");
+            return CEPH_CB_CONTINUE;
+        }
+    }
+    else /* not a latency type */
+    {
         BUFFER_ADD (key, ".");
-        BUFFER_ADD (key, yajl->state[i].key);
+        BUFFER_ADD (key, state->key);
     }
 
-    status = yajl->handler(yajl->handler_arg, buffer, key);
+    status = state->handler(state->handler_arg, buffer, key);
     if((status == RETRY_AVGCOUNT) && latency_type)
     {
         /* Add previously skipped part of the key, either "avgcount" or "sum",
          * and try again. */
         BUFFER_ADD (key, ".");
-        BUFFER_ADD (key, yajl->state[yajl->depth-1].key);
+        BUFFER_ADD (key, state->key);
 
-        status = yajl->handler(yajl->handler_arg, buffer, key);
+        status = state->handler(state->handler_arg, buffer, key);
     }
 
-    if(status == -ENOMEM)
+    if (status != 0)
     {
-        ERROR("ceph plugin: memory allocation failed");
+        ERROR("ceph plugin: JSON handler failed with status %d.", status);
         return CEPH_CB_ABORT;
     }
 
-    yajl->depth -= 1;
     return CEPH_CB_CONTINUE;
 }
 
@@ -341,37 +347,52 @@ static int ceph_cb_string(void *ctx, const unsigned char *string_val,
 
 static int ceph_cb_start_map(void *ctx)
 {
+    yajl_struct *state = (yajl_struct*) ctx;
+
+    /* Push key to the stack */
+    if (state->depth == YAJL_MAX_DEPTH)
+        return CEPH_CB_ABORT;
+
+    state->stack[state->depth] = state->key;
+    state->depth++;
+    state->key = NULL;
+
     return CEPH_CB_CONTINUE;
 }
 
-static int
-ceph_cb_map_key(void *ctx, const unsigned char *key, yajl_len_t string_len)
+static int ceph_cb_end_map(void *ctx)
 {
-    yajl_struct *yajl = (yajl_struct*)ctx;
+    yajl_struct *state = (yajl_struct*) ctx;
 
-    if((yajl->depth+1)  >= YAJL_MAX_DEPTH)
-    {
-        ERROR("ceph plugin: depth exceeds max, aborting.");
+    /* Pop key from the stack */
+    if (state->depth == 0)
         return CEPH_CB_ABORT;
-    }
-
-    char buffer[string_len+1];
 
-    memcpy(buffer, key, string_len);
-    buffer[sizeof(buffer) - 1] = 0;
-
-    snprintf(yajl->state[yajl->depth].key, sizeof(buffer), "%s", buffer);
-    yajl->state[yajl->depth].key_len = sizeof(buffer);
-    yajl->depth = (yajl->depth + 1);
+    sfree (state->key);
+    state->depth--;
+    state->key = state->stack[state->depth];
+    state->stack[state->depth] = NULL;
 
     return CEPH_CB_CONTINUE;
 }
 
-static int ceph_cb_end_map(void *ctx)
+static int
+ceph_cb_map_key(void *ctx, const unsigned char *key, yajl_len_t string_len)
 {
-    yajl_struct *yajl = (yajl_struct*)ctx;
+    yajl_struct *state = (yajl_struct*) ctx;
+    size_t sz = ((size_t) string_len) + 1;
+
+    sfree (state->key);
+    state->key = malloc (sz);
+    if (state->key == NULL)
+    {
+        ERROR ("ceph plugin: malloc failed.");
+        return CEPH_CB_ABORT;
+    }
+
+    memmove (state->key, key, sz - 1);
+    state->key[sz - 1] = 0;
 
-    yajl->depth = (yajl->depth - 1);
     return CEPH_CB_CONTINUE;
 }
 
@@ -406,8 +427,7 @@ static void ceph_daemon_print(const struct ceph_daemon *d)
 
 static void ceph_daemons_print(void)
 {
-    int i;
-    for(i = 0; i < g_num_daemons; ++i)
+    for(size_t i = 0; i < g_num_daemons; ++i)
     {
         ceph_daemon_print(g_daemons[i]);
     }
@@ -415,15 +435,15 @@ static void ceph_daemons_print(void)
 
 static void ceph_daemon_free(struct ceph_daemon *d)
 {
-    int i = 0;
-    for(; i < d->last_idx; i++)
+    for(int i = 0; i < d->last_idx; i++)
     {
         sfree(d->last_poll_data[i]);
     }
     sfree(d->last_poll_data);
     d->last_poll_data = NULL;
     d->last_idx = 0;
-    for(i = 0; i < d->ds_num; i++)
+
+    for(int i = 0; i < d->ds_num; i++)
     {
         sfree(d->ds_names[i]);
     }
@@ -532,10 +552,9 @@ static _Bool has_suffix (char const *str, char const *suffix)
 /* count_parts returns the number of elements a "foo.bar.baz" style key has. */
 static size_t count_parts (char const *key)
 {
-    char const *ptr;
     size_t parts_num = 0;
 
-    for (ptr = key; ptr != NULL; ptr = strchr (ptr + 1, '.'))
+    for (const char *ptr = key; ptr != NULL; ptr = strchr (ptr + 1, '.'))
         parts_num++;
 
     return parts_num;
@@ -577,7 +596,6 @@ static int ceph_daemon_add_ds_entry(struct ceph_daemon *d, const char *name,
 {
     uint32_t type;
     char ds_name[DATA_MAX_NAME_LEN];
-    memset(ds_name, 0, sizeof(ds_name));
 
     if(convert_special_metrics)
     {
@@ -607,7 +625,7 @@ static int ceph_daemon_add_ds_entry(struct ceph_daemon *d, const char *name,
         return -ENOMEM;
     }
 
-    d->ds_names[d->ds_num] = malloc(sizeof(char) * DATA_MAX_NAME_LEN);
+    d->ds_names[d->ds_num] = malloc(DATA_MAX_NAME_LEN);
     if(!d->ds_names[d->ds_num])
     {
         return -ENOMEM;
@@ -668,10 +686,9 @@ static int cc_handle_bool(struct oconfig_item_s *item, int *dest)
 
 static int cc_add_daemon_config(oconfig_item_t *ci)
 {
-    int ret, i;
-    struct ceph_daemon *nd, cd;
+    int ret;
+    struct ceph_daemon *nd, cd = { 0 };
     struct ceph_daemon **tmp;
-    memset(&cd, 0, sizeof(struct ceph_daemon));
 
     if((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
     {
@@ -686,7 +703,7 @@ static int cc_add_daemon_config(oconfig_item_t *ci)
         return ret;
     }
 
-    for(i=0; i < ci->children_num; i++)
+    for(int i=0; i < ci->children_num; i++)
     {
         oconfig_item_t *child = ci->children + i;
 
@@ -731,21 +748,22 @@ static int cc_add_daemon_config(oconfig_item_t *ci)
     }
     g_daemons = tmp;
 
-    nd = malloc(sizeof(*nd));
+    nd = malloc(sizeof (*nd));
     if(!nd)
     {
         return ENOMEM;
     }
     memcpy(nd, &cd, sizeof(*nd));
-    g_daemons[g_num_daemons++] = nd;
+    g_daemons[g_num_daemons] = nd;
+    g_num_daemons++;
     return 0;
 }
 
 static int ceph_config(oconfig_item_t *ci)
 {
-    int ret, i;
+    int ret;
 
-    for(i = 0; i < ci->children_num; ++i)
+    for(int i = 0; i < ci->children_num; ++i)
     {
         oconfig_item_t *child = ci->children + i;
         if(strcasecmp("Daemon", child->key) == 0)
@@ -829,7 +847,7 @@ node_handler_define_schema(void *arg, const char *val, const char *key)
 static int add_last(struct ceph_daemon *d, const char *ds_n, double cur_sum,
         uint64_t cur_count)
 {
-    d->last_poll_data[d->last_idx] = malloc(1 * sizeof(struct last_data));
+    d->last_poll_data[d->last_idx] = malloc(sizeof (*d->last_poll_data[d->last_idx]));
     if(!d->last_poll_data[d->last_idx])
     {
         return -ENOMEM;
@@ -857,7 +875,7 @@ static int update_last(struct ceph_daemon *d, const char *ds_n, int index,
 
     if(!d->last_poll_data)
     {
-        d->last_poll_data = malloc(1 * sizeof(struct last_data *));
+        d->last_poll_data = malloc(sizeof (*d->last_poll_data));
         if(!d->last_poll_data)
         {
             return -ENOMEM;
@@ -882,8 +900,7 @@ static int update_last(struct ceph_daemon *d, const char *ds_n, int index,
  */
 static int backup_search_for_last_avg(struct ceph_daemon *d, const char *ds_n)
 {
-    int i = 0;
-    for(; i < d->last_idx; i++)
+    for(int i = 0; i < d->last_idx; i++)
     {
         if(strcmp(d->last_poll_data[i]->ds_name, ds_n) == 0)
         {
@@ -943,12 +960,11 @@ static double get_last_avg(struct ceph_daemon *d, const char *ds_n, int index,
  */
 static uint32_t backup_search_for_type(struct ceph_daemon *d, char *ds_name)
 {
-    int idx = 0;
-    for(; idx < d->ds_num; idx++)
+    for(int i = 0; i < d->ds_num; i++)
     {
-        if(strcmp(d->ds_names[idx], ds_name) == 0)
+        if(strcmp(d->ds_names[i], ds_name) == 0)
         {
-            return d->ds_types[idx];
+            return d->ds_types[i];
         }
     }
     return DSET_TYPE_UNFOUND;
@@ -967,7 +983,6 @@ static int node_handler_fetch_data(void *arg, const char *val, const char *key)
     int index = vtmp->index;
 
     char ds_name[DATA_MAX_NAME_LEN];
-    memset(ds_name, 0, sizeof(ds_name));
 
     if (parse_keys (ds_name, sizeof (ds_name), key))
     {
@@ -1070,7 +1085,7 @@ static int node_handler_fetch_data(void *arg, const char *val, const char *key)
 
 static int cconn_connect(struct cconn *io)
 {
-    struct sockaddr_un address;
+    struct sockaddr_un address = { 0 };
     int flags, fd, err;
     if(io->state != CSTATE_UNCONNECTED)
     {
@@ -1080,12 +1095,11 @@ static int cconn_connect(struct cconn *io)
     fd = socket(PF_UNIX, SOCK_STREAM, 0);
     if(fd < 0)
     {
-        int err = -errno;
+        err = -errno;
         ERROR("ceph plugin: cconn_connect: socket(PF_UNIX, SOCK_STREAM, 0) "
             "failed: error %d", err);
         return err;
     }
-    memset(&address, 0, sizeof(struct sockaddr_un));
     address.sun_family = AF_UNIX;
     snprintf(address.sun_path, sizeof(address.sun_path), "%s",
             io->d->asok_path);
@@ -1445,7 +1459,7 @@ static int milli_diff(const struct timeval *t1, const struct timeval *t2)
  */
 static int cconn_main_loop(uint32_t request_type)
 {
-    int i, ret, some_unreachable = 0;
+    int ret, some_unreachable = 0;
     struct timeval end_tv;
     struct cconn io_array[g_num_daemons];
 
@@ -1453,7 +1467,7 @@ static int cconn_main_loop(uint32_t request_type)
 
     /* create cconn array */
     memset(io_array, 0, sizeof(io_array));
-    for(i = 0; i < g_num_daemons; ++i)
+    for(size_t i = 0; i < g_num_daemons; ++i)
     {
         io_array[i].d = g_daemons[i];
         io_array[i].request_type = request_type;
@@ -1472,13 +1486,13 @@ static int cconn_main_loop(uint32_t request_type)
         struct pollfd fds[g_num_daemons];
         memset(fds, 0, sizeof(fds));
         nfds = 0;
-        for(i = 0; i < g_num_daemons; ++i)
+        for(size_t i = 0; i < g_num_daemons; ++i)
         {
             struct cconn *io = io_array + i;
             ret = cconn_prepare(io, fds + nfds);
             if(ret < 0)
             {
-                WARNING("ceph plugin: cconn_prepare(name=%s,i=%d,st=%d)=%d",
+                WARNING("ceph plugin: cconn_prepare(name=%s,i=%zu,st=%d)=%d",
                         io->d->name, i, io->state, ret);
                 cconn_close(io);
                 io->request_type = ASOK_REQ_NONE;
@@ -1510,13 +1524,14 @@ static int cconn_main_loop(uint32_t request_type)
             ERROR("ceph plugin: poll(2) error: %d", ret);
             goto done;
         }
-        for(i = 0; i < nfds; ++i)
+        for(int i = 0; i < nfds; ++i)
         {
             struct cconn *io = polled_io_array[i];
             int revents = fds[i].revents;
             if(revents == 0)
             {
                 /* do nothing */
+                continue;
             }
             else if(cconn_validate_revents(io, revents))
             {
@@ -1529,7 +1544,7 @@ static int cconn_main_loop(uint32_t request_type)
             }
             else
             {
-                int ret = cconn_handle_event(io);
+                ret = cconn_handle_event(io);
                 if(ret)
                 {
                     WARNING("ceph plugin: cconn_handle_event(name=%s,"
@@ -1541,7 +1556,7 @@ static int cconn_main_loop(uint32_t request_type)
             }
         }
     }
-    done: for(i = 0; i < g_num_daemons; ++i)
+    done: for(size_t i = 0; i < g_num_daemons; ++i)
     {
         cconn_close(io_array + i);
     }
@@ -1565,6 +1580,22 @@ static int ceph_read(void)
 static int ceph_init(void)
 {
     int ret;
+
+#if defined(HAVE_SYS_CAPABILITY_H) && defined(CAP_DAC_OVERRIDE)
+  if (check_capability (CAP_DAC_OVERRIDE) != 0)
+  {
+    if (getuid () == 0)
+      WARNING ("ceph plugin: Running collectd as root, but the "
+          "CAP_DAC_OVERRIDE capability is missing. The plugin's read "
+          "function will probably fail. Is your init system dropping "
+          "capabilities?");
+    else
+      WARNING ("ceph plugin: collectd doesn't have the CAP_DAC_OVERRIDE "
+          "capability. If you don't want to run collectd as root, try running "
+          "\"setcap cap_dac_override=ep\" on the collectd binary.");
+  }
+#endif
+
     ceph_daemons_print();
 
     ret = cconn_main_loop(ASOK_REQ_VERSION);
@@ -1574,8 +1605,7 @@ static int ceph_init(void)
 
 static int ceph_shutdown(void)
 {
-    int i;
-    for(i = 0; i < g_num_daemons; ++i)
+    for(size_t i = 0; i < g_num_daemons; ++i)
     {
         ceph_daemon_free(g_daemons[i]);
     }