]> arthur.barton.de Git - netdata.git/blobdiff - src/plugin_nfacct.c
convert nfacct to linked-list instead of array
[netdata.git] / src / plugin_nfacct.c
index e513e5d0e7a09ab37b262159d7220de428d21ba5..09309ae35f06b831d4b601828c91be6f11ebe525 100644 (file)
@@ -4,58 +4,74 @@
 #include <libmnl/libmnl.h>
 #include <libnetfilter_acct/libnetfilter_acct.h>
 
-struct mynfacct {
-    const char *name;
+struct nfacct_data {
+    char *name;
+    uint32_t hash;
+
     uint64_t pkts;
     uint64_t bytes;
-    struct nfacct *nfacct;
-};
 
-struct nfacct_list {
-    int size;
-    int len;
-    struct mynfacct data[];
-} *nfacct_list = NULL;
+    RRDDIM *rd_bytes;
+    RRDDIM *rd_packets;
 
-static int nfacct_callback(const struct nlmsghdr *nlh, void *data) {
-    if(data) {};
+    int updated;
 
-    if(!nfacct_list || nfacct_list->len == nfacct_list->size) {
-        int size = (nfacct_list) ? nfacct_list->size : 0;
-        int len = (nfacct_list) ? nfacct_list->len : 0;
-        size++;
+    struct nfacct_data *next;
+};
 
-        info("nfacct.plugin: increasing nfacct_list to size %d", size);
+static struct nfacct_list {
+    struct nfacct *nfacct_buffer;
+    struct nfacct_data *nfacct_metrics;
+} nfacct_root = {
+        .nfacct_buffer = NULL,
+        .nfacct_metrics = NULL
+};
 
-        nfacct_list = reallocz(nfacct_list, sizeof(struct nfacct_list) + (sizeof(struct mynfacct) * size));
+static inline struct nfacct_data *nfacct_data_get(const char *name, uint32_t hash) {
+    struct nfacct_data *d = NULL, *last = NULL;
+    for(d = nfacct_root.nfacct_metrics; d ; last = d, d = d->next) {
+        if(unlikely(d->hash == hash && !strcmp(d->name, name)))
+            return d;
+    }
 
-        nfacct_list->data[len].nfacct = nfacct_alloc();
-        if(!nfacct_list->data[size - 1].nfacct) {
-            error("nfacct.plugin: nfacct_alloc() failed.");
-            free(nfacct_list);
-            nfacct_list = NULL;
-            return MNL_CB_OK;
-        }
+    d = callocz(1, sizeof(struct nfacct_data));
+    d->name = strdupz(name);
+    d->hash = hash;
 
-        nfacct_list->size = size;
-        nfacct_list->len = len;
+    if(!last) {
+        d->next = nfacct_root.nfacct_metrics;
+        nfacct_root.nfacct_metrics = d;
+    }
+    else {
+        d->next = last->next;
+        last->next = d;
     }
 
-    if(nfacct_nlmsg_parse_payload(nlh, nfacct_list->data[nfacct_list->len].nfacct) < 0) {
+    return d;
+}
+
+static int nfacct_callback(const struct nlmsghdr *nlh, void *data) {
+    (void)data;
+
+    if(nfacct_nlmsg_parse_payload(nlh, nfacct_root.nfacct_buffer) < 0) {
         error("nfacct.plugin: nfacct_nlmsg_parse_payload() failed.");
         return MNL_CB_OK;
     }
 
-    nfacct_list->data[nfacct_list->len].name  = nfacct_attr_get_str(nfacct_list->data[nfacct_list->len].nfacct, NFACCT_ATTR_NAME);
-    nfacct_list->data[nfacct_list->len].pkts  = nfacct_attr_get_u64(nfacct_list->data[nfacct_list->len].nfacct, NFACCT_ATTR_PKTS);
-    nfacct_list->data[nfacct_list->len].bytes = nfacct_attr_get_u64(nfacct_list->data[nfacct_list->len].nfacct, NFACCT_ATTR_BYTES);
+    const char *name = nfacct_attr_get_str(nfacct_root.nfacct_buffer, NFACCT_ATTR_NAME);
+    uint32_t hash = simple_hash(name);
+
+    struct nfacct_data *d = nfacct_data_get(name, hash);
+
+    d->pkts  = nfacct_attr_get_u64(nfacct_root.nfacct_buffer, NFACCT_ATTR_PKTS);
+    d->bytes = nfacct_attr_get_u64(nfacct_root.nfacct_buffer, NFACCT_ATTR_BYTES);
+    d->updated = 1;
 
-    nfacct_list->len++;
     return MNL_CB_OK;
 }
 
 void *nfacct_main(void *ptr) {
-    if(ptr) { ; }
+    struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr;
 
     info("NFACCT thread created with task id %d", gettid());
 
@@ -65,133 +81,170 @@ void *nfacct_main(void *ptr) {
     if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
         error("nfacct.plugin: Cannot set pthread cancel state to ENABLE.");
 
+    nfacct_root.nfacct_buffer = nfacct_alloc();
+    if(!nfacct_root.nfacct_buffer)
+        fatal("nfacct.plugin: nfacct_alloc() failed.");
+
     char buf[MNL_SOCKET_BUFFER_SIZE];
     struct mnl_socket *nl = NULL;
     struct nlmsghdr *nlh = NULL;
     unsigned int seq = 0, portid = 0;
 
-    seq = time(NULL) - 1;
+    seq = (unsigned int)now_realtime_sec() - 1;
 
     nl  = mnl_socket_open(NETLINK_NETFILTER);
     if(!nl) {
         error("nfacct.plugin: mnl_socket_open() failed");
-        pthread_exit(NULL);
-        return NULL;
+        goto cleanup;
     }
 
     if(mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
-        mnl_socket_close(nl);
         error("nfacct.plugin: mnl_socket_bind() failed");
-        pthread_exit(NULL);
-        return NULL;
+        goto cleanup;
     }
     portid = mnl_socket_get_portid(nl);
 
     // ------------------------------------------------------------------------
 
-    struct timeval last, now;
-    unsigned long long usec = 0, susec = 0;
-    RRDSET *st = NULL;
-
-    gettimeofday(&last, NULL);
+    RRDSET *st_bytes = NULL, *st_packets = NULL;
 
     // ------------------------------------------------------------------------
 
-    while(1) {
+    int update_every = (int)config_get_number("plugin:nfacct", "update every", localhost->rrd_update_every);
+    if(update_every < localhost->rrd_update_every)
+        update_every = localhost->rrd_update_every;
+
+    usec_t step = update_every * USEC_PER_SEC;
+    heartbeat_t hb;
+    heartbeat_init(&hb);
+    for(;;) {
+        heartbeat_dt_usec(&hb);
+        heartbeat_next(&hb, step);
+
         if(unlikely(netdata_exit)) break;
 
         seq++;
 
-        nlh = nfacct_nlmsg_build_hdr(buf, NFNL_MSG_ACCT_GET, NLM_F_DUMP, seq);
+        nlh = nfacct_nlmsg_build_hdr(buf, NFNL_MSG_ACCT_GET, NLM_F_DUMP, (uint32_t)seq);
         if(!nlh) {
-            mnl_socket_close(nl);
             error("nfacct.plugin: nfacct_nlmsg_build_hdr() failed");
-            pthread_exit(NULL);
-            return NULL;
+            goto cleanup;
         }
 
         if(mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
             error("nfacct.plugin: mnl_socket_send");
-            pthread_exit(NULL);
-            return NULL;
+            goto cleanup;
         }
 
-        if(nfacct_list) nfacct_list->len = 0;
-
-        int ret;
+        ssize_t ret;
         while((ret = mnl_socket_recvfrom(nl, buf, sizeof(buf))) > 0) {
-            if((ret = mnl_cb_run(buf, ret, seq, portid, nfacct_callback, NULL)) <= 0) break;
+            if((ret = mnl_cb_run(buf, (size_t)ret, seq, portid, nfacct_callback, NULL)) <= 0) break;
         }
 
         if (ret == -1) {
-            error("nfacct.plugin: error communicating with kernel.");
-            pthread_exit(NULL);
-            return NULL;
+            error("nfacct.plugin: error communicating with kernel. NFACCT plugin can only work when netdata runs as root.");
+            goto cleanup;
         }
 
         // --------------------------------------------------------------------
 
-        gettimeofday(&now, NULL);
-        usec = usec_dt(&now, &last) - susec;
-        debug(D_NFACCT_LOOP, "nfacct.plugin: last loop took %llu usec (worked for %llu, sleeped for %llu).", usec + susec, usec, susec);
-
-        if(usec < (rrd_update_every * 1000000ULL / 2ULL)) susec = (rrd_update_every * 1000000ULL) - usec;
-        else susec = rrd_update_every * 1000000ULL / 2ULL;
-
-
-        // --------------------------------------------------------------------
-
-        if(nfacct_list && nfacct_list->len) {
-            int i;
-
-            st = rrdset_find_bytype("netfilter", "nfacct_packets");
-            if(!st) {
-                st = rrdset_create("netfilter", "nfacct_packets", NULL, "nfacct", NULL, "Netfilter Accounting Packets", "packets/s", 3206, rrd_update_every, RRDSET_TYPE_STACKED);
-
-                for(i = 0; i < nfacct_list->len ; i++)
-                    rrddim_add(st, nfacct_list->data[i].name, NULL, 1, rrd_update_every, RRDDIM_INCREMENTAL);
+        if(nfacct_root.nfacct_metrics) {
+            struct nfacct_data *d;
+
+            if(!st_packets) {
+                st_packets = rrdset_create_localhost(
+                        "netfilter"
+                        , "nfacct_packets"
+                        , NULL
+                        , "nfacct"
+                        , NULL
+                        , "Netfilter Accounting Packets"
+                        , "packets/s"
+                        , 3206
+                        , update_every
+                        , RRDSET_TYPE_STACKED
+                );
             }
-            else rrdset_next(st);
-
-            for(i = 0; i < nfacct_list->len ; i++) {
-                RRDDIM *rd = rrddim_find(st, nfacct_list->data[i].name);
-
-                if(!rd) rd = rrddim_add(st, nfacct_list->data[i].name, NULL, 1, rrd_update_every, RRDDIM_INCREMENTAL);
-                if(rd) rrddim_set_by_pointer(st, rd, nfacct_list->data[i].pkts);
+            else rrdset_next(st_packets);
+
+            for(d = nfacct_root.nfacct_metrics; d ; d = d->next) {
+                if(likely(d->updated)) {
+                    if(unlikely(!d->rd_packets))
+                        d->rd_packets = rrddim_add(
+                                st_packets
+                                , d->name
+                                , NULL
+                                , 1
+                                , update_every
+                                , RRD_ALGORITHM_INCREMENTAL
+                        );
+
+                    rrddim_set_by_pointer(
+                            st_packets
+                            , d->rd_packets
+                            , (collected_number)d->pkts
+                    );
+                }
             }
 
-            rrdset_done(st);
+            rrdset_done(st_packets);
 
             // ----------------------------------------------------------------
 
-            st = rrdset_find_bytype("netfilter", "nfacct_bytes");
-            if(!st) {
-                st = rrdset_create("netfilter", "nfacct_bytes", NULL, "nfacct", NULL, "Netfilter Accounting Bandwidth", "kilobytes/s", 3207, rrd_update_every, RRDSET_TYPE_STACKED);
-
-                for(i = 0; i < nfacct_list->len ; i++)
-                    rrddim_add(st, nfacct_list->data[i].name, NULL, 1, 1000 * rrd_update_every, RRDDIM_INCREMENTAL);
+            st_bytes = rrdset_find_bytype_localhost("netfilter", "nfacct_bytes");
+            if(!st_bytes) {
+                st_bytes = rrdset_create_localhost(
+                        "netfilter"
+                        , "nfacct_bytes"
+                        , NULL
+                        , "nfacct"
+                        , NULL
+                        , "Netfilter Accounting Bandwidth"
+                        , "kilobytes/s"
+                        , 3207
+                        , update_every
+                        , RRDSET_TYPE_STACKED
+                );
             }
-            else rrdset_next(st);
-
-            for(i = 0; i < nfacct_list->len ; i++) {
-                RRDDIM *rd = rrddim_find(st, nfacct_list->data[i].name);
-
-                if(!rd) rd = rrddim_add(st, nfacct_list->data[i].name, NULL, 1, 1000 * rrd_update_every, RRDDIM_INCREMENTAL);
-                if(rd) rrddim_set_by_pointer(st, rd, nfacct_list->data[i].bytes);
+            else rrdset_next(st_bytes);
+
+            for(d = nfacct_root.nfacct_metrics; d ; d = d->next) {
+                if(likely(d->updated)) {
+                    if(unlikely(!d->rd_bytes))
+                        d->rd_bytes = rrddim_add(
+                                st_bytes
+                                , d->name
+                                , NULL
+                                , 1
+                                , 1000 * update_every
+                                , RRD_ALGORITHM_INCREMENTAL
+                        );
+
+                    rrddim_set_by_pointer(
+                            st_bytes
+                            , d->rd_bytes
+                            , (collected_number)d->bytes
+                    );
+                }
             }
 
-            rrdset_done(st);
-        }
+            rrdset_done(st_bytes);
 
-        // --------------------------------------------------------------------
 
-        usleep(susec);
+            // ----------------------------------------------------------------
+            // prepare for the next loop
 
-        // copy current to last
-        bcopy(&now, &last, sizeof(struct timeval));
+            for(d = nfacct_root.nfacct_metrics; d ; d = d->next)
+                d->updated = 0;
+        }
     }
 
-    mnl_socket_close(nl);
+cleanup:
+    info("NFACCT thread exiting");
+
+    if(nl) mnl_socket_close(nl);
+
+    static_thread->enabled = 0;
     pthread_exit(NULL);
     return NULL;
 }