]> arthur.barton.de Git - netdata.git/commitdiff
simple_pattern now also supports asterisks in the middle; cgroups cleanup;
authorCosta Tsaousis (ktsaou) <costa@tsaousis.gr>
Sun, 15 Jan 2017 04:03:41 +0000 (06:03 +0200)
committerCosta Tsaousis (ktsaou) <costa@tsaousis.gr>
Sun, 15 Jan 2017 04:03:41 +0000 (06:03 +0200)
CMakeLists.txt
src/Makefile.am
src/common.c
src/common.h
src/simple_pattern.c [new file with mode: 0644]
src/simple_pattern.h [new file with mode: 0644]
src/sys_fs_cgroup.c

index e49fd7963246c765f7ff7f4f50a0b26df48d63b4..b9ae6a6241d7e9eeeb1bf81b139abb5c6162fb86 100755 (executable)
@@ -101,7 +101,7 @@ set(NETDATA_SOURCE_FILES
         src/registry_person.c
         src/registry_person.h
         src/registry_machine.c
-        src/registry_machine.h src/registry_internals.c src/registry_init.c src/registry_db.c src/registry_log.c src/proc_uptime.c src/sys_devices_system_edac_mc.c src/plugin_proc_diskspace.c src/plugin_proc_diskspace.h)
+        src/registry_machine.h src/registry_internals.c src/registry_init.c src/registry_db.c src/registry_log.c src/proc_uptime.c src/sys_devices_system_edac_mc.c src/plugin_proc_diskspace.c src/plugin_proc_diskspace.h src/simple_pattern.c src/simple_pattern.h)
 
 set(APPS_PLUGIN_SOURCE_FILES
         src/appconfig.c
index 616a2786e568739c7424c3045b638e7f5b490a33..264e58dfb1485a13a8b2ec8a755268919f94d327 100644 (file)
@@ -46,6 +46,7 @@ netdata_SOURCES = \
        plugins_d.c plugins_d.h \
        popen.c popen.h \
        socket.c socket.h \
+       simple_pattern.c simple_pattern.h \
        sys_fs_cgroup.c \
        sys_devices_system_edac_mc.c \
        procfile.c procfile.h \
index 058721e9c549561bbc74db3df45ab1b4fb81a4af..36fa0c9be53468ff5cea44affbf86149da8b4422 100644 (file)
@@ -1187,129 +1187,3 @@ int read_single_number_file(const char *filename, unsigned long long *result) {
     *result = strtoull(buffer, NULL, 0);
     return 0;
 }
-
-// ----------------------------------------------------------------------------
-// simple_pattern_match
-
-struct simple_pattern {
-    const char *match;
-    size_t len;
-    NETDATA_SIMPLE_PREFIX_MODE mode;
-    struct simple_pattern *next;
-};
-
-NETDATA_SIMPLE_PATTERN *netdata_simple_pattern_list_create(const char *list, NETDATA_SIMPLE_PREFIX_MODE default_mode) {
-    struct simple_pattern *root = NULL, *last = NULL;
-
-    if(unlikely(!list || !*list)) return root;
-
-    char *a = strdupz(list);
-    if(a && *a) {
-        char *s = a;
-
-        while(s && *s) {
-            // skip all spaces
-            while(isspace(*s)) s++;
-
-            // empty string
-            if(unlikely(!*s)) break;
-
-            // find the next space
-            char *c = s;
-            while(*c && !isspace(*c)) c++;
-
-            // find the next word
-            char *n;
-            if(likely(*c)) n = c + 1;
-            else n = NULL;
-
-            // terminate our string
-            *c = '\0';
-
-            char buf[100 + 1];
-            strncpy(buf, s, 100);
-            buf[100] = '\0';
-            if(likely(n)) *c = ' ';
-            s = buf;
-
-            NETDATA_SIMPLE_PREFIX_MODE mode;
-            size_t len = strlen(s);
-            if(len >= 2 && *s == '*' && s[len - 1] == '*') {
-                s[len - 1] = '\0';
-                s++;
-                mode = NETDATA_SIMPLE_PATTERN_MODE_SUBSTRING;
-            }
-            else if(len >= 1 && *s == '*') {
-                s++;
-                mode = NETDATA_SIMPLE_PATTERN_MODE_SUFFIX;
-            }
-            else if(len >= 1 && s[len - 1] == '*') {
-                s[len - 1] = '\0';
-                mode = NETDATA_SIMPLE_PATTERN_MODE_PREFIX;
-            }
-            else
-                mode = default_mode;
-
-            // allocate the structure
-            struct simple_pattern *m = callocz(1, sizeof(struct simple_pattern));
-            if(*s) {
-                m->match = strdup(s);
-                m->len = strlen(m->match);
-                m->mode = mode;
-            }
-            else {
-                m->mode = NETDATA_SIMPLE_PATTERN_MODE_SUBSTRING;
-            }
-
-            // link it at the end
-            if(unlikely(!root))
-                root = last = m;
-            else {
-                last->next = m;
-                last = m;
-            }
-
-            // prepare for next loop
-            s = n;
-        }
-    }
-
-    free(a);
-    return (NETDATA_SIMPLE_PATTERN *)root;
-}
-
-int netdata_simple_pattern_list_matches(NETDATA_SIMPLE_PATTERN *list, const char *str) {
-    struct simple_pattern *m, *root = (struct simple_pattern *)list;
-
-    if(unlikely(!root)) return 0;
-
-    size_t len = strlen(str);
-    for(m = root; m ; m = m->next) {
-        if(m->len <= len) {
-            switch(m->mode) {
-                case NETDATA_SIMPLE_PATTERN_MODE_SUBSTRING:
-                    if(unlikely(!m->len || strstr(str, m->match)))
-                        return 1;
-                    break;
-
-                case NETDATA_SIMPLE_PATTERN_MODE_PREFIX:
-                    if(unlikely(strncmp(str, m->match, m->len) == 0))
-                        return 1;
-                    break;
-
-                case NETDATA_SIMPLE_PATTERN_MODE_SUFFIX:
-                    if(unlikely(strcmp(&str[len - m->len], m->match) == 0))
-                        return 1;
-                    break;
-
-                case NETDATA_SIMPLE_PATTERN_MODE_EXACT:
-                default:
-                    if(unlikely(strcmp(str, m->match) == 0))
-                        return 1;
-                    break;
-            }
-        }
-    }
-
-    return 0;
-}
index 04521023cbff3f9e5dcf7dbf2536082de9ade417..9e9cc8fc09c837efe1656a4d2893d7a52265e6d1 100644 (file)
 // ----------------------------------------------------------------------------
 // netdata include files
 
+#include "simple_pattern.h"
 #include "avl.h"
 #include "clocks.h"
 #include "log.h"
@@ -278,14 +279,4 @@ extern void get_system_HZ(void);
 
 extern int read_single_number_file(const char *filename, unsigned long long *result);
 
-typedef enum {
-    NETDATA_SIMPLE_PATTERN_MODE_EXACT,
-    NETDATA_SIMPLE_PATTERN_MODE_PREFIX,
-    NETDATA_SIMPLE_PATTERN_MODE_SUFFIX,
-    NETDATA_SIMPLE_PATTERN_MODE_SUBSTRING
-} NETDATA_SIMPLE_PREFIX_MODE;
-typedef void NETDATA_SIMPLE_PATTERN;
-extern NETDATA_SIMPLE_PATTERN *netdata_simple_pattern_list_create(const char *list, NETDATA_SIMPLE_PREFIX_MODE default_mode);
-extern int netdata_simple_pattern_list_matches(NETDATA_SIMPLE_PATTERN *list, const char *str);
-
 #endif /* NETDATA_COMMON_H */
diff --git a/src/simple_pattern.c b/src/simple_pattern.c
new file mode 100644 (file)
index 0000000..fe9e559
--- /dev/null
@@ -0,0 +1,204 @@
+#include "common.h"
+
+struct simple_pattern {
+    const char *match;
+    size_t len;
+    NETDATA_SIMPLE_PREFIX_MODE mode;
+
+    struct simple_pattern *child;
+
+    struct simple_pattern *next;
+};
+
+static inline struct simple_pattern *parse_pattern(const char *str, NETDATA_SIMPLE_PREFIX_MODE default_mode) {
+    info(">>>> PARSE: '%s'", str);
+
+    NETDATA_SIMPLE_PREFIX_MODE mode;
+    struct simple_pattern *child = NULL;
+
+    char *buf = strdupz(str);
+    char *s = buf, *c = buf;
+
+    // skip asterisks in front
+    while(*c == '*') c++;
+
+    // find the next asterisk
+    while(*c && *c != '*') c++ ;
+
+    // do we have an asterisk in the middle?
+    if(*c == '*' && c[1] != '\0') {
+        // yes, we have
+        child = parse_pattern(c, default_mode);
+        c[1] = '\0';
+    }
+
+    // check what this one matches
+
+    size_t len = strlen(s);
+    if(len >= 2 && *s == '*' && s[len - 1] == '*') {
+        s[len - 1] = '\0';
+        s++;
+        mode = NETDATA_SIMPLE_PATTERN_MODE_SUBSTRING;
+    }
+    else if(len >= 1 && *s == '*') {
+        s++;
+        mode = NETDATA_SIMPLE_PATTERN_MODE_SUFFIX;
+    }
+    else if(len >= 1 && s[len - 1] == '*') {
+        s[len - 1] = '\0';
+        mode = NETDATA_SIMPLE_PATTERN_MODE_PREFIX;
+    }
+    else
+        mode = default_mode;
+
+    // allocate the structure
+    struct simple_pattern *m = callocz(1, sizeof(struct simple_pattern));
+    if(*s) {
+        m->match = strdup(s);
+        m->len = strlen(m->match);
+        m->mode = mode;
+    }
+    else {
+        m->mode = NETDATA_SIMPLE_PATTERN_MODE_SUBSTRING;
+    }
+
+    m->child = child;
+
+    free(buf);
+
+    info("PATTERN '%s' is composed by", str);
+    struct simple_pattern *p;
+    for(p = m; p ; p = p->child)
+        info(">>>> COMPONENT: '%s%s%s' (len %zu type %u)",
+            (p->mode == NETDATA_SIMPLE_PATTERN_MODE_SUFFIX || p->mode == NETDATA_SIMPLE_PATTERN_MODE_SUBSTRING)?"*":"",
+            p->match,
+            (p->mode == NETDATA_SIMPLE_PATTERN_MODE_PREFIX || p->mode == NETDATA_SIMPLE_PATTERN_MODE_SUBSTRING)?"*":"",
+            p->len,
+            p->mode);
+
+    return m;
+}
+
+NETDATA_SIMPLE_PATTERN *netdata_simple_pattern_list_create(const char *list, NETDATA_SIMPLE_PREFIX_MODE default_mode) {
+    struct simple_pattern *root = NULL, *last = NULL;
+
+    if(unlikely(!list || !*list)) return root;
+
+    char *buf = strdupz(list);
+    if(buf && *buf) {
+        char *s = buf;
+
+        while(s && *s) {
+            // skip all spaces
+            while(isspace(*s)) s++;
+
+            // empty string
+            if(unlikely(!*s)) break;
+
+            // find the next space
+            char *c = s;
+            while(*c && !isspace(*c)) c++;
+
+            // find the next word
+            char *n;
+            if(likely(*c)) n = c + 1;
+            else n = NULL;
+
+            // terminate our string
+            *c = '\0';
+
+            struct simple_pattern *m = parse_pattern(s, default_mode);
+
+            if(likely(n)) *c = ' ';
+
+            // link it at the end
+            if(unlikely(!root))
+                root = last = m;
+            else {
+                last->next = m;
+                last = m;
+            }
+
+            // prepare for next loop
+            s = n;
+        }
+    }
+
+    free(buf);
+    return (NETDATA_SIMPLE_PATTERN *)root;
+}
+
+static inline int match_pattern(struct simple_pattern *m, const char *str, size_t len) {
+    info("CHECK string '%s' (len %zu) with pattern '%s%s%s' (len %zu type %u)", str, len,
+            (m->mode == NETDATA_SIMPLE_PATTERN_MODE_SUFFIX || m->mode == NETDATA_SIMPLE_PATTERN_MODE_SUBSTRING)?"*":"",
+            m->match,
+            (m->mode == NETDATA_SIMPLE_PATTERN_MODE_PREFIX || m->mode == NETDATA_SIMPLE_PATTERN_MODE_SUBSTRING)?"*":"",
+            m->len, m->mode);
+
+    char *s;
+
+    if(m->len <= len) {
+        switch(m->mode) {
+            case NETDATA_SIMPLE_PATTERN_MODE_SUBSTRING:
+                if(!m->len) return 1;
+                if((s = strstr(str, m->match))) {
+                    if(!m->child) return 1;
+                    return match_pattern(m->child, &s[m->len], len - (s - str) - m->len);
+                }
+                break;
+
+            case NETDATA_SIMPLE_PATTERN_MODE_PREFIX:
+                if(unlikely(strncmp(str, m->match, m->len) == 0)) {
+                    if(!m->child) return 1;
+                    return match_pattern(m->child, &str[m->len], len - m->len);
+                }
+                break;
+
+            case NETDATA_SIMPLE_PATTERN_MODE_SUFFIX:
+                if(unlikely(strcmp(&str[len - m->len], m->match) == 0)) {
+                    if(!m->child) return 1;
+                    return 0;
+                }
+                break;
+
+            case NETDATA_SIMPLE_PATTERN_MODE_EXACT:
+            default:
+                if(unlikely(strcmp(str, m->match) == 0)) {
+                    if(!m->child) return 1;
+                    return 0;
+                }
+                break;
+        }
+    }
+
+    return 0;
+}
+
+int netdata_simple_pattern_list_matches(NETDATA_SIMPLE_PATTERN *list, const char *str) {
+    struct simple_pattern *m, *root = (struct simple_pattern *)list;
+
+    if(unlikely(!root)) return 0;
+
+    size_t len = strlen(str);
+    for(m = root; m ; m = m->next)
+        if(match_pattern(m, str, len)) {
+            info("MATCHED string '%s' (len %zu) with pattern '%s%s%s' (len %zu type %u)", str, len,
+                    (m->mode == NETDATA_SIMPLE_PATTERN_MODE_SUFFIX || m->mode == NETDATA_SIMPLE_PATTERN_MODE_SUBSTRING)?"*":"",
+                    m->match,
+                    (m->mode == NETDATA_SIMPLE_PATTERN_MODE_PREFIX || m->mode == NETDATA_SIMPLE_PATTERN_MODE_SUBSTRING)?"*":"",
+                    m->len, m->mode);
+
+            struct simple_pattern *p;
+            for(p = m; p ; p = p->child)
+                info(">>>> MATCHED COMPONENT: '%s%s%s' (len %zu type %u)",
+                        (p->mode == NETDATA_SIMPLE_PATTERN_MODE_SUFFIX || p->mode == NETDATA_SIMPLE_PATTERN_MODE_SUBSTRING)?"*":"",
+                        p->match,
+                        (p->mode == NETDATA_SIMPLE_PATTERN_MODE_PREFIX || p->mode == NETDATA_SIMPLE_PATTERN_MODE_SUBSTRING)?"*":"",
+                        p->len,
+                        p->mode);
+
+            return 1;
+        }
+
+    return 0;
+}
diff --git a/src/simple_pattern.h b/src/simple_pattern.h
new file mode 100644 (file)
index 0000000..f3184f0
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef NETDATA_SIMPLE_PATTERN_H
+#define NETDATA_SIMPLE_PATTERN_H
+
+typedef enum {
+    NETDATA_SIMPLE_PATTERN_MODE_EXACT,
+    NETDATA_SIMPLE_PATTERN_MODE_PREFIX,
+    NETDATA_SIMPLE_PATTERN_MODE_SUFFIX,
+    NETDATA_SIMPLE_PATTERN_MODE_SUBSTRING
+} NETDATA_SIMPLE_PREFIX_MODE;
+
+typedef void NETDATA_SIMPLE_PATTERN;
+extern NETDATA_SIMPLE_PATTERN *netdata_simple_pattern_list_create(const char *list, NETDATA_SIMPLE_PREFIX_MODE default_mode);
+extern int netdata_simple_pattern_list_matches(NETDATA_SIMPLE_PATTERN *list, const char *str);
+
+#endif //NETDATA_SIMPLE_PATTERN_H
index 9927452dc6d168adac13ca819ecf1410384fc7e7..f29de5742495f66cfa4a8e14a20bff63f185b5c2 100644 (file)
@@ -8,8 +8,10 @@ static int cgroup_enable_cpuacct_usage = CONFIG_ONDEMAND_ONDEMAND;
 static int cgroup_enable_memory = CONFIG_ONDEMAND_ONDEMAND;
 static int cgroup_enable_devices = CONFIG_ONDEMAND_ONDEMAND;
 static int cgroup_enable_blkio = CONFIG_ONDEMAND_ONDEMAND;
+static int cgroup_enable_systemd_services = CONFIG_ONDEMAND_NO;
 static int cgroup_enable_new_cgroups_detected_at_runtime = 1;
 static int cgroup_check_for_new_every = 10;
+static int cgroup_update_every = 1;
 static char *cgroup_cpuacct_base = NULL;
 static char *cgroup_blkio_base = NULL;
 static char *cgroup_memory_base = NULL;
@@ -27,13 +29,21 @@ static NETDATA_SIMPLE_PATTERN *systemd_services_cgroups = NULL;
 static char *cgroups_rename_script = PLUGINS_DIR "/cgroup-name.sh";
 
 void read_cgroup_plugin_configuration() {
-    cgroup_check_for_new_every = (int)config_get_number("plugin:cgroups", "check for new cgroups every", cgroup_check_for_new_every);
+    cgroup_update_every = (int)config_get_number("plugin:cgroups", "update every", rrd_update_every);
+    if(cgroup_update_every < rrd_update_every)
+        cgroup_update_every = rrd_update_every;
+
+    cgroup_check_for_new_every = (int)config_get_number("plugin:cgroups", "check for new cgroups every", cgroup_check_for_new_every * cgroup_update_every);
+    if(cgroup_check_for_new_every < cgroup_update_every)
+        cgroup_check_for_new_every = cgroup_update_every;
 
     cgroup_enable_cpuacct_stat = config_get_boolean_ondemand("plugin:cgroups", "enable cpuacct stat", cgroup_enable_cpuacct_stat);
     cgroup_enable_cpuacct_usage = config_get_boolean_ondemand("plugin:cgroups", "enable cpuacct usage", cgroup_enable_cpuacct_usage);
     cgroup_enable_memory = config_get_boolean_ondemand("plugin:cgroups", "enable memory", cgroup_enable_memory);
     cgroup_enable_blkio = config_get_boolean_ondemand("plugin:cgroups", "enable blkio", cgroup_enable_blkio);
 
+    cgroup_enable_systemd_services = config_get_boolean_ondemand("plugin:cgroups", "enable systemd services", cgroup_enable_systemd_services);
+
     char filename[FILENAME_MAX + 1], *s;
     struct mountinfo *mi, *root = mountinfo_read(0);
 
@@ -94,6 +104,7 @@ void read_cgroup_plugin_configuration() {
                     " *.user "
                     " *.mount "
                     " *.partition "
+                    " *.service "
                     " */ns "                               //   /lxc/*/ns    #1397
             ), NETDATA_SIMPLE_PATTERN_MODE_EXACT);
 
@@ -121,10 +132,11 @@ void read_cgroup_plugin_configuration() {
                     " *.user "
             ), NETDATA_SIMPLE_PATTERN_MODE_EXACT);
 
-    systemd_services_cgroups = netdata_simple_pattern_list_create(
-            config_get("plugin:cgroups", "cgroups to match as systemd services",
-                    " *.service "
-            ), NETDATA_SIMPLE_PATTERN_MODE_EXACT);
+    if(cgroup_enable_systemd_services)
+        systemd_services_cgroups = netdata_simple_pattern_list_create(
+                config_get("plugin:cgroups", "cgroups to match as systemd services",
+                        " /system.slice/*.service "
+                ), NETDATA_SIMPLE_PATTERN_MODE_EXACT);
 
     mountinfo_free(root);
 }
@@ -749,37 +761,40 @@ void cgroup_get_chart_name(struct cgroup *cg) {
         debug(D_CGROUP, "cgroup '%s' will not be renamed - it matches the list of disabled cgroup renames (will be shown as '%s')", cg->id, cg->chart_id);
 
 
-    if(netdata_simple_pattern_list_matches(systemd_services_cgroups, cg->id) ||
-            netdata_simple_pattern_list_matches(systemd_services_cgroups, cg->chart_id)) {
-        debug(D_CGROUP, "cgroup '%s' with chart id '%s' (title: '%s') matches systemd services cgroups", cg->id, cg->chart_id, cg->chart_title);
+    if(cgroup_enable_systemd_services) {
+        if(netdata_simple_pattern_list_matches(systemd_services_cgroups, cg->id) ||
+           netdata_simple_pattern_list_matches(systemd_services_cgroups, cg->chart_id)) {
+            debug(D_CGROUP, "cgroup '%s' with chart id '%s' (title: '%s') matches systemd services cgroups", cg->id, cg->chart_id, cg->chart_title);
 
-        char buffer[CGROUP_CHARTID_LINE_MAX + 1];
-        cg->options |= CGROUP_OPTIONS_SYSTEM_SLICE_SERVICE;
+            char buffer[CGROUP_CHARTID_LINE_MAX + 1];
+            cg->options |= CGROUP_OPTIONS_SYSTEM_SLICE_SERVICE;
 
-        strncpy(buffer, cg->id, CGROUP_CHARTID_LINE_MAX);
-        char *s = buffer;
+            strncpy(buffer, cg->id, CGROUP_CHARTID_LINE_MAX);
+            char *s = buffer;
 
-        // skip to the last slash
-        size_t len = strlen(s);
-        while(len--) if(unlikely(s[len] == '/')) break;
-        if(len) s = &s[len+1];
+            // skip to the last slash
+            size_t len = strlen(s);
+            while(len--) if(unlikely(s[len] == '/')) break;
+            if(len) s = &s[len + 1];
 
-        // remove extension
-        //len = strlen(s);
-        //while(len--) if(unlikely(s[len] == '.')) break;
-        //if(len) s[len] = '\0';
+            // remove extension
+            //len = strlen(s);
+            //while(len--) if(unlikely(s[len] == '.')) break;
+            //if(len) s[len] = '\0';
 
-        freez(cg->chart_title);
-        cg->chart_title = cgroup_title_strdupz(s);
+            freez(cg->chart_title);
+            cg->chart_title = cgroup_title_strdupz(s);
 
-        freez(cg->chart_id);
-        cg->chart_id = cgroup_chart_id_strdupz(s);
-        cg->hash_chart = simple_hash(cg->chart_id);
+            freez(cg->chart_id);
+            cg->chart_id = cgroup_chart_id_strdupz(s);
+            cg->hash_chart = simple_hash(cg->chart_id);
+            cg->enabled = 1;
 
-        debug(D_CGROUP, "cgroup '%s' renamed to '%s' (title: '%s')", cg->id, cg->chart_id, cg->chart_title);
+            debug(D_CGROUP, "cgroup '%s' renamed to '%s' (title: '%s')", cg->id, cg->chart_id, cg->chart_title);
+        }
+        else
+            debug(D_CGROUP, "cgroup '%s' with chart id '%s' (title: '%s') does not match systemd services groups", cg->id, cg->chart_id, cg->chart_title);
     }
-    else
-        debug(D_CGROUP, "cgroup '%s' with chart id '%s' (title: '%s') does not match systemd services groups", cg->id, cg->chart_id, cg->chart_title);
 }
 
 struct cgroup *cgroup_add(const char *id) {
@@ -1509,29 +1524,6 @@ void update_cgroup_charts(int update_every) {
 // ----------------------------------------------------------------------------
 // cgroups main
 
-int do_sys_fs_cgroup(int update_every, usec_t dt) {
-    (void)dt;
-
-    static int cgroup_global_config_read = 0;
-    static time_t last_run = 0;
-    time_t now = now_realtime_sec();
-
-    if(unlikely(!cgroup_global_config_read)) {
-        read_cgroup_plugin_configuration();
-        cgroup_global_config_read = 1;
-    }
-
-    if(unlikely(cgroup_enable_new_cgroups_detected_at_runtime && now - last_run > cgroup_check_for_new_every)) {
-        find_all_cgroups();
-        last_run = now;
-    }
-
-    read_all_cgroups(cgroup_root);
-    update_cgroup_charts(update_every);
-
-    return 0;
-}
-
 void *cgroups_main(void *ptr) {
     struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr;
 
@@ -1546,51 +1538,45 @@ void *cgroups_main(void *ptr) {
     struct rusage thread;
 
     // when ZERO, attempt to do it
-    int vdo_sys_fs_cgroup           = 0;
-    int vdo_cpu_netdata             = !config_get_boolean("plugin:cgroups", "cgroups plugin resources", 1);
+    int vdo_cpu_netdata = config_get_boolean("plugin:cgroups", "cgroups plugin resources", 1);
 
-    // keep track of the time each module was called
-    usec_t sutime_sys_fs_cgroup = 0ULL;
-
-    // the next time we will run - aligned properly
-    usec_t sunext = (now_realtime_sec() - (now_realtime_sec() % rrd_update_every) + rrd_update_every) * USEC_PER_SEC;
+    read_cgroup_plugin_configuration();
 
     RRDSET *stcpu_thread = NULL;
 
+    usec_t step = cgroup_update_every * USEC_PER_SEC;
+    usec_t find_every = cgroup_check_for_new_every * USEC_PER_SEC, find_next = 0;
     for(;;) {
-        usec_t sunow;
-        if(unlikely(netdata_exit)) break;
-
-        // delay until it is our time to run
-        while((sunow = now_realtime_usec()) < sunext)
-            sleep_usec(sunext - sunow);
+        usec_t now = now_monotonic_usec();
+        usec_t next = now - (now % step) + step;
 
-        // find the next time we need to run
-        while(now_realtime_usec() > sunext)
-            sunext += rrd_update_every * USEC_PER_SEC;
+        while(now < next) {
+            sleep_usec(next - now);
+            now = now_monotonic_usec();
+        }
 
         if(unlikely(netdata_exit)) break;
 
         // BEGIN -- the job to be done
 
-        if(!vdo_sys_fs_cgroup) {
-            debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_sys_fs_cgroup().");
-            sunow = now_realtime_usec();
-            vdo_sys_fs_cgroup = do_sys_fs_cgroup(rrd_update_every, (sutime_sys_fs_cgroup > 0)?sunow - sutime_sys_fs_cgroup:0ULL);
-            sutime_sys_fs_cgroup = sunow;
+        if(unlikely(now >= find_next)) {
+            find_all_cgroups();
+            find_next = now + find_every;
         }
-        if(unlikely(netdata_exit)) break;
+
+        read_all_cgroups(cgroup_root);
+        update_cgroup_charts(cgroup_update_every);
 
         // END -- the job is done
 
         // --------------------------------------------------------------------
 
-        if(!vdo_cpu_netdata) {
+        if(vdo_cpu_netdata) {
             getrusage(RUSAGE_THREAD, &thread);
 
             if(!stcpu_thread) stcpu_thread = rrdset_find("netdata.plugin_cgroups_cpu");
             if(!stcpu_thread) {
-                stcpu_thread = rrdset_create("netdata", "plugin_cgroups_cpu", NULL, "cgroups", NULL, "NetData CGroups Plugin CPU usage", "milliseconds/s", 132000, rrd_update_every, RRDSET_TYPE_STACKED);
+                stcpu_thread = rrdset_create("netdata", "plugin_cgroups_cpu", NULL, "cgroups", NULL, "NetData CGroups Plugin CPU usage", "milliseconds/s", 132000, cgroup_update_every, RRDSET_TYPE_STACKED);
 
                 rrddim_add(stcpu_thread, "user",  NULL,  1, 1000, RRDDIM_INCREMENTAL);
                 rrddim_add(stcpu_thread, "system", NULL, 1, 1000, RRDDIM_INCREMENTAL);