summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am6
-rw-r--r--src/Makefile.in105
-rw-r--r--src/appconfig.c677
-rw-r--r--src/appconfig.h5
-rw-r--r--src/apps_plugin.c4651
-rw-r--r--src/avl.c669
-rw-r--r--src/avl.h80
-rw-r--r--src/common.c1545
-rw-r--r--src/common.h137
-rw-r--r--src/daemon.c521
-rw-r--r--src/daemon.h10
-rw-r--r--src/dictionary.c335
-rw-r--r--src/dictionary.h47
-rw-r--r--src/eval.c1119
-rw-r--r--src/eval.h72
-rw-r--r--src/global_statistics.c264
-rw-r--r--src/global_statistics.h26
-rw-r--r--src/health.c2194
-rw-r--r--src/health.h283
-rw-r--r--src/log.c481
-rw-r--r--src/log.h56
-rw-r--r--src/main.c1161
-rw-r--r--src/main.h25
-rw-r--r--src/plugin_checks.c129
-rw-r--r--src/plugin_idlejitter.c84
-rw-r--r--src/plugin_nfacct.c314
-rw-r--r--src/plugin_proc.c535
-rw-r--r--src/plugin_tc.c1341
-rw-r--r--src/plugins_d.c1024
-rw-r--r--src/plugins_d.h32
-rw-r--r--src/popen.c320
-rw-r--r--src/popen.h4
-rw-r--r--src/proc_diskstats.c1258
-rw-r--r--src/proc_interrupts.c322
-rw-r--r--src/proc_loadavg.c125
-rw-r--r--src/proc_meminfo.c480
-rw-r--r--src/proc_net_dev.c475
-rw-r--r--src/proc_net_ip_vs_stats.c140
-rw-r--r--src/proc_net_netstat.c362
-rw-r--r--src/proc_net_rpc_nfsd.c1324
-rw-r--r--src/proc_net_snmp.c708
-rw-r--r--src/proc_net_snmp6.c2109
-rw-r--r--src/proc_net_stat_conntrack.c408
-rw-r--r--src/proc_net_stat_synproxy.c193
-rw-r--r--src/proc_self_mountinfo.c347
-rw-r--r--src/proc_self_mountinfo.h36
-rw-r--r--src/proc_softirqs.c318
-rw-r--r--src/proc_stat.c407
-rw-r--r--src/proc_sys_kernel_random_entropy_avail.c50
-rw-r--r--src/proc_vmstat.c893
-rw-r--r--src/procfile.c744
-rw-r--r--src/procfile.h36
-rw-r--r--src/registry.c2576
-rw-r--r--src/registry.h6
-rw-r--r--src/rrd.c2363
-rw-r--r--src/rrd.h358
-rw-r--r--src/rrd2json.c3715
-rw-r--r--src/rrd2json.h41
-rw-r--r--src/storage_number.c290
-rw-r--r--src/storage_number.h25
-rw-r--r--src/sys_fs_cgroup.c2191
-rw-r--r--src/sys_kernel_mm_ksm.c214
-rw-r--r--src/unit_test.c1345
-rw-r--r--src/url.c100
-rw-r--r--src/url.h2
-rw-r--r--src/web_buffer.c360
-rw-r--r--src/web_buffer.h75
-rw-r--r--src/web_buffer_svg.c616
-rw-r--r--src/web_buffer_svg.h6
-rw-r--r--src/web_client.c4456
-rw-r--r--src/web_client.h122
-rw-r--r--src/web_server.c803
-rw-r--r--src/web_server.h38
73 files changed, 27740 insertions, 20949 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index e9fc8f332..8fa6d5bdf 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -21,6 +21,8 @@ AM_CFLAGS = \
sbin_PROGRAMS = netdata
dist_cache_DATA = .keep
+dist_varlib_DATA = .keep
+dist_registry_DATA = .keep
dist_log_DATA = .keep
plugins_PROGRAMS = apps.plugin
@@ -30,7 +32,9 @@ netdata_SOURCES = \
common.c common.h \
daemon.c daemon.h \
dictionary.c dictionary.h \
+ eval.c eval.h \
global_statistics.c global_statistics.h \
+ health.c health.h \
log.c log.h \
main.c main.h \
plugin_checks.c plugin_checks.h \
@@ -67,6 +71,7 @@ netdata_SOURCES = \
unit_test.c unit_test.h \
url.c url.h \
web_buffer.c web_buffer.h \
+ web_buffer_svg.c web_buffer_svg.h \
web_client.c web_client.h \
web_server.c web_server.h \
$(NULL)
@@ -83,6 +88,7 @@ apps_plugin_SOURCES = \
common.c common.h \
log.c log.h \
procfile.c procfile.h \
+ web_buffer.c web_buffer.h \
$(NULL)
install-data-hook:
diff --git a/src/Makefile.in b/src/Makefile.in
index 11b68946e..1a721a045 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -83,7 +83,8 @@ sbin_PROGRAMS = netdata$(EXEEXT)
plugins_PROGRAMS = apps.plugin$(EXEEXT)
subdir = src
DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
- $(top_srcdir)/depcomp $(dist_cache_DATA) $(dist_log_DATA)
+ $(top_srcdir)/depcomp $(dist_cache_DATA) $(dist_log_DATA) \
+ $(dist_registry_DATA) $(dist_varlib_DATA)
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/m4/ax_pthread.m4 \
$(top_srcdir)/configure.ac
@@ -94,24 +95,27 @@ CONFIG_HEADER = $(top_builddir)/config.h
CONFIG_CLEAN_FILES =
CONFIG_CLEAN_VPATH_FILES =
am__installdirs = "$(DESTDIR)$(pluginsdir)" "$(DESTDIR)$(sbindir)" \
- "$(DESTDIR)$(cachedir)" "$(DESTDIR)$(logdir)"
+ "$(DESTDIR)$(cachedir)" "$(DESTDIR)$(logdir)" \
+ "$(DESTDIR)$(registrydir)" "$(DESTDIR)$(varlibdir)"
PROGRAMS = $(plugins_PROGRAMS) $(sbin_PROGRAMS)
am_apps_plugin_OBJECTS = apps_plugin.$(OBJEXT) avl.$(OBJEXT) \
- common.$(OBJEXT) log.$(OBJEXT) procfile.$(OBJEXT)
+ common.$(OBJEXT) log.$(OBJEXT) procfile.$(OBJEXT) \
+ web_buffer.$(OBJEXT)
apps_plugin_OBJECTS = $(am_apps_plugin_OBJECTS)
apps_plugin_LDADD = $(LDADD)
am_netdata_OBJECTS = appconfig.$(OBJEXT) avl.$(OBJEXT) \
common.$(OBJEXT) daemon.$(OBJEXT) dictionary.$(OBJEXT) \
- global_statistics.$(OBJEXT) log.$(OBJEXT) main.$(OBJEXT) \
- plugin_checks.$(OBJEXT) plugin_idlejitter.$(OBJEXT) \
- plugin_nfacct.$(OBJEXT) plugin_proc.$(OBJEXT) \
- plugin_tc.$(OBJEXT) plugins_d.$(OBJEXT) popen.$(OBJEXT) \
- proc_diskstats.$(OBJEXT) proc_interrupts.$(OBJEXT) \
- proc_softirqs.$(OBJEXT) proc_loadavg.$(OBJEXT) \
- proc_meminfo.$(OBJEXT) proc_net_dev.$(OBJEXT) \
- proc_net_ip_vs_stats.$(OBJEXT) proc_net_netstat.$(OBJEXT) \
- proc_net_rpc_nfsd.$(OBJEXT) proc_net_snmp.$(OBJEXT) \
- proc_net_snmp6.$(OBJEXT) proc_net_stat_conntrack.$(OBJEXT) \
+ eval.$(OBJEXT) global_statistics.$(OBJEXT) health.$(OBJEXT) \
+ log.$(OBJEXT) main.$(OBJEXT) plugin_checks.$(OBJEXT) \
+ plugin_idlejitter.$(OBJEXT) plugin_nfacct.$(OBJEXT) \
+ plugin_proc.$(OBJEXT) plugin_tc.$(OBJEXT) plugins_d.$(OBJEXT) \
+ popen.$(OBJEXT) proc_diskstats.$(OBJEXT) \
+ proc_interrupts.$(OBJEXT) proc_softirqs.$(OBJEXT) \
+ proc_loadavg.$(OBJEXT) proc_meminfo.$(OBJEXT) \
+ proc_net_dev.$(OBJEXT) proc_net_ip_vs_stats.$(OBJEXT) \
+ proc_net_netstat.$(OBJEXT) proc_net_rpc_nfsd.$(OBJEXT) \
+ proc_net_snmp.$(OBJEXT) proc_net_snmp6.$(OBJEXT) \
+ proc_net_stat_conntrack.$(OBJEXT) \
proc_net_stat_synproxy.$(OBJEXT) proc_stat.$(OBJEXT) \
proc_self_mountinfo.$(OBJEXT) \
proc_sys_kernel_random_entropy_avail.$(OBJEXT) \
@@ -119,7 +123,8 @@ am_netdata_OBJECTS = appconfig.$(OBJEXT) avl.$(OBJEXT) \
sys_fs_cgroup.$(OBJEXT) procfile.$(OBJEXT) registry.$(OBJEXT) \
rrd.$(OBJEXT) rrd2json.$(OBJEXT) storage_number.$(OBJEXT) \
unit_test.$(OBJEXT) url.$(OBJEXT) web_buffer.$(OBJEXT) \
- web_client.$(OBJEXT) web_server.$(OBJEXT)
+ web_buffer_svg.$(OBJEXT) web_client.$(OBJEXT) \
+ web_server.$(OBJEXT)
netdata_OBJECTS = $(am_netdata_OBJECTS)
am__DEPENDENCIES_1 =
netdata_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
@@ -186,7 +191,8 @@ am__uninstall_files_from_dir = { \
|| { echo " ( cd '$$dir' && rm -f" $$files ")"; \
$(am__cd) "$$dir" && rm -f $$files; }; \
}
-DATA = $(dist_cache_DATA) $(dist_log_DATA)
+DATA = $(dist_cache_DATA) $(dist_log_DATA) $(dist_registry_DATA) \
+ $(dist_varlib_DATA)
am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
# Read a list of newline-separated strings from the standard input,
# and print each of them once, without duplicates. Input order is
@@ -328,6 +334,8 @@ pluginsdir = @pluginsdir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
+pythondir = @pythondir@
+registrydir = @registrydir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
srcdir = @srcdir@
@@ -361,6 +369,8 @@ AM_CFLAGS = \
$(NULL)
dist_cache_DATA = .keep
+dist_varlib_DATA = .keep
+dist_registry_DATA = .keep
dist_log_DATA = .keep
netdata_SOURCES = \
appconfig.c appconfig.h \
@@ -368,7 +378,9 @@ netdata_SOURCES = \
common.c common.h \
daemon.c daemon.h \
dictionary.c dictionary.h \
+ eval.c eval.h \
global_statistics.c global_statistics.h \
+ health.c health.h \
log.c log.h \
main.c main.h \
plugin_checks.c plugin_checks.h \
@@ -405,6 +417,7 @@ netdata_SOURCES = \
unit_test.c unit_test.h \
url.c url.h \
web_buffer.c web_buffer.h \
+ web_buffer_svg.c web_buffer_svg.h \
web_client.c web_client.h \
web_server.c web_server.h \
$(NULL)
@@ -422,6 +435,7 @@ apps_plugin_SOURCES = \
common.c common.h \
log.c log.h \
procfile.c procfile.h \
+ web_buffer.c web_buffer.h \
$(NULL)
all: all-am
@@ -563,7 +577,9 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/common.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/daemon.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dictionary.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eval.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/global_statistics.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/health.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/log.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/plugin_checks.Po@am__quote@
@@ -600,6 +616,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unit_test.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/url.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/web_buffer.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/web_buffer_svg.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/web_client.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/web_server.Po@am__quote@
@@ -658,6 +675,48 @@ uninstall-dist_logDATA:
@list='$(dist_log_DATA)'; test -n "$(logdir)" || list=; \
files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
dir='$(DESTDIR)$(logdir)'; $(am__uninstall_files_from_dir)
+install-dist_registryDATA: $(dist_registry_DATA)
+ @$(NORMAL_INSTALL)
+ @list='$(dist_registry_DATA)'; test -n "$(registrydir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(registrydir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(registrydir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(registrydir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(registrydir)" || exit $$?; \
+ done
+
+uninstall-dist_registryDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(dist_registry_DATA)'; test -n "$(registrydir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(registrydir)'; $(am__uninstall_files_from_dir)
+install-dist_varlibDATA: $(dist_varlib_DATA)
+ @$(NORMAL_INSTALL)
+ @list='$(dist_varlib_DATA)'; test -n "$(varlibdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(varlibdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(varlibdir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(varlibdir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(varlibdir)" || exit $$?; \
+ done
+
+uninstall-dist_varlibDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(dist_varlib_DATA)'; test -n "$(varlibdir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(varlibdir)'; $(am__uninstall_files_from_dir)
ID: $(am__tagged_files)
$(am__define_uniq_tagged_files); mkid -fID $$unique
@@ -745,7 +804,7 @@ check-am: all-am
check: check-am
all-am: Makefile $(PROGRAMS) $(DATA)
installdirs:
- for dir in "$(DESTDIR)$(pluginsdir)" "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(cachedir)" "$(DESTDIR)$(logdir)"; do \
+ for dir in "$(DESTDIR)$(pluginsdir)" "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(cachedir)" "$(DESTDIR)$(logdir)" "$(DESTDIR)$(registrydir)" "$(DESTDIR)$(varlibdir)"; do \
test -z "$$dir" || $(MKDIR_P) "$$dir"; \
done
install: install-am
@@ -803,6 +862,7 @@ info: info-am
info-am:
install-data-am: install-dist_cacheDATA install-dist_logDATA \
+ install-dist_registryDATA install-dist_varlibDATA \
install-pluginsPROGRAMS
@$(NORMAL_INSTALL)
$(MAKE) $(AM_MAKEFLAGS) install-data-hook
@@ -850,6 +910,7 @@ ps: ps-am
ps-am:
uninstall-am: uninstall-dist_cacheDATA uninstall-dist_logDATA \
+ uninstall-dist_registryDATA uninstall-dist_varlibDATA \
uninstall-pluginsPROGRAMS uninstall-sbinPROGRAMS
.MAKE: install-am install-data-am install-strip
@@ -860,14 +921,16 @@ uninstall-am: uninstall-dist_cacheDATA uninstall-dist_logDATA \
distclean-tags distdir dvi dvi-am html html-am info info-am \
install install-am install-data install-data-am \
install-data-hook install-dist_cacheDATA install-dist_logDATA \
- install-dvi install-dvi-am install-exec install-exec-am \
- install-html install-html-am install-info install-info-am \
- install-man install-pdf install-pdf-am install-pluginsPROGRAMS \
- install-ps install-ps-am install-sbinPROGRAMS install-strip \
- installcheck installcheck-am installdirs maintainer-clean \
+ install-dist_registryDATA install-dist_varlibDATA install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-pdf install-pdf-am install-pluginsPROGRAMS install-ps \
+ install-ps-am install-sbinPROGRAMS install-strip installcheck \
+ installcheck-am installdirs maintainer-clean \
maintainer-clean-generic mostlyclean mostlyclean-compile \
mostlyclean-generic pdf pdf-am ps ps-am tags tags-am uninstall \
uninstall-am uninstall-dist_cacheDATA uninstall-dist_logDATA \
+ uninstall-dist_registryDATA uninstall-dist_varlibDATA \
uninstall-pluginsPROGRAMS uninstall-sbinPROGRAMS
diff --git a/src/appconfig.c b/src/appconfig.c
index 0ec4cad32..34fb6d7d3 100644
--- a/src/appconfig.c
+++ b/src/appconfig.c
@@ -1,22 +1,4 @@
-
-/*
- * TODO
- *
- * 1. Re-write this using DICTIONARY
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <pthread.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "avl.h"
#include "common.h"
-#include "appconfig.h"
-#include "log.h"
#define CONFIG_FILE_LINE_MAX ((CONFIG_MAX_NAME + CONFIG_MAX_VALUE + 1024) * 2)
@@ -31,33 +13,33 @@ pthread_mutex_t config_mutex = PTHREAD_MUTEX_INITIALIZER;
#define CONFIG_VALUE_CHECKED 0x08 // has been checked if the value is different from the default
struct config_value {
- avl avl; // the index - this has to be first!
+ avl avl; // the index - this has to be first!
- uint8_t flags;
- uint32_t hash; // a simple hash to speed up searching
- // we first compare hashes, and only if the hashes are equal we do string comparisons
+ uint8_t flags;
+ uint32_t hash; // a simple hash to speed up searching
+ // we first compare hashes, and only if the hashes are equal we do string comparisons
- char *name;
- char *value;
+ char *name;
+ char *value;
- struct config_value *next; // config->mutex protects just this
+ struct config_value *next; // config->mutex protects just this
};
struct config {
- avl avl;
+ avl avl;
- uint32_t hash; // a simple hash to speed up searching
- // we first compare hashes, and only if the hashes are equal we do string comparisons
+ uint32_t hash; // a simple hash to speed up searching
+ // we first compare hashes, and only if the hashes are equal we do string comparisons
- char *name;
+ char *name;
- struct config *next; // gloabl config_mutex protects just this
+ struct config *next; // gloabl config_mutex protects just this
- struct config_value *values;
- avl_tree_lock values_index;
+ struct config_value *values;
+ avl_tree_lock values_index;
- pthread_mutex_t mutex; // this locks only the writers, to ensure atomic updates
- // readers are protected using the rwlock in avl_tree_lock
+ pthread_mutex_t mutex; // this locks only the writers, to ensure atomic updates
+ // readers are protected using the rwlock in avl_tree_lock
} *config_root = NULL;
@@ -65,19 +47,19 @@ struct config {
// locking
static inline void config_global_write_lock(void) {
- pthread_mutex_lock(&config_mutex);
+ pthread_mutex_lock(&config_mutex);
}
static inline void config_global_unlock(void) {
- pthread_mutex_unlock(&config_mutex);
+ pthread_mutex_unlock(&config_mutex);
}
static inline void config_section_write_lock(struct config *co) {
- pthread_mutex_lock(&co->mutex);
+ pthread_mutex_lock(&co->mutex);
}
static inline void config_section_unlock(struct config *co) {
- pthread_mutex_unlock(&co->mutex);
+ pthread_mutex_unlock(&co->mutex);
}
@@ -85,20 +67,20 @@ static inline void config_section_unlock(struct config *co) {
// config name-value index
static int config_value_compare(void* a, void* b) {
- if(((struct config_value *)a)->hash < ((struct config_value *)b)->hash) return -1;
- else if(((struct config_value *)a)->hash > ((struct config_value *)b)->hash) return 1;
- else return strcmp(((struct config_value *)a)->name, ((struct config_value *)b)->name);
+ if(((struct config_value *)a)->hash < ((struct config_value *)b)->hash) return -1;
+ else if(((struct config_value *)a)->hash > ((struct config_value *)b)->hash) return 1;
+ else return strcmp(((struct config_value *)a)->name, ((struct config_value *)b)->name);
}
#define config_value_index_add(co, cv) avl_insert_lock(&((co)->values_index), (avl *)(cv))
#define config_value_index_del(co, cv) avl_remove_lock(&((co)->values_index), (avl *)(cv))
static struct config_value *config_value_index_find(struct config *co, const char *name, uint32_t hash) {
- struct config_value tmp;
- tmp.hash = (hash)?hash:simple_hash(name);
- tmp.name = (char *)name;
+ struct config_value tmp;
+ tmp.hash = (hash)?hash:simple_hash(name);
+ tmp.name = (char *)name;
- return (struct config_value *)avl_search_lock(&(co->values_index), (avl *) &tmp);
+ return (struct config_value *)avl_search_lock(&(co->values_index), (avl *) &tmp);
}
@@ -106,25 +88,25 @@ static struct config_value *config_value_index_find(struct config *co, const cha
// config sections index
static int config_compare(void* a, void* b) {
- if(((struct config *)a)->hash < ((struct config *)b)->hash) return -1;
- else if(((struct config *)a)->hash > ((struct config *)b)->hash) return 1;
- else return strcmp(((struct config *)a)->name, ((struct config *)b)->name);
+ if(((struct config *)a)->hash < ((struct config *)b)->hash) return -1;
+ else if(((struct config *)a)->hash > ((struct config *)b)->hash) return 1;
+ else return strcmp(((struct config *)a)->name, ((struct config *)b)->name);
}
avl_tree_lock config_root_index = {
- { NULL, config_compare },
- AVL_LOCK_INITIALIZER
+ { NULL, config_compare },
+ AVL_LOCK_INITIALIZER
};
#define config_index_add(cfg) avl_insert_lock(&config_root_index, (avl *)(cfg))
#define config_index_del(cfg) avl_remove_lock(&config_root_index, (avl *)(cfg))
static struct config *config_index_find(const char *name, uint32_t hash) {
- struct config tmp;
- tmp.hash = (hash)?hash:simple_hash(name);
- tmp.name = (char *)name;
+ struct config tmp;
+ tmp.hash = (hash)?hash:simple_hash(name);
+ tmp.name = (char *)name;
- return (struct config *)avl_search_lock(&config_root_index, (avl *) &tmp);
+ return (struct config *)avl_search_lock(&config_root_index, (avl *) &tmp);
}
@@ -132,34 +114,31 @@ static struct config *config_index_find(const char *name, uint32_t hash) {
// config section methods
static inline struct config *config_section_find(const char *section) {
- return config_index_find(section, 0);
+ return config_index_find(section, 0);
}
static inline struct config *config_section_create(const char *section)
{
- debug(D_CONFIG, "Creating section '%s'.", section);
-
- struct config *co = calloc(1, sizeof(struct config));
- if(!co) fatal("Cannot allocate config");
+ debug(D_CONFIG, "Creating section '%s'.", section);
- co->name = strdup(section);
- if(!co->name) fatal("Cannot allocate config.name");
- co->hash = simple_hash(co->name);
+ struct config *co = callocz(1, sizeof(struct config));
+ co->name = strdupz(section);
+ co->hash = simple_hash(co->name);
- avl_init_lock(&co->values_index, config_value_compare);
+ avl_init_lock(&co->values_index, config_value_compare);
- config_index_add(co);
+ config_index_add(co);
- config_global_write_lock();
- struct config *co2 = config_root;
- if(co2) {
- while (co2->next) co2 = co2->next;
- co2->next = co;
- }
- else config_root = co;
- config_global_unlock();
+ config_global_write_lock();
+ struct config *co2 = config_root;
+ if(co2) {
+ while (co2->next) co2 = co2->next;
+ co2->next = co;
+ }
+ else config_root = co;
+ config_global_unlock();
- return co;
+ return co;
}
@@ -168,181 +147,221 @@ static inline struct config *config_section_create(const char *section)
static inline struct config_value *config_value_create(struct config *co, const char *name, const char *value)
{
- debug(D_CONFIG, "Creating config entry for name '%s', value '%s', in section '%s'.", name, value, co->name);
+ debug(D_CONFIG, "Creating config entry for name '%s', value '%s', in section '%s'.", name, value, co->name);
+
+ struct config_value *cv = callocz(1, sizeof(struct config_value));
+ cv->name = strdupz(name);
+ cv->hash = simple_hash(cv->name);
+ cv->value = strdupz(value);
+
+ config_value_index_add(co, cv);
+
+ config_section_write_lock(co);
+ struct config_value *cv2 = co->values;
+ if(cv2) {
+ while (cv2->next) cv2 = cv2->next;
+ cv2->next = cv;
+ }
+ else co->values = cv;
+ config_section_unlock(co);
+
+ return cv;
+}
+
+int config_exists(const char *section, const char *name) {
+ struct config_value *cv;
+
+ debug(D_CONFIG, "request to get config in section '%s', name '%s'", section, name);
+
+ struct config *co = config_section_find(section);
+ if(!co) return 0;
+
+ cv = config_value_index_find(co, name, 0);
+ if(!cv) return 0;
+
+ return 1;
+}
+
+int config_rename(const char *section, const char *old, const char *new) {
+ struct config_value *cv, *cv2;
+
+ debug(D_CONFIG, "request to rename config in section '%s', old name '%s', new name '%s'", section, old, new);
+
+ struct config *co = config_section_find(section);
+ if(!co) return -1;
+
+ config_section_write_lock(co);
+
+ cv = config_value_index_find(co, old, 0);
+ if(!cv) goto cleanup;
+
+ cv2 = config_value_index_find(co, new, 0);
+ if(cv2) goto cleanup;
- struct config_value *cv = calloc(1, sizeof(struct config_value));
- if(!cv) fatal("Cannot allocate config_value");
+ config_value_index_del(co, cv);
- cv->name = strdup(name);
- if(!cv->name) fatal("Cannot allocate config.name");
- cv->hash = simple_hash(cv->name);
+ freez(cv->name);
+ cv->name = strdupz(new);
- cv->value = strdup(value);
- if(!cv->value) fatal("Cannot allocate config.value");
+ cv->hash = simple_hash(cv->name);
- config_value_index_add(co, cv);
+ config_value_index_add(co, cv);
+ config_section_unlock(co);
- config_section_write_lock(co);
- struct config_value *cv2 = co->values;
- if(cv2) {
- while (cv2->next) cv2 = cv2->next;
- cv2->next = cv;
- }
- else co->values = cv;
- config_section_unlock(co);
+ return 0;
- return cv;
+cleanup:
+ config_section_unlock(co);
+ return -1;
}
char *config_get(const char *section, const char *name, const char *default_value)
{
- struct config_value *cv;
-
- debug(D_CONFIG, "request to get config in section '%s', name '%s', default_value '%s'", section, name, default_value);
-
- struct config *co = config_section_find(section);
- if(!co) co = config_section_create(section);
-
- cv = config_value_index_find(co, name, 0);
- if(!cv) {
- cv = config_value_create(co, name, default_value);
- if(!cv) return NULL;
- }
- cv->flags |= CONFIG_VALUE_USED;
-
- if((cv->flags & CONFIG_VALUE_LOADED) || (cv->flags & CONFIG_VALUE_CHANGED)) {
- // this is a loaded value from the config file
- // if it is different that the default, mark it
- if(!(cv->flags & CONFIG_VALUE_CHECKED)) {
- if(strcmp(cv->value, default_value) != 0) cv->flags |= CONFIG_VALUE_CHANGED;
- cv->flags |= CONFIG_VALUE_CHECKED;
- }
- }
-
- return(cv->value);
+ struct config_value *cv;
+
+ debug(D_CONFIG, "request to get config in section '%s', name '%s', default_value '%s'", section, name, default_value);
+
+ struct config *co = config_section_find(section);
+ if(!co) co = config_section_create(section);
+
+ cv = config_value_index_find(co, name, 0);
+ if(!cv) {
+ cv = config_value_create(co, name, default_value);
+ if(!cv) return NULL;
+ }
+ cv->flags |= CONFIG_VALUE_USED;
+
+ if((cv->flags & CONFIG_VALUE_LOADED) || (cv->flags & CONFIG_VALUE_CHANGED)) {
+ // this is a loaded value from the config file
+ // if it is different that the default, mark it
+ if(!(cv->flags & CONFIG_VALUE_CHECKED)) {
+ if(strcmp(cv->value, default_value) != 0) cv->flags |= CONFIG_VALUE_CHANGED;
+ cv->flags |= CONFIG_VALUE_CHECKED;
+ }
+ }
+
+ return(cv->value);
}
long long config_get_number(const char *section, const char *name, long long value)
{
- char buffer[100], *s;
- sprintf(buffer, "%lld", value);
+ char buffer[100], *s;
+ sprintf(buffer, "%lld", value);
- s = config_get(section, name, buffer);
- if(!s) return value;
+ s = config_get(section, name, buffer);
+ if(!s) return value;
- return strtoll(s, NULL, 0);
+ return strtoll(s, NULL, 0);
}
int config_get_boolean(const char *section, const char *name, int value)
{
- char *s;
- if(value) s = "yes";
- else s = "no";
+ char *s;
+ if(value) s = "yes";
+ else s = "no";
- s = config_get(section, name, s);
- if(!s) return value;
+ s = config_get(section, name, s);
+ if(!s) return value;
- if(!strcmp(s, "yes")) return 1;
- else return 0;
+ if(!strcmp(s, "yes") || !strcmp(s, "auto") || !strcmp(s, "on demand")) return 1;
+ return 0;
}
int config_get_boolean_ondemand(const char *section, const char *name, int value)
{
- char *s;
+ char *s;
- if(value == CONFIG_ONDEMAND_ONDEMAND)
- s = "on demand";
+ if(value == CONFIG_ONDEMAND_ONDEMAND)
+ s = "auto";
- else if(value == CONFIG_ONDEMAND_NO)
- s = "no";
+ else if(value == CONFIG_ONDEMAND_NO)
+ s = "no";
- else
- s = "yes";
+ else
+ s = "yes";
- s = config_get(section, name, s);
- if(!s) return value;
+ s = config_get(section, name, s);
+ if(!s) return value;
- if(!strcmp(s, "yes"))
- return CONFIG_ONDEMAND_YES;
- else if(!strcmp(s, "no"))
- return CONFIG_ONDEMAND_NO;
- else if(!strcmp(s, "on demand"))
- return CONFIG_ONDEMAND_ONDEMAND;
+ if(!strcmp(s, "yes"))
+ return CONFIG_ONDEMAND_YES;
+ else if(!strcmp(s, "no"))
+ return CONFIG_ONDEMAND_NO;
+ else if(!strcmp(s, "auto") || !strcmp(s, "on demand"))
+ return CONFIG_ONDEMAND_ONDEMAND;
- return value;
+ return value;
}
const char *config_set_default(const char *section, const char *name, const char *value)
{
- struct config_value *cv;
+ struct config_value *cv;
- debug(D_CONFIG, "request to set config in section '%s', name '%s', value '%s'", section, name, value);
+ debug(D_CONFIG, "request to set config in section '%s', name '%s', value '%s'", section, name, value);
- struct config *co = config_section_find(section);
- if(!co) return config_set(section, name, value);
+ struct config *co = config_section_find(section);
+ if(!co) return config_set(section, name, value);
- cv = config_value_index_find(co, name, 0);
- if(!cv) return config_set(section, name, value);
+ cv = config_value_index_find(co, name, 0);
+ if(!cv) return config_set(section, name, value);
- cv->flags |= CONFIG_VALUE_USED;
+ cv->flags |= CONFIG_VALUE_USED;
- if(cv->flags & CONFIG_VALUE_LOADED)
- return cv->value;
+ if(cv->flags & CONFIG_VALUE_LOADED)
+ return cv->value;
- if(strcmp(cv->value, value) != 0) {
- cv->flags |= CONFIG_VALUE_CHANGED;
+ if(strcmp(cv->value, value) != 0) {
+ cv->flags |= CONFIG_VALUE_CHANGED;
- free(cv->value);
- cv->value = strdup(value);
- if(!cv->value) fatal("Cannot allocate config.value");
- }
+ freez(cv->value);
+ cv->value = strdupz(value);
+ }
- return cv->value;
+ return cv->value;
}
const char *config_set(const char *section, const char *name, const char *value)
{
- struct config_value *cv;
+ struct config_value *cv;
- debug(D_CONFIG, "request to set config in section '%s', name '%s', value '%s'", section, name, value);
+ debug(D_CONFIG, "request to set config in section '%s', name '%s', value '%s'", section, name, value);
- struct config *co = config_section_find(section);
- if(!co) co = config_section_create(section);
+ struct config *co = config_section_find(section);
+ if(!co) co = config_section_create(section);
- cv = config_value_index_find(co, name, 0);
- if(!cv) cv = config_value_create(co, name, value);
- cv->flags |= CONFIG_VALUE_USED;
+ cv = config_value_index_find(co, name, 0);
+ if(!cv) cv = config_value_create(co, name, value);
+ cv->flags |= CONFIG_VALUE_USED;
- if(strcmp(cv->value, value) != 0) {
- cv->flags |= CONFIG_VALUE_CHANGED;
+ if(strcmp(cv->value, value) != 0) {
+ cv->flags |= CONFIG_VALUE_CHANGED;
- free(cv->value);
- cv->value = strdup(value);
- if(!cv->value) fatal("Cannot allocate config.value");
- }
+ freez(cv->value);
+ cv->value = strdupz(value);
+ }
- return value;
+ return value;
}
long long config_set_number(const char *section, const char *name, long long value)
{
- char buffer[100];
- sprintf(buffer, "%lld", value);
+ char buffer[100];
+ sprintf(buffer, "%lld", value);
- config_set(section, name, buffer);
+ config_set(section, name, buffer);
- return value;
+ return value;
}
int config_set_boolean(const char *section, const char *name, int value)
{
- char *s;
- if(value) s = "yes";
- else s = "no";
+ char *s;
+ if(value) s = "yes";
+ else s = "no";
- config_set(section, name, s);
+ config_set(section, name, s);
- return value;
+ return value;
}
@@ -351,152 +370,158 @@ int config_set_boolean(const char *section, const char *name, int value)
int load_config(char *filename, int overwrite_used)
{
- int line = 0;
- struct config *co = NULL;
-
- char buffer[CONFIG_FILE_LINE_MAX + 1], *s;
-
- if(!filename) filename = CONFIG_DIR "/" CONFIG_FILENAME;
- FILE *fp = fopen(filename, "r");
- if(!fp) {
- error("Cannot open file '%s'", filename);
- return 0;
- }
-
- while(fgets(buffer, CONFIG_FILE_LINE_MAX, fp) != NULL) {
- buffer[CONFIG_FILE_LINE_MAX] = '\0';
- line++;
-
- s = trim(buffer);
- if(!s) {
- debug(D_CONFIG, "Ignoring line %d, it is empty.", line);
- continue;
- }
-
- int len = (int) strlen(s);
- if(*s == '[' && s[len - 1] == ']') {
- // new section
- s[len - 1] = '\0';
- s++;
-
- co = config_section_find(s);
- if(!co) co = config_section_create(s);
-
- continue;
- }
-
- if(!co) {
- // line outside a section
- error("Ignoring line %d ('%s'), it is outside all sections.", line, s);
- continue;
- }
-
- char *name = s;
- char *value = strchr(s, '=');
- if(!value) {
- error("Ignoring line %d ('%s'), there is no = in it.", line, s);
- continue;
- }
- *value = '\0';
- value++;
-
- name = trim(name);
- value = trim(value);
-
- if(!name) {
- error("Ignoring line %d, name is empty.", line);
- continue;
- }
- if(!value) {
- debug(D_CONFIG, "Ignoring line %d, value is empty.", line);
- continue;
- }
-
- struct config_value *cv = config_value_index_find(co, name, 0);
-
- if(!cv) cv = config_value_create(co, name, value);
- else {
- if(((cv->flags & CONFIG_VALUE_USED) && overwrite_used) || !(cv->flags & CONFIG_VALUE_USED)) {
- debug(D_CONFIG, "Overwriting '%s/%s'.", line, co->name, cv->name);
- free(cv->value);
- cv->value = strdup(value);
- if(!cv->value) fatal("Cannot allocate config.value");
- }
- else
- debug(D_CONFIG, "Ignoring line %d, '%s/%s' is already present and used.", line, co->name, cv->name);
- }
- cv->flags |= CONFIG_VALUE_LOADED;
- }
-
- fclose(fp);
-
- return 1;
+ int line = 0;
+ struct config *co = NULL;
+
+ char buffer[CONFIG_FILE_LINE_MAX + 1], *s;
+
+ if(!filename) filename = CONFIG_DIR "/" CONFIG_FILENAME;
+
+ debug(D_CONFIG, "Opening config file '%s'", filename);
+
+ FILE *fp = fopen(filename, "r");
+ if(!fp) {
+ error("Cannot open file '%s'", filename);
+ return 0;
+ }
+
+ while(fgets(buffer, CONFIG_FILE_LINE_MAX, fp) != NULL) {
+ buffer[CONFIG_FILE_LINE_MAX] = '\0';
+ line++;
+
+ s = trim(buffer);
+ if(!s) {
+ debug(D_CONFIG, "Ignoring line %d, it is empty.", line);
+ continue;
+ }
+
+ int len = (int) strlen(s);
+ if(*s == '[' && s[len - 1] == ']') {
+ // new section
+ s[len - 1] = '\0';
+ s++;
+
+ co = config_section_find(s);
+ if(!co) co = config_section_create(s);
+
+ continue;
+ }
+
+ if(!co) {
+ // line outside a section
+ error("Ignoring line %d ('%s'), it is outside all sections.", line, s);
+ continue;
+ }
+
+ char *name = s;
+ char *value = strchr(s, '=');
+ if(!value) {
+ error("Ignoring line %d ('%s'), there is no = in it.", line, s);
+ continue;
+ }
+ *value = '\0';
+ value++;
+
+ name = trim(name);
+ value = trim(value);
+
+ if(!name) {
+ error("Ignoring line %d, name is empty.", line);
+ continue;
+ }
+ if(!value) {
+ debug(D_CONFIG, "Ignoring line %d, value is empty.", line);
+ continue;
+ }
+
+ struct config_value *cv = config_value_index_find(co, name, 0);
+
+ if(!cv) cv = config_value_create(co, name, value);
+ else {
+ if(((cv->flags & CONFIG_VALUE_USED) && overwrite_used) || !(cv->flags & CONFIG_VALUE_USED)) {
+ debug(D_CONFIG, "Line %d, overwriting '%s/%s'.", line, co->name, cv->name);
+ freez(cv->value);
+ cv->value = strdupz(value);
+ }
+ else
+ debug(D_CONFIG, "Ignoring line %d, '%s/%s' is already present and used.", line, co->name, cv->name);
+ }
+ cv->flags |= CONFIG_VALUE_LOADED;
+ }
+
+ fclose(fp);
+
+ return 1;
}
void generate_config(BUFFER *wb, int only_changed)
{
- int i, pri;
- struct config *co;
- struct config_value *cv;
-
- for(i = 0; i < 3 ;i++) {
- switch(i) {
- case 0:
- buffer_strcat(wb,
- "# NetData Configuration\n"
- "# You can uncomment and change any of the options below.\n"
- "# The value shown in the commented settings, is the default value.\n"
- "\n# global netdata configuration\n");
- break;
-
- case 1:
- buffer_strcat(wb, "\n\n# per plugin configuration\n");
- break;
-
- case 2:
- buffer_strcat(wb, "\n\n# per chart configuration\n");
- break;
- }
-
- config_global_write_lock();
- for(co = config_root; co ; co = co->next) {
- if(strcmp(co->name, "global") == 0 || strcmp(co->name, "plugins") == 0 || strcmp(co->name, "registry") == 0) pri = 0;
- else if(strncmp(co->name, "plugin:", 7) == 0) pri = 1;
- else pri = 2;
-
- if(i == pri) {
- int used = 0;
- int changed = 0;
- int count = 0;
-
- config_section_write_lock(co);
- for(cv = co->values; cv ; cv = cv->next) {
- used += (cv->flags & CONFIG_VALUE_USED)?1:0;
- changed += (cv->flags & CONFIG_VALUE_CHANGED)?1:0;
- count++;
- }
- config_section_unlock(co);
-
- if(!count) continue;
- if(only_changed && !changed) continue;
-
- if(!used) {
- buffer_sprintf(wb, "\n# node '%s' is not used.", co->name);
- }
-
- buffer_sprintf(wb, "\n[%s]\n", co->name);
-
- config_section_write_lock(co);
- for(cv = co->values; cv ; cv = cv->next) {
-
- if(used && !(cv->flags & CONFIG_VALUE_USED)) {
- buffer_sprintf(wb, "\n\t# option '%s' is not used.\n", cv->name);
- }
- buffer_sprintf(wb, "\t%s%s = %s\n", ((!(cv->flags & CONFIG_VALUE_CHANGED)) && (cv->flags & CONFIG_VALUE_USED))?"# ":"", cv->name, cv->value);
- }
- config_section_unlock(co);
- }
- }
- config_global_unlock();
- }
+ int i, pri;
+ struct config *co;
+ struct config_value *cv;
+
+ for(i = 0; i < 3 ;i++) {
+ switch(i) {
+ case 0:
+ buffer_strcat(wb,
+ "# NetData Configuration\n"
+ "# You can uncomment and change any of the options below.\n"
+ "# The value shown in the commented settings, is the default value.\n"
+ "\n# global netdata configuration\n");
+ break;
+
+ case 1:
+ buffer_strcat(wb, "\n\n# per plugin configuration\n");
+ break;
+
+ case 2:
+ buffer_strcat(wb, "\n\n# per chart configuration\n");
+ break;
+ }
+
+ config_global_write_lock();
+ for(co = config_root; co ; co = co->next) {
+ if(!strcmp(co->name, "global") ||
+ !strcmp(co->name, "plugins") ||
+ !strcmp(co->name, "registry") ||
+ !strcmp(co->name, "health"))
+ pri = 0;
+ else if(!strncmp(co->name, "plugin:", 7)) pri = 1;
+ else pri = 2;
+
+ if(i == pri) {
+ int used = 0;
+ int changed = 0;
+ int count = 0;
+
+ config_section_write_lock(co);
+ for(cv = co->values; cv ; cv = cv->next) {
+ used += (cv->flags & CONFIG_VALUE_USED)?1:0;
+ changed += (cv->flags & CONFIG_VALUE_CHANGED)?1:0;
+ count++;
+ }
+ config_section_unlock(co);
+
+ if(!count) continue;
+ if(only_changed && !changed) continue;
+
+ if(!used) {
+ buffer_sprintf(wb, "\n# node '%s' is not used.", co->name);
+ }
+
+ buffer_sprintf(wb, "\n[%s]\n", co->name);
+
+ config_section_write_lock(co);
+ for(cv = co->values; cv ; cv = cv->next) {
+
+ if(used && !(cv->flags & CONFIG_VALUE_USED)) {
+ buffer_sprintf(wb, "\n\t# option '%s' is not used.\n", cv->name);
+ }
+ buffer_sprintf(wb, "\t%s%s = %s\n", ((!(cv->flags & CONFIG_VALUE_CHANGED)) && (cv->flags & CONFIG_VALUE_USED))?"# ":"", cv->name, cv->value);
+ }
+ config_section_unlock(co);
+ }
+ }
+ config_global_unlock();
+ }
}
diff --git a/src/appconfig.h b/src/appconfig.h
index 41d1e19bb..08aae8348 100644
--- a/src/appconfig.h
+++ b/src/appconfig.h
@@ -1,5 +1,3 @@
-#include "web_buffer.h"
-
#ifndef NETDATA_CONFIG_H
#define NETDATA_CONFIG_H 1
@@ -26,6 +24,9 @@ extern const char *config_set_default(const char *section, const char *name, con
extern long long config_set_number(const char *section, const char *name, long long value);
extern int config_set_boolean(const char *section, const char *name, int value);
+extern int config_exists(const char *section, const char *name);
+extern int config_rename(const char *section, const char *old, const char *new);
+
extern void generate_config(BUFFER *wb, int only_changed);
#endif /* NETDATA_CONFIG_H */
diff --git a/src/apps_plugin.c b/src/apps_plugin.c
index 0bcdfcf50..ee400b72e 100644
--- a/src/apps_plugin.c
+++ b/src/apps_plugin.c
@@ -1,272 +1,96 @@
-// TODO
-//
-// 1. disable RESET_OR_OVERFLOW check in charts
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/time.h>
-#include <sys/wait.h>
-#include <sys/stat.h>
-
-#include <sys/resource.h>
-#include <sys/stat.h>
-
-#include <errno.h>
-#include <stdarg.h>
-#include <locale.h>
-#include <ctype.h>
-#include <fcntl.h>
-
-#include <malloc.h>
-#include <dirent.h>
-#include <arpa/inet.h>
-
-#include <sys/types.h>
-#include <pwd.h>
-#include <grp.h>
-
-#include "avl.h"
-
#include "common.h"
-#include "log.h"
-#include "procfile.h"
-#include "../config.h"
-
-#ifdef NETDATA_INTERNAL_CHECKS
-#include <sys/prctl.h>
-#endif
#define MAX_COMPARE_NAME 100
#define MAX_NAME 100
#define MAX_CMDLINE 1024
-long processors = 1;
-long pid_max = 32768;
+// the rates we are going to send to netdata
+// will have this detail
+// a value of:
+// 1 will send just integer parts to netdata
+// 100 will send 2 decimal points
+// 1000 will send 3 decimal points
+// etc.
+#define RATES_DETAIL 10000ULL
+
+int processors = 1;
+pid_t pid_max = 32768;
int debug = 0;
int update_every = 1;
+unsigned long long global_iterations_counter = 1;
unsigned long long file_counter = 0;
int proc_pid_cmdline_is_needed = 0;
-
+int include_exited_childs = 1;
char *host_prefix = "";
char *config_dir = CONFIG_DIR;
-#ifdef NETDATA_INTERNAL_CHECKS
-// ----------------------------------------------------------------------------
-// memory debugger
-// do not use in production systems - it mis-aligns allocated memory
-
-struct allocations {
- size_t allocations;
- size_t allocated;
- size_t allocated_max;
-} allocations = { 0, 0, 0 };
-
-#define MALLOC_MARK (uint32_t)(0x0BADCAFE)
-#define MALLOC_PREFIX (sizeof(uint32_t) * 2)
-#define MALLOC_SUFFIX (sizeof(uint32_t))
-#define MALLOC_OVERHEAD (MALLOC_PREFIX + MALLOC_SUFFIX)
-
-void *mark_allocation(void *allocated_ptr, size_t size_without_overheads) {
- uint32_t *real_ptr = (uint32_t *)allocated_ptr;
- real_ptr[0] = MALLOC_MARK;
- real_ptr[1] = (uint32_t) size_without_overheads;
-
- uint32_t *end_ptr = (uint32_t *)(allocated_ptr + MALLOC_PREFIX + size_without_overheads);
- end_ptr[0] = MALLOC_MARK;
-
- // fprintf(stderr, "MEMORY_POINTER: Allocated at %p, returning %p.\n", allocated_ptr, (void *)(allocated_ptr + MALLOC_PREFIX));
-
- return allocated_ptr + MALLOC_PREFIX;
-}
-
-void *check_allocation(const char *file, int line, const char *function, void *marked_ptr, size_t *size_without_overheads_ptr) {
- uint32_t *real_ptr = (uint32_t *)(marked_ptr - MALLOC_PREFIX);
-
- // fprintf(stderr, "MEMORY_POINTER: Checking pointer at %p, real %p for %s/%u@%s.\n", marked_ptr, (void *)(marked_ptr - MALLOC_PREFIX), function, line, file);
-
- if(real_ptr[0] != MALLOC_MARK) fatal("MEMORY: prefix MARK is not valid for %s/%u@%s.", function, line, file);
-
- size_t size = real_ptr[1];
-
- uint32_t *end_ptr = (uint32_t *)(marked_ptr + size);
- if(end_ptr[0] != MALLOC_MARK) fatal("MEMORY: suffix MARK of allocation with size %zu is not valid for %s/%u@%s.", size, function, line, file);
-
- if(size_without_overheads_ptr) *size_without_overheads_ptr = size;
-
- return real_ptr;
-}
-
-void *malloc_debug(const char *file, int line, const char *function, size_t size) {
- void *ptr = malloc(size + MALLOC_OVERHEAD);
- if(!ptr) fatal("MEMORY: Cannot allocate %zu bytes for %s/%u@%s.", size, function, line, file);
-
- allocations.allocated += size;
- allocations.allocations++;
-
- debug(D_MEMORY, "MEMORY: Allocated %zu bytes for %s/%u@%s."
- " Status: allocated %zu in %zu allocs."
- , size
- , function, line, file
- , allocations.allocated
- , allocations.allocations
- );
-
- if(allocations.allocated > allocations.allocated_max) {
- debug(D_MEMORY, "MEMORY: total allocation peak increased from %zu to %zu", allocations.allocated_max, allocations.allocated);
- allocations.allocated_max = allocations.allocated;
- }
-
- size_t csize;
- check_allocation(file, line, function, mark_allocation(ptr, size), &csize);
- if(size != csize) {
- fatal("Invalid size.");
- }
-
- return mark_allocation(ptr, size);
-}
-
-void *calloc_debug(const char *file, int line, const char *function, size_t nmemb, size_t size) {
- void *ptr = malloc_debug(file, line, function, (nmemb * size));
- bzero(ptr, nmemb * size);
- return ptr;
-}
-
-void free_debug(const char *file, int line, const char *function, void *ptr) {
- size_t size;
- void *real_ptr = check_allocation(file, line, function, ptr, &size);
-
- bzero(real_ptr, size + MALLOC_OVERHEAD);
-
- free(real_ptr);
- allocations.allocated -= size;
- allocations.allocations--;
-
- debug(D_MEMORY, "MEMORY: freed %zu bytes for %s/%u@%s."
- " Status: allocated %zu in %zu allocs."
- , size
- , function, line, file
- , allocations.allocated
- , allocations.allocations
- );
-}
-
-void *realloc_debug(const char *file, int line, const char *function, void *ptr, size_t size) {
- if(!ptr) return malloc_debug(file, line, function, size);
- if(!size) { free_debug(file, line, function, ptr); return NULL; }
-
- size_t old_size;
- void *real_ptr = check_allocation(file, line, function, ptr, &old_size);
-
- void *new_ptr = realloc(real_ptr, size + MALLOC_OVERHEAD);
- if(!new_ptr) fatal("MEMORY: Cannot allocate %zu bytes for %s/%u@%s.", size, function, line, file);
+pid_t *all_pids_sortlist = NULL;
- allocations.allocated += size;
- allocations.allocated -= old_size;
+// will be automatically set to 1, if guest values are collected
+int show_guest_time = 0;
+int show_guest_time_old = 0;
- debug(D_MEMORY, "MEMORY: Re-allocated from %zu to %zu bytes for %s/%u@%s."
- " Status: allocated %z in %zu allocs."
- , old_size, size
- , function, line, file
- , allocations.allocated
- , allocations.allocations
- );
+int enable_guest_charts = 0;
+int enable_file_charts = 1;
- if(allocations.allocated > allocations.allocated_max) {
- debug(D_MEMORY, "MEMORY: total allocation peak increased from %zu to %zu", allocations.allocated_max, allocations.allocated);
- allocations.allocated_max = allocations.allocated;
- }
-
- return mark_allocation(new_ptr, size);
-}
-
-char *strdup_debug(const char *file, int line, const char *function, const char *ptr) {
- size_t size = 0;
- const char *s = ptr;
-
- while(*s++) size++;
- size++;
-
- char *p = malloc_debug(file, line, function, size);
- if(!p) fatal("Cannot allocate %zu bytes.", size);
+// ----------------------------------------------------------------------------
- memcpy(p, ptr, size);
- return p;
+void netdata_cleanup_and_exit(int ret) {
+ exit(ret);
}
-#define malloc(size) malloc_debug(__FILE__, __LINE__, __FUNCTION__, (size))
-#define calloc(nmemb, size) calloc_debug(__FILE__, __LINE__, __FUNCTION__, (nmemb), (size))
-#define realloc(ptr, size) realloc_debug(__FILE__, __LINE__, __FUNCTION__, (ptr), (size))
-#define free(ptr) free_debug(__FILE__, __LINE__, __FUNCTION__, (ptr))
-
-#ifdef strdup
-#undef strdup
-#endif
-#define strdup(ptr) strdup_debug(__FILE__, __LINE__, __FUNCTION__, (ptr))
-
-#endif /* NETDATA_INTERNAL_CHECKS */
// ----------------------------------------------------------------------------
// system functions
// to retrieve settings of the system
long get_system_cpus(void) {
- procfile *ff = NULL;
+ procfile *ff = NULL;
- int processors = 0;
+ int processors = 0;
- char filename[FILENAME_MAX + 1];
- snprintfz(filename, FILENAME_MAX, "%s/proc/stat", host_prefix);
+ char filename[FILENAME_MAX + 1];
+ snprintfz(filename, FILENAME_MAX, "%s/proc/stat", host_prefix);
- ff = procfile_open(filename, NULL, PROCFILE_FLAG_DEFAULT);
- if(!ff) return 1;
+ ff = procfile_open(filename, NULL, PROCFILE_FLAG_DEFAULT);
+ if(!ff) return 1;
- ff = procfile_readall(ff);
- if(!ff) {
- procfile_close(ff);
- return 1;
- }
+ ff = procfile_readall(ff);
+ if(!ff)
+ return 1;
- unsigned int i;
- for(i = 0; i < procfile_lines(ff); i++) {
- if(!procfile_linewords(ff, i)) continue;
+ unsigned int i;
+ for(i = 0; i < procfile_lines(ff); i++) {
+ if(!procfile_linewords(ff, i)) continue;
- if(strncmp(procfile_lineword(ff, i, 0), "cpu", 3) == 0) processors++;
- }
- processors--;
- if(processors < 1) processors = 1;
+ if(strncmp(procfile_lineword(ff, i, 0), "cpu", 3) == 0) processors++;
+ }
+ processors--;
+ if(processors < 1) processors = 1;
- procfile_close(ff);
- return processors;
+ procfile_close(ff);
+ return processors;
}
-long get_system_pid_max(void) {
- procfile *ff = NULL;
- long mpid = 32768;
+pid_t get_system_pid_max(void) {
+ procfile *ff = NULL;
+ pid_t mpid = 32768;
- char filename[FILENAME_MAX + 1];
- snprintfz(filename, FILENAME_MAX, "%s/proc/sys/kernel/pid_max", host_prefix);
- ff = procfile_open(filename, NULL, PROCFILE_FLAG_DEFAULT);
- if(!ff) return mpid;
+ char filename[FILENAME_MAX + 1];
+ snprintfz(filename, FILENAME_MAX, "%s/proc/sys/kernel/pid_max", host_prefix);
+ ff = procfile_open(filename, NULL, PROCFILE_FLAG_DEFAULT);
+ if(!ff) return mpid;
- ff = procfile_readall(ff);
- if(!ff) {
- procfile_close(ff);
- return mpid;
- }
+ ff = procfile_readall(ff);
+ if(!ff)
+ return mpid;
- mpid = atol(procfile_lineword(ff, 0, 0));
- if(!mpid) mpid = 32768;
+ mpid = (pid_t)atoi(procfile_lineword(ff, 0, 0));
+ if(!mpid) mpid = 32768;
- procfile_close(ff);
- return mpid;
+ procfile_close(ff);
+ return mpid;
}
// ----------------------------------------------------------------------------
@@ -274,83 +98,68 @@ long get_system_pid_max(void) {
// target is the structure that process data are aggregated
struct target {
- char compare[MAX_COMPARE_NAME + 1];
- uint32_t comparehash;
- size_t comparelen;
-
- char id[MAX_NAME + 1];
- uint32_t idhash;
-
- char name[MAX_NAME + 1];
-
- uid_t uid;
- gid_t gid;
-
- unsigned long long minflt;
- unsigned long long cminflt;
- unsigned long long majflt;
- unsigned long long cmajflt;
- unsigned long long utime;
- unsigned long long stime;
- unsigned long long cutime;
- unsigned long long cstime;
- unsigned long long num_threads;
- unsigned long long rss;
-
- unsigned long long fix_minflt;
- unsigned long long fix_cminflt;
- unsigned long long fix_majflt;
- unsigned long long fix_cmajflt;
- unsigned long long fix_utime;
- unsigned long long fix_stime;
- unsigned long long fix_cutime;
- unsigned long long fix_cstime;
-
- unsigned long long statm_size;
- unsigned long long statm_resident;
- unsigned long long statm_share;
- unsigned long long statm_text;
- unsigned long long statm_lib;
- unsigned long long statm_data;
- unsigned long long statm_dirty;
-
- unsigned long long io_logical_bytes_read;
- unsigned long long io_logical_bytes_written;
- unsigned long long io_read_calls;
- unsigned long long io_write_calls;
- unsigned long long io_storage_bytes_read;
- unsigned long long io_storage_bytes_written;
- unsigned long long io_cancelled_write_bytes;
-
- unsigned long long fix_io_logical_bytes_read;
- unsigned long long fix_io_logical_bytes_written;
- unsigned long long fix_io_read_calls;
- unsigned long long fix_io_write_calls;
- unsigned long long fix_io_storage_bytes_read;
- unsigned long long fix_io_storage_bytes_written;
- unsigned long long fix_io_cancelled_write_bytes;
-
- int *fds;
- unsigned long long openfiles;
- unsigned long long openpipes;
- unsigned long long opensockets;
- unsigned long long openinotifies;
- unsigned long long openeventfds;
- unsigned long long opentimerfds;
- unsigned long long opensignalfds;
- unsigned long long openeventpolls;
- unsigned long long openother;
-
- unsigned long processes; // how many processes have been merged to this
- int exposed; // if set, we have sent this to netdata
- int hidden; // if set, we set the hidden flag on the dimension
- int debug;
- int ends_with;
- int starts_with; // if set, the compare string matches only the
- // beginning of the command
-
- struct target *target; // the one that will be reported to netdata
- struct target *next;
+ char compare[MAX_COMPARE_NAME + 1];
+ uint32_t comparehash;
+ size_t comparelen;
+
+ char id[MAX_NAME + 1];
+ uint32_t idhash;
+
+ char name[MAX_NAME + 1];
+
+ uid_t uid;
+ gid_t gid;
+
+ unsigned long long minflt;
+ unsigned long long cminflt;
+ unsigned long long majflt;
+ unsigned long long cmajflt;
+ unsigned long long utime;
+ unsigned long long stime;
+ unsigned long long gtime;
+ unsigned long long cutime;
+ unsigned long long cstime;
+ unsigned long long cgtime;
+ unsigned long long num_threads;
+ unsigned long long rss;
+
+ unsigned long long statm_size;
+ unsigned long long statm_resident;
+ unsigned long long statm_share;
+ unsigned long long statm_text;
+ unsigned long long statm_lib;
+ unsigned long long statm_data;
+ unsigned long long statm_dirty;
+
+ unsigned long long io_logical_bytes_read;
+ unsigned long long io_logical_bytes_written;
+ unsigned long long io_read_calls;
+ unsigned long long io_write_calls;
+ unsigned long long io_storage_bytes_read;
+ unsigned long long io_storage_bytes_written;
+ unsigned long long io_cancelled_write_bytes;
+
+ int *fds;
+ unsigned long long openfiles;
+ unsigned long long openpipes;
+ unsigned long long opensockets;
+ unsigned long long openinotifies;
+ unsigned long long openeventfds;
+ unsigned long long opentimerfds;
+ unsigned long long opensignalfds;
+ unsigned long long openeventpolls;
+ unsigned long long openother;
+
+ unsigned long processes; // how many processes have been merged to this
+ int exposed; // if set, we have sent this to netdata
+ int hidden; // if set, we set the hidden flag on the dimension
+ int debug;
+ int ends_with;
+ int starts_with; // if set, the compare string matches only the
+ // beginning of the command
+
+ struct target *target; // the one that will be reported to netdata
+ struct target *next;
};
@@ -367,227 +176,212 @@ struct target *groups_root_target = NULL;
struct target *get_users_target(uid_t uid)
{
- struct target *w;
- for(w = users_root_target ; w ; w = w->next)
- if(w->uid == uid) return w;
+ struct target *w;
+ for(w = users_root_target ; w ; w = w->next)
+ if(w->uid == uid) return w;
- w = calloc(sizeof(struct target), 1);
- if(unlikely(!w)) {
- error("Cannot allocate %lu bytes of memory", (unsigned long)sizeof(struct target));
- return NULL;
- }
+ w = callocz(sizeof(struct target), 1);
+ snprintfz(w->compare, MAX_COMPARE_NAME, "%u", uid);
+ w->comparehash = simple_hash(w->compare);
+ w->comparelen = strlen(w->compare);
- snprintfz(w->compare, MAX_COMPARE_NAME, "%d", uid);
- w->comparehash = simple_hash(w->compare);
- w->comparelen = strlen(w->compare);
+ snprintfz(w->id, MAX_NAME, "%u", uid);
+ w->idhash = simple_hash(w->id);
- snprintfz(w->id, MAX_NAME, "%d", uid);
- w->idhash = simple_hash(w->id);
+ struct passwd *pw = getpwuid(uid);
+ if(!pw)
+ snprintfz(w->name, MAX_NAME, "%u", uid);
+ else
+ snprintfz(w->name, MAX_NAME, "%s", pw->pw_name);
- struct passwd *pw = getpwuid(uid);
- if(!pw)
- snprintfz(w->name, MAX_NAME, "%d", uid);
- else
- snprintfz(w->name, MAX_NAME, "%s", pw->pw_name);
+ netdata_fix_chart_name(w->name);
- netdata_fix_chart_name(w->name);
+ w->uid = uid;
- w->uid = uid;
+ w->next = users_root_target;
+ users_root_target = w;
- w->next = users_root_target;
- users_root_target = w;
+ if(unlikely(debug))
+ fprintf(stderr, "apps.plugin: added uid %u ('%s') target\n", w->uid, w->name);
- if(unlikely(debug))
- fprintf(stderr, "apps.plugin: added uid %d ('%s') target\n", w->uid, w->name);
-
- return w;
+ return w;
}
struct target *get_groups_target(gid_t gid)
{
- struct target *w;
- for(w = groups_root_target ; w ; w = w->next)
- if(w->gid == gid) return w;
-
- w = calloc(sizeof(struct target), 1);
- if(unlikely(!w)) {
- error("Cannot allocate %lu bytes of memory", (unsigned long)sizeof(struct target));
- return NULL;
- }
+ struct target *w;
+ for(w = groups_root_target ; w ; w = w->next)
+ if(w->gid == gid) return w;
- snprintfz(w->compare, MAX_COMPARE_NAME, "%d", gid);
- w->comparehash = simple_hash(w->compare);
- w->comparelen = strlen(w->compare);
+ w = callocz(sizeof(struct target), 1);
+ snprintfz(w->compare, MAX_COMPARE_NAME, "%u", gid);
+ w->comparehash = simple_hash(w->compare);
+ w->comparelen = strlen(w->compare);
- snprintfz(w->id, MAX_NAME, "%d", gid);
- w->idhash = simple_hash(w->id);
+ snprintfz(w->id, MAX_NAME, "%u", gid);
+ w->idhash = simple_hash(w->id);
- struct group *gr = getgrgid(gid);
- if(!gr)
- snprintfz(w->name, MAX_NAME, "%d", gid);
- else
- snprintfz(w->name, MAX_NAME, "%s", gr->gr_name);
+ struct group *gr = getgrgid(gid);
+ if(!gr)
+ snprintfz(w->name, MAX_NAME, "%u", gid);
+ else
+ snprintfz(w->name, MAX_NAME, "%s", gr->gr_name);
- netdata_fix_chart_name(w->name);
+ netdata_fix_chart_name(w->name);
- w->gid = gid;
+ w->gid = gid;
- w->next = groups_root_target;
- groups_root_target = w;
+ w->next = groups_root_target;
+ groups_root_target = w;
- if(unlikely(debug))
- fprintf(stderr, "apps.plugin: added gid %d ('%s') target\n", w->gid, w->name);
+ if(unlikely(debug))
+ fprintf(stderr, "apps.plugin: added gid %u ('%s') target\n", w->gid, w->name);
- return w;
+ return w;
}
// find or create a new target
// there are targets that are just aggregated to other target (the second argument)
-struct target *get_apps_groups_target(const char *id, struct target *target)
-{
- int tdebug = 0, thidden = 0, ends_with = 0;
- const char *nid = id;
-
- while(nid[0] == '-' || nid[0] == '+' || nid[0] == '*') {
- if(nid[0] == '-') thidden = 1;
- if(nid[0] == '+') tdebug = 1;
- if(nid[0] == '*') ends_with = 1;
- nid++;
- }
- uint32_t hash = simple_hash(id);
-
- struct target *w;
- for(w = apps_groups_root_target ; w ; w = w->next) {
- if(w->idhash == hash && strncmp(nid, w->id, MAX_NAME) == 0)
- return w;
- }
-
- w = calloc(sizeof(struct target), 1);
- if(unlikely(!w)) {
- error("Cannot allocate %lu bytes of memory", (unsigned long)sizeof(struct target));
- return NULL;
- }
-
- strncpyz(w->id, nid, MAX_NAME);
- w->idhash = simple_hash(w->id);
-
- strncpyz(w->name, nid, MAX_NAME);
-
- strncpyz(w->compare, nid, MAX_COMPARE_NAME);
- int len = strlen(w->compare);
- if(w->compare[len - 1] == '*') {
- w->compare[len - 1] = '\0';
- w->starts_with = 1;
- }
- w->ends_with = ends_with;
-
- if(w->starts_with && w->ends_with)
- proc_pid_cmdline_is_needed = 1;
-
- w->comparehash = simple_hash(w->compare);
- w->comparelen = strlen(w->compare);
-
- w->hidden = thidden;
- w->debug = tdebug;
- w->target = target;
-
- w->next = apps_groups_root_target;
- apps_groups_root_target = w;
-
- if(unlikely(debug))
- fprintf(stderr, "apps.plugin: ADDING TARGET ID '%s', process name '%s' (%s), aggregated on target '%s', options: %s %s\n"
- , w->id
- , w->compare, (w->starts_with && w->ends_with)?"substring":((w->starts_with)?"prefix":((w->ends_with)?"suffix":"exact"))
- , w->target?w->target->id:w->id
- , (w->hidden)?"hidden":"-"
- , (w->debug)?"debug":"-"
- );
-
- return w;
+struct target *get_apps_groups_target(const char *id, struct target *target) {
+ int tdebug = 0, thidden = 0, ends_with = 0;
+ const char *nid = id;
+
+ while(nid[0] == '-' || nid[0] == '+' || nid[0] == '*') {
+ if(nid[0] == '-') thidden = 1;
+ if(nid[0] == '+') tdebug = 1;
+ if(nid[0] == '*') ends_with = 1;
+ nid++;
+ }
+ uint32_t hash = simple_hash(id);
+
+ struct target *w, *last = apps_groups_root_target;
+ for(w = apps_groups_root_target ; w ; w = w->next) {
+ if(w->idhash == hash && strncmp(nid, w->id, MAX_NAME) == 0)
+ return w;
+
+ last = w;
+ }
+
+ w = callocz(sizeof(struct target), 1);
+ strncpyz(w->id, nid, MAX_NAME);
+ w->idhash = simple_hash(w->id);
+
+ strncpyz(w->name, nid, MAX_NAME);
+
+ strncpyz(w->compare, nid, MAX_COMPARE_NAME);
+ size_t len = strlen(w->compare);
+ if(w->compare[len - 1] == '*') {
+ w->compare[len - 1] = '\0';
+ w->starts_with = 1;
+ }
+ w->ends_with = ends_with;
+
+ if(w->starts_with && w->ends_with)
+ proc_pid_cmdline_is_needed = 1;
+
+ w->comparehash = simple_hash(w->compare);
+ w->comparelen = strlen(w->compare);
+
+ w->hidden = thidden;
+ w->debug = tdebug;
+ w->target = target;
+
+ // append it, to maintain the order in apps_groups.conf
+ if(last) last->next = w;
+ else apps_groups_root_target = w;
+
+ if(unlikely(debug))
+ fprintf(stderr, "apps.plugin: ADDING TARGET ID '%s', process name '%s' (%s), aggregated on target '%s', options: %s %s\n"
+ , w->id
+ , w->compare, (w->starts_with && w->ends_with)?"substring":((w->starts_with)?"prefix":((w->ends_with)?"suffix":"exact"))
+ , w->target?w->target->id:w->id
+ , (w->hidden)?"hidden":"-"
+ , (w->debug)?"debug":"-"
+ );
+
+ return w;
}
// read the apps_groups.conf file
int read_apps_groups_conf(const char *name)
{
- char filename[FILENAME_MAX + 1];
+ char filename[FILENAME_MAX + 1];
- snprintfz(filename, FILENAME_MAX, "%s/apps_%s.conf", config_dir, name);
+ snprintfz(filename, FILENAME_MAX, "%s/apps_%s.conf", config_dir, name);
- if(unlikely(debug))
- fprintf(stderr, "apps.plugin: process groups file: '%s'\n", filename);
+ if(unlikely(debug))
+ fprintf(stderr, "apps.plugin: process groups file: '%s'\n", filename);
- // ----------------------------------------
+ // ----------------------------------------
- procfile *ff = procfile_open(filename, " :\t", PROCFILE_FLAG_DEFAULT);
- if(!ff) return 1;
+ procfile *ff = procfile_open(filename, " :\t", PROCFILE_FLAG_DEFAULT);
+ if(!ff) return 1;
- procfile_set_quotes(ff, "'\"");
+ procfile_set_quotes(ff, "'\"");
- ff = procfile_readall(ff);
- if(!ff) {
- procfile_close(ff);
- return 1;
- }
+ ff = procfile_readall(ff);
+ if(!ff)
+ return 1;
- unsigned long line, lines = procfile_lines(ff);
+ unsigned long line, lines = procfile_lines(ff);
- for(line = 0; line < lines ;line++) {
- unsigned long word, words = procfile_linewords(ff, line);
- struct target *w = NULL;
+ for(line = 0; line < lines ;line++) {
+ unsigned long word, words = procfile_linewords(ff, line);
+ struct target *w = NULL;
- char *t = procfile_lineword(ff, line, 0);
- if(!t || !*t) continue;
+ char *t = procfile_lineword(ff, line, 0);
+ if(!t || !*t) continue;
- for(word = 0; word < words ;word++) {
- char *s = procfile_lineword(ff, line, word);
- if(!s || !*s) continue;
- if(*s == '#') break;
+ for(word = 0; word < words ;word++) {
+ char *s = procfile_lineword(ff, line, word);
+ if(!s || !*s) continue;
+ if(*s == '#') break;
- if(t == s) continue;
+ if(t == s) continue;
- struct target *n = get_apps_groups_target(s, w);
- if(!n) {
- error("Cannot create target '%s' (line %d, word %d)", s, line, word);
- continue;
- }
+ struct target *n = get_apps_groups_target(s, w);
+ if(!n) {
+ error("Cannot create target '%s' (line %lu, word %lu)", s, line, word);
+ continue;
+ }
- if(!w) w = n;
- }
+ if(!w) w = n;
+ }
- if(w) {
- int tdebug = 0, thidden = 0;
+ if(w) {
+ int tdebug = 0, thidden = 0;
- while(t[0] == '-' || t[0] == '+') {
- if(t[0] == '-') thidden = 1;
- if(t[0] == '+') tdebug = 1;
- t++;
- }
+ while(t[0] == '-' || t[0] == '+') {
+ if(t[0] == '-') thidden = 1;
+ if(t[0] == '+') tdebug = 1;
+ t++;
+ }
- strncpyz(w->name, t, MAX_NAME);
- w->hidden = thidden;
- w->debug = tdebug;
+ strncpyz(w->name, t, MAX_NAME);
+ w->hidden = thidden;
+ w->debug = tdebug;
- if(unlikely(debug))
- fprintf(stderr, "apps.plugin: AGGREGATION TARGET NAME '%s' on ID '%s', process name '%s' (%s), aggregated on target '%s', options: %s %s\n"
- , w->name
- , w->id
- , w->compare, (w->starts_with && w->ends_with)?"substring":((w->starts_with)?"prefix":((w->ends_with)?"suffix":"exact"))
- , w->target?w->target->id:w->id
- , (w->hidden)?"hidden":"-"
- , (w->debug)?"debug":"-"
- );
- }
- }
+ if(unlikely(debug))
+ fprintf(stderr, "apps.plugin: AGGREGATION TARGET NAME '%s' on ID '%s', process name '%s' (%s), aggregated on target '%s', options: %s %s\n"
+ , w->name
+ , w->id
+ , w->compare, (w->starts_with && w->ends_with)?"substring":((w->starts_with)?"prefix":((w->ends_with)?"suffix":"exact"))
+ , w->target?w->target->id:w->id
+ , (w->hidden)?"hidden":"-"
+ , (w->debug)?"debug":"-"
+ );
+ }
+ }
- procfile_close(ff);
+ procfile_close(ff);
- apps_groups_default_target = get_apps_groups_target("p+!o@w#e$i^r&7*5(-i)l-o_", NULL); // match nothing
- if(!apps_groups_default_target)
- error("Cannot create default target");
- else
- strncpyz(apps_groups_default_target->name, "other", MAX_NAME);
+ apps_groups_default_target = get_apps_groups_target("p+!o@w#e$i^r&7*5(-i)l-o_", NULL); // match nothing
+ if(!apps_groups_default_target)
+ error("Cannot create default target");
+ else
+ strncpyz(apps_groups_default_target->name, "other", MAX_NAME);
- return 0;
+ return 0;
}
@@ -595,178 +389,183 @@ int read_apps_groups_conf(const char *name)
// data to store for each pid
// see: man proc
-struct pid_stat {
- int32_t pid;
- char comm[MAX_COMPARE_NAME + 1];
- char cmdline[MAX_CMDLINE + 1];
-
- // char state;
- int32_t ppid;
- // int32_t pgrp;
- // int32_t session;
- // int32_t tty_nr;
- // int32_t tpgid;
- // uint64_t flags;
- unsigned long long minflt;
- unsigned long long cminflt;
- unsigned long long majflt;
- unsigned long long cmajflt;
- unsigned long long utime;
- unsigned long long stime;
- unsigned long long cutime;
- unsigned long long cstime;
- // int64_t priority;
- // int64_t nice;
- int32_t num_threads;
- // int64_t itrealvalue;
- // unsigned long long starttime;
- // unsigned long long vsize;
- unsigned long long rss;
- // unsigned long long rsslim;
- // unsigned long long starcode;
- // unsigned long long endcode;
- // unsigned long long startstack;
- // unsigned long long kstkesp;
- // unsigned long long kstkeip;
- // uint64_t signal;
- // uint64_t blocked;
- // uint64_t sigignore;
- // uint64_t sigcatch;
- // uint64_t wchan;
- // uint64_t nswap;
- // uint64_t cnswap;
- // int32_t exit_signal;
- // int32_t processor;
- // uint32_t rt_priority;
- // uint32_t policy;
- // unsigned long long delayacct_blkio_ticks;
- // uint64_t guest_time;
- // int64_t cguest_time;
-
- uid_t uid;
- gid_t gid;
-
- unsigned long long statm_size;
- unsigned long long statm_resident;
- unsigned long long statm_share;
- unsigned long long statm_text;
- unsigned long long statm_lib;
- unsigned long long statm_data;
- unsigned long long statm_dirty;
-
- unsigned long long io_logical_bytes_read;
- unsigned long long io_logical_bytes_written;
- unsigned long long io_read_calls;
- unsigned long long io_write_calls;
- unsigned long long io_storage_bytes_read;
- unsigned long long io_storage_bytes_written;
- unsigned long long io_cancelled_write_bytes;
-
- // we need the last values
- // for all incremental counters
- // so that when a process switches users/groups
- // we will subtract these values from the old
- // target
- unsigned long long last_minflt;
- unsigned long long last_cminflt;
- unsigned long long last_majflt;
- unsigned long long last_cmajflt;
- unsigned long long last_utime;
- unsigned long long last_stime;
- unsigned long long last_cutime;
- unsigned long long last_cstime;
-
- unsigned long long last_io_logical_bytes_read;
- unsigned long long last_io_logical_bytes_written;
- unsigned long long last_io_read_calls;
- unsigned long long last_io_write_calls;
- unsigned long long last_io_storage_bytes_read;
- unsigned long long last_io_storage_bytes_written;
- unsigned long long last_io_cancelled_write_bytes;
-
-#ifdef AGGREGATE_CHILDREN_TO_PARENTS
- unsigned long long old_utime;
- unsigned long long old_stime;
- unsigned long long old_minflt;
- unsigned long long old_majflt;
-
- unsigned long long old_cutime;
- unsigned long long old_cstime;
- unsigned long long old_cminflt;
- unsigned long long old_cmajflt;
-
- unsigned long long fix_cutime;
- unsigned long long fix_cstime;
- unsigned long long fix_cminflt;
- unsigned long long fix_cmajflt;
-
- unsigned long long diff_cutime;
- unsigned long long diff_cstime;
- unsigned long long diff_cminflt;
- unsigned long long diff_cmajflt;
-#endif /* AGGREGATE_CHILDREN_TO_PARENTS */
-
- int *fds; // array of fds it uses
- int fds_size; // the size of the fds array
-
- int children_count; // number of processes directly referencing this
- int updated; // 1 when update
- int merged; // 1 when it has been merged to its parent
- int new_entry;
-
- struct target *target; // app_groups.conf targets
- struct target *user_target; // uid based targets
- struct target *group_target; // gid based targets
-
- struct pid_stat *parent;
- struct pid_stat *prev;
- struct pid_stat *next;
+#define PID_LOG_IO 0x00000001
+#define PID_LOG_STATM 0x00000002
+#define PID_LOG_CMDLINE 0x00000004
+#define PID_LOG_FDS 0x00000008
+#define PID_LOG_STAT 0x00000010
+struct pid_stat {
+ int32_t pid;
+ char comm[MAX_COMPARE_NAME + 1];
+ char cmdline[MAX_CMDLINE + 1];
+
+ uint32_t log_thrown;
+
+ // char state;
+ int32_t ppid;
+ // int32_t pgrp;
+ // int32_t session;
+ // int32_t tty_nr;
+ // int32_t tpgid;
+ // uint64_t flags;
+
+ // these are raw values collected
+ unsigned long long minflt_raw;
+ unsigned long long cminflt_raw;
+ unsigned long long majflt_raw;
+ unsigned long long cmajflt_raw;
+ unsigned long long utime_raw;
+ unsigned long long stime_raw;
+ unsigned long long gtime_raw; // guest_time
+ unsigned long long cutime_raw;
+ unsigned long long cstime_raw;
+ unsigned long long cgtime_raw; // cguest_time
+
+ // these are rates
+ unsigned long long minflt;
+ unsigned long long cminflt;
+ unsigned long long majflt;
+ unsigned long long cmajflt;
+ unsigned long long utime;
+ unsigned long long stime;
+ unsigned long long gtime;
+ unsigned long long cutime;
+ unsigned long long cstime;
+ unsigned long long cgtime;
+
+ // int64_t priority;
+ // int64_t nice;
+ int32_t num_threads;
+ // int64_t itrealvalue;
+ // unsigned long long starttime;
+ // unsigned long long vsize;
+ unsigned long long rss;
+ // unsigned long long rsslim;
+ // unsigned long long starcode;
+ // unsigned long long endcode;
+ // unsigned long long startstack;
+ // unsigned long long kstkesp;
+ // unsigned long long kstkeip;
+ // uint64_t signal;
+ // uint64_t blocked;
+ // uint64_t sigignore;
+ // uint64_t sigcatch;
+ // uint64_t wchan;
+ // uint64_t nswap;
+ // uint64_t cnswap;
+ // int32_t exit_signal;
+ // int32_t processor;
+ // uint32_t rt_priority;
+ // uint32_t policy;
+ // unsigned long long delayacct_blkio_ticks;
+
+ uid_t uid;
+ gid_t gid;
+
+ unsigned long long statm_size;
+ unsigned long long statm_resident;
+ unsigned long long statm_share;
+ unsigned long long statm_text;
+ unsigned long long statm_lib;
+ unsigned long long statm_data;
+ unsigned long long statm_dirty;
+
+ unsigned long long io_logical_bytes_read_raw;
+ unsigned long long io_logical_bytes_written_raw;
+ unsigned long long io_read_calls_raw;
+ unsigned long long io_write_calls_raw;
+ unsigned long long io_storage_bytes_read_raw;
+ unsigned long long io_storage_bytes_written_raw;
+ unsigned long long io_cancelled_write_bytes_raw;
+
+ unsigned long long io_logical_bytes_read;
+ unsigned long long io_logical_bytes_written;
+ unsigned long long io_read_calls;
+ unsigned long long io_write_calls;
+ unsigned long long io_storage_bytes_read;
+ unsigned long long io_storage_bytes_written;
+ unsigned long long io_cancelled_write_bytes;
+
+ int *fds; // array of fds it uses
+ int fds_size; // the size of the fds array
+
+ int children_count; // number of processes directly referencing this
+ int keep; // 1 when we need to keep this process in memory even after it exited
+ int keeploops; // increases by 1 every time keep is 1 and updated 0
+ int updated; // 1 when the process is currently running
+ int merged; // 1 when it has been merged to its parent
+ int new_entry; // 1 when this is a new process, just saw for the first time
+ int read; // 1 when we have already read this process for this iteration
+ int sortlist; // higher numbers = top on the process tree
+ // each process gets a unique number
+
+ struct target *target; // app_groups.conf targets
+ struct target *user_target; // uid based targets
+ struct target *group_target; // gid based targets
+
+ unsigned long long stat_collected_usec;
+ unsigned long long last_stat_collected_usec;
+
+ unsigned long long io_collected_usec;
+ unsigned long long last_io_collected_usec;
+
+ char *stat_filename;
+ char *statm_filename;
+ char *io_filename;
+ char *cmdline_filename;
+
+ struct pid_stat *parent;
+ struct pid_stat *prev;
+ struct pid_stat *next;
} *root_of_pids = NULL, **all_pids;
long all_pids_count = 0;
-struct pid_stat *get_pid_entry(pid_t pid)
-{
- if(all_pids[pid]) {
- all_pids[pid]->new_entry = 0;
- return all_pids[pid];
- }
-
- all_pids[pid] = calloc(sizeof(struct pid_stat), 1);
- if(!all_pids[pid]) {
- error("Cannot allocate %lu bytes of memory", (unsigned long)sizeof(struct pid_stat));
- return NULL;
- }
-
- all_pids[pid]->fds = calloc(sizeof(int), 100);
- if(!all_pids[pid]->fds)
- error("Cannot allocate %ld bytes of memory", (unsigned long)(sizeof(int) * 100));
- else all_pids[pid]->fds_size = 100;
-
- if(root_of_pids) root_of_pids->prev = all_pids[pid];
- all_pids[pid]->next = root_of_pids;
- root_of_pids = all_pids[pid];
-
- all_pids[pid]->pid = pid;
- all_pids[pid]->new_entry = 1;
-
- return all_pids[pid];
+struct pid_stat *get_pid_entry(pid_t pid) {
+ if(all_pids[pid]) {
+ all_pids[pid]->new_entry = 0;
+ return all_pids[pid];
+ }
+
+ all_pids[pid] = callocz(sizeof(struct pid_stat), 1);
+ all_pids[pid]->fds = callocz(sizeof(int), 100);
+ all_pids[pid]->fds_size = 100;
+
+ if(root_of_pids) root_of_pids->prev = all_pids[pid];
+ all_pids[pid]->next = root_of_pids;
+ root_of_pids = all_pids[pid];
+
+ all_pids[pid]->pid = pid;
+ all_pids[pid]->new_entry = 1;
+
+ all_pids_count++;
+
+ return all_pids[pid];
}
-void del_pid_entry(pid_t pid)
-{
- if(!all_pids[pid]) return;
+void del_pid_entry(pid_t pid) {
+ if(!all_pids[pid]) {
+ error("attempted to free pid %d that is not allocated.", pid);
+ return;
+ }
+
+ if(unlikely(debug))
+ fprintf(stderr, "apps.plugin: process %d %s exited, deleting it.\n", pid, all_pids[pid]->comm);
- if(debug) fprintf(stderr, "apps.plugin: process %d %s exited, deleting it.\n", pid, all_pids[pid]->comm);
+ if(root_of_pids == all_pids[pid]) root_of_pids = all_pids[pid]->next;
+ if(all_pids[pid]->next) all_pids[pid]->next->prev = all_pids[pid]->prev;
+ if(all_pids[pid]->prev) all_pids[pid]->prev->next = all_pids[pid]->next;
- if(root_of_pids == all_pids[pid]) root_of_pids = all_pids[pid]->next;
- if(all_pids[pid]->next) all_pids[pid]->next->prev = all_pids[pid]->prev;
- if(all_pids[pid]->prev) all_pids[pid]->prev->next = all_pids[pid]->next;
+ if(all_pids[pid]->fds) freez(all_pids[pid]->fds);
+ if(all_pids[pid]->stat_filename) freez(all_pids[pid]->stat_filename);
+ if(all_pids[pid]->statm_filename) freez(all_pids[pid]->statm_filename);
+ if(all_pids[pid]->io_filename) freez(all_pids[pid]->io_filename);
+ if(all_pids[pid]->cmdline_filename) freez(all_pids[pid]->cmdline_filename);
+ freez(all_pids[pid]);
- if(all_pids[pid]->fds) free(all_pids[pid]->fds);
- free(all_pids[pid]);
- all_pids[pid] = NULL;
+ all_pids[pid] = NULL;
+ all_pids_count--;
}
@@ -774,188 +573,380 @@ void del_pid_entry(pid_t pid)
// update pids from proc
int read_proc_pid_cmdline(struct pid_stat *p) {
- char filename[FILENAME_MAX + 1];
- snprintfz(filename, FILENAME_MAX, "%s/proc/%d/cmdline", host_prefix, p->pid);
+
+ if(unlikely(!p->cmdline_filename)) {
+ char filename[FILENAME_MAX + 1];
+ snprintfz(filename, FILENAME_MAX, "%s/proc/%d/cmdline", host_prefix, p->pid);
+ p->cmdline_filename = strdupz(filename);
+ }
- int fd = open(filename, O_RDONLY, 0666);
- if(unlikely(fd == -1)) return 1;
+ int fd = open(p->cmdline_filename, O_RDONLY, 0666);
+ if(unlikely(fd == -1)) goto cleanup;
- int i, bytes = read(fd, p->cmdline, MAX_CMDLINE);
- close(fd);
+ ssize_t i, bytes = read(fd, p->cmdline, MAX_CMDLINE);
+ close(fd);
- if(bytes <= 0) {
- // copy the command to the command line
- strncpyz(p->cmdline, p->comm, MAX_CMDLINE);
- return 0;
- }
+ if(unlikely(bytes < 0)) goto cleanup;
- p->cmdline[bytes] = '\0';
- for(i = 0; i < bytes ; i++)
- if(!p->cmdline[i]) p->cmdline[i] = ' ';
+ p->cmdline[bytes] = '\0';
+ for(i = 0; i < bytes ; i++)
+ if(unlikely(!p->cmdline[i])) p->cmdline[i] = ' ';
- if(unlikely(debug))
- fprintf(stderr, "Read file '%s' contents: %s\n", filename, p->cmdline);
+ if(unlikely(debug))
+ fprintf(stderr, "Read file '%s' contents: %s\n", p->cmdline_filename, p->cmdline);
- return 0;
+ return 1;
+
+cleanup:
+ // copy the command to the command line
+ strncpyz(p->cmdline, p->comm, MAX_CMDLINE);
+ return 0;
}
int read_proc_pid_ownership(struct pid_stat *p) {
- char filename[FILENAME_MAX + 1];
-
- snprintfz(filename, FILENAME_MAX, "%s/proc/%d", host_prefix, p->pid);
+ if(unlikely(!p->stat_filename)) {
+ error("pid %d does not have a stat_filename", p->pid);
+ return 0;
+ }
- // ----------------------------------------
- // read uid and gid
+ // ----------------------------------------
+ // read uid and gid
- struct stat st;
- if(stat(filename, &st) != 0)
- return 1;
+ struct stat st;
+ if(stat(p->stat_filename, &st) != 0) {
+ error("Cannot stat file '%s'", p->stat_filename);
+ return 1;
+ }
- p->uid = st.st_uid;
- p->gid = st.st_gid;
+ p->uid = st.st_uid;
+ p->gid = st.st_gid;
- return 0;
+ return 1;
}
int read_proc_pid_stat(struct pid_stat *p) {
- static procfile *ff = NULL;
-
- char filename[FILENAME_MAX + 1];
-
- snprintfz(filename, FILENAME_MAX, "%s/proc/%d/stat", host_prefix, p->pid);
-
- // ----------------------------------------
-
- int set_quotes = (!ff)?1:0;
-
- ff = procfile_reopen(ff, filename, NULL, PROCFILE_FLAG_NO_ERROR_ON_FILE_IO);
- if(!ff) return 1;
-
- // if(set_quotes) procfile_set_quotes(ff, "()");
- if(set_quotes) procfile_set_open_close(ff, "(", ")");
-
- ff = procfile_readall(ff);
- if(!ff) {
- // procfile_close(ff);
- return 1;
- }
-
- file_counter++;
-
- // parse the process name
- unsigned int i = 0;
- strncpyz(p->comm, procfile_lineword(ff, 0, 1), MAX_COMPARE_NAME);
-
- // p->pid = atol(procfile_lineword(ff, 0, 0+i));
- // comm is at 1
- // p->state = *(procfile_lineword(ff, 0, 2+i));
- p->ppid = (int32_t) atol(procfile_lineword(ff, 0, 3 + i));
- // p->pgrp = atol(procfile_lineword(ff, 0, 4+i));
- // p->session = atol(procfile_lineword(ff, 0, 5+i));
- // p->tty_nr = atol(procfile_lineword(ff, 0, 6+i));
- // p->tpgid = atol(procfile_lineword(ff, 0, 7+i));
- // p->flags = strtoull(procfile_lineword(ff, 0, 8+i), NULL, 10);
- p->minflt = strtoull(procfile_lineword(ff, 0, 9+i), NULL, 10);
- p->cminflt = strtoull(procfile_lineword(ff, 0, 10+i), NULL, 10);
- p->majflt = strtoull(procfile_lineword(ff, 0, 11+i), NULL, 10);
- p->cmajflt = strtoull(procfile_lineword(ff, 0, 12+i), NULL, 10);
- p->utime = strtoull(procfile_lineword(ff, 0, 13+i), NULL, 10);
- p->stime = strtoull(procfile_lineword(ff, 0, 14+i), NULL, 10);
- p->cutime = strtoull(procfile_lineword(ff, 0, 15+i), NULL, 10);
- p->cstime = strtoull(procfile_lineword(ff, 0, 16+i), NULL, 10);
- // p->priority = strtoull(procfile_lineword(ff, 0, 17+i), NULL, 10);
- // p->nice = strtoull(procfile_lineword(ff, 0, 18+i), NULL, 10);
- p->num_threads = (int32_t) atol(procfile_lineword(ff, 0, 19 + i));
- // p->itrealvalue = strtoull(procfile_lineword(ff, 0, 20+i), NULL, 10);
- // p->starttime = strtoull(procfile_lineword(ff, 0, 21+i), NULL, 10);
- // p->vsize = strtoull(procfile_lineword(ff, 0, 22+i), NULL, 10);
- p->rss = strtoull(procfile_lineword(ff, 0, 23+i), NULL, 10);
- // p->rsslim = strtoull(procfile_lineword(ff, 0, 24+i), NULL, 10);
- // p->starcode = strtoull(procfile_lineword(ff, 0, 25+i), NULL, 10);
- // p->endcode = strtoull(procfile_lineword(ff, 0, 26+i), NULL, 10);
- // p->startstack = strtoull(procfile_lineword(ff, 0, 27+i), NULL, 10);
- // p->kstkesp = strtoull(procfile_lineword(ff, 0, 28+i), NULL, 10);
- // p->kstkeip = strtoull(procfile_lineword(ff, 0, 29+i), NULL, 10);
- // p->signal = strtoull(procfile_lineword(ff, 0, 30+i), NULL, 10);
- // p->blocked = strtoull(procfile_lineword(ff, 0, 31+i), NULL, 10);
- // p->sigignore = strtoull(procfile_lineword(ff, 0, 32+i), NULL, 10);
- // p->sigcatch = strtoull(procfile_lineword(ff, 0, 33+i), NULL, 10);
- // p->wchan = strtoull(procfile_lineword(ff, 0, 34+i), NULL, 10);
- // p->nswap = strtoull(procfile_lineword(ff, 0, 35+i), NULL, 10);
- // p->cnswap = strtoull(procfile_lineword(ff, 0, 36+i), NULL, 10);
- // p->exit_signal = atol(procfile_lineword(ff, 0, 37+i));
- // p->processor = atol(procfile_lineword(ff, 0, 38+i));
- // p->rt_priority = strtoul(procfile_lineword(ff, 0, 39+i), NULL, 10);
- // p->policy = strtoul(procfile_lineword(ff, 0, 40+i), NULL, 10);
- // p->delayacct_blkio_ticks = strtoull(procfile_lineword(ff, 0, 41+i), NULL, 10);
- // p->guest_time = strtoull(procfile_lineword(ff, 0, 42+i), NULL, 10);
- // p->cguest_time = strtoull(procfile_lineword(ff, 0, 43), NULL, 10);
-
- if(debug || (p->target && p->target->debug))
- fprintf(stderr, "apps.plugin: READ PROC/PID/STAT: %s/proc/%d/stat, process: '%s' VALUES: utime=%llu, stime=%llu, cutime=%llu, cstime=%llu, minflt=%llu, majflt=%llu, cminflt=%llu, cmajflt=%llu, threads=%d\n", host_prefix, p->pid, p->comm, p->utime, p->stime, p->cutime, p->cstime, p->minflt, p->majflt, p->cminflt, p->cmajflt, p->num_threads);
-
- // procfile_close(ff);
- return 0;
+ static procfile *ff = NULL;
+
+ if(unlikely(!p->stat_filename)) {
+ char filename[FILENAME_MAX + 1];
+ snprintfz(filename, FILENAME_MAX, "%s/proc/%d/stat", host_prefix, p->pid);
+ p->stat_filename = strdupz(filename);
+ }
+
+ int set_quotes = (!ff)?1:0;
+
+ ff = procfile_reopen(ff, p->stat_filename, NULL, PROCFILE_FLAG_NO_ERROR_ON_FILE_IO);
+ if(unlikely(!ff)) goto cleanup;
+
+ // if(set_quotes) procfile_set_quotes(ff, "()");
+ if(set_quotes) procfile_set_open_close(ff, "(", ")");
+
+ ff = procfile_readall(ff);
+ if(unlikely(!ff)) goto cleanup;
+
+ p->last_stat_collected_usec = p->stat_collected_usec;
+ p->stat_collected_usec = time_usec();
+ file_counter++;
+
+ // p->pid = atol(procfile_lineword(ff, 0, 0+i));
+
+ strncpyz(p->comm, procfile_lineword(ff, 0, 1), MAX_COMPARE_NAME);
+
+ // p->state = *(procfile_lineword(ff, 0, 2));
+ p->ppid = (int32_t) atol(procfile_lineword(ff, 0, 3));
+ // p->pgrp = atol(procfile_lineword(ff, 0, 4));
+ // p->session = atol(procfile_lineword(ff, 0, 5));
+ // p->tty_nr = atol(procfile_lineword(ff, 0, 6));
+ // p->tpgid = atol(procfile_lineword(ff, 0, 7));
+ // p->flags = strtoull(procfile_lineword(ff, 0, 8), NULL, 10);
+
+ unsigned long long last;
+
+ last = p->minflt_raw;
+ p->minflt_raw = strtoull(procfile_lineword(ff, 0, 9), NULL, 10);
+ p->minflt = (p->minflt_raw - last) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
+
+ last = p->cminflt_raw;
+ p->cminflt_raw = strtoull(procfile_lineword(ff, 0, 10), NULL, 10);
+ p->cminflt = (p->cminflt_raw - last) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
+
+ last = p->majflt_raw;
+ p->majflt_raw = strtoull(procfile_lineword(ff, 0, 11), NULL, 10);
+ p->majflt = (p->majflt_raw - last) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
+
+ last = p->cmajflt_raw;
+ p->cmajflt_raw = strtoull(procfile_lineword(ff, 0, 12), NULL, 10);
+ p->cmajflt = (p->cmajflt_raw - last) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
+
+ last = p->utime_raw;
+ p->utime_raw = strtoull(procfile_lineword(ff, 0, 13), NULL, 10);
+ p->utime = (p->utime_raw - last) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
+
+ last = p->stime_raw;
+ p->stime_raw = strtoull(procfile_lineword(ff, 0, 14), NULL, 10);
+ p->stime = (p->stime_raw - last) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
+
+ last = p->cutime_raw;
+ p->cutime_raw = strtoull(procfile_lineword(ff, 0, 15), NULL, 10);
+ p->cutime = (p->cutime_raw - last) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
+
+ last = p->cstime_raw;
+ p->cstime_raw = strtoull(procfile_lineword(ff, 0, 16), NULL, 10);
+ p->cstime = (p->cstime_raw - last) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
+
+ // p->priority = strtoull(procfile_lineword(ff, 0, 17), NULL, 10);
+ // p->nice = strtoull(procfile_lineword(ff, 0, 18), NULL, 10);
+ p->num_threads = (int32_t) atol(procfile_lineword(ff, 0, 19));
+ // p->itrealvalue = strtoull(procfile_lineword(ff, 0, 20), NULL, 10);
+ // p->starttime = strtoull(procfile_lineword(ff, 0, 21), NULL, 10);
+ // p->vsize = strtoull(procfile_lineword(ff, 0, 22), NULL, 10);
+ p->rss = strtoull(procfile_lineword(ff, 0, 23), NULL, 10);
+ // p->rsslim = strtoull(procfile_lineword(ff, 0, 24), NULL, 10);
+ // p->starcode = strtoull(procfile_lineword(ff, 0, 25), NULL, 10);
+ // p->endcode = strtoull(procfile_lineword(ff, 0, 26), NULL, 10);
+ // p->startstack = strtoull(procfile_lineword(ff, 0, 27), NULL, 10);
+ // p->kstkesp = strtoull(procfile_lineword(ff, 0, 28), NULL, 10);
+ // p->kstkeip = strtoull(procfile_lineword(ff, 0, 29), NULL, 10);
+ // p->signal = strtoull(procfile_lineword(ff, 0, 30), NULL, 10);
+ // p->blocked = strtoull(procfile_lineword(ff, 0, 31), NULL, 10);
+ // p->sigignore = strtoull(procfile_lineword(ff, 0, 32), NULL, 10);
+ // p->sigcatch = strtoull(procfile_lineword(ff, 0, 33), NULL, 10);
+ // p->wchan = strtoull(procfile_lineword(ff, 0, 34), NULL, 10);
+ // p->nswap = strtoull(procfile_lineword(ff, 0, 35), NULL, 10);
+ // p->cnswap = strtoull(procfile_lineword(ff, 0, 36), NULL, 10);
+ // p->exit_signal = atol(procfile_lineword(ff, 0, 37));
+ // p->processor = atol(procfile_lineword(ff, 0, 38));
+ // p->rt_priority = strtoul(procfile_lineword(ff, 0, 39), NULL, 10);
+ // p->policy = strtoul(procfile_lineword(ff, 0, 40), NULL, 10);
+ // p->delayacct_blkio_ticks = strtoull(procfile_lineword(ff, 0, 41), NULL, 10);
+
+ if(enable_guest_charts) {
+ last = p->gtime_raw;
+ p->gtime_raw = strtoull(procfile_lineword(ff, 0, 42), NULL, 10);
+ p->gtime = (p->gtime_raw - last) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
+
+ last = p->cgtime_raw;
+ p->cgtime_raw = strtoull(procfile_lineword(ff, 0, 43), NULL, 10);
+ p->cgtime = (p->cgtime_raw - last) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
+
+ if (show_guest_time || p->gtime || p->cgtime) {
+ p->utime -= (p->utime >= p->gtime) ? p->gtime : p->utime;
+ p->cutime -= (p->cutime >= p->cgtime) ? p->cgtime : p->cutime;
+ show_guest_time = 1;
+ }
+ }
+
+ if(unlikely(debug || (p->target && p->target->debug)))
+ fprintf(stderr, "apps.plugin: READ PROC/PID/STAT: %s/proc/%d/stat, process: '%s' on target '%s' (dt=%llu) VALUES: utime=%llu, stime=%llu, cutime=%llu, cstime=%llu, minflt=%llu, majflt=%llu, cminflt=%llu, cmajflt=%llu, threads=%d\n", host_prefix, p->pid, p->comm, (p->target)?p->target->name:"UNSET", p->stat_collected_usec - p->last_stat_collected_usec, p->utime, p->stime, p->cutime, p->cstime, p->minflt, p->majflt, p->cminflt, p->cmajflt, p->num_threads);
+
+ if(unlikely(global_iterations_counter == 1)) {
+ p->minflt = 0;
+ p->cminflt = 0;
+ p->majflt = 0;
+ p->cmajflt = 0;
+ p->utime = 0;
+ p->stime = 0;
+ p->gtime = 0;
+ p->cutime = 0;
+ p->cstime = 0;
+ p->cgtime = 0;
+ }
+
+ return 1;
+
+cleanup:
+ p->minflt = 0;
+ p->cminflt = 0;
+ p->majflt = 0;
+ p->cmajflt = 0;
+ p->utime = 0;
+ p->stime = 0;
+ p->gtime = 0;
+ p->cutime = 0;
+ p->cstime = 0;
+ p->cgtime = 0;
+ p->num_threads = 0;
+ p->rss = 0;
+ return 0;
}
int read_proc_pid_statm(struct pid_stat *p) {
- static procfile *ff = NULL;
+ static procfile *ff = NULL;
+
+ if(unlikely(!p->statm_filename)) {
+ char filename[FILENAME_MAX + 1];
+ snprintfz(filename, FILENAME_MAX, "%s/proc/%d/statm", host_prefix, p->pid);
+ p->statm_filename = strdupz(filename);
+ }
+
+ ff = procfile_reopen(ff, p->statm_filename, NULL, PROCFILE_FLAG_NO_ERROR_ON_FILE_IO);
+ if(unlikely(!ff)) goto cleanup;
+
+ ff = procfile_readall(ff);
+ if(unlikely(!ff)) goto cleanup;
+
+ file_counter++;
+
+ p->statm_size = strtoull(procfile_lineword(ff, 0, 0), NULL, 10);
+ p->statm_resident = strtoull(procfile_lineword(ff, 0, 1), NULL, 10);
+ p->statm_share = strtoull(procfile_lineword(ff, 0, 2), NULL, 10);
+ p->statm_text = strtoull(procfile_lineword(ff, 0, 3), NULL, 10);
+ p->statm_lib = strtoull(procfile_lineword(ff, 0, 4), NULL, 10);
+ p->statm_data = strtoull(procfile_lineword(ff, 0, 5), NULL, 10);
+ p->statm_dirty = strtoull(procfile_lineword(ff, 0, 6), NULL, 10);
+
+ return 1;
+
+cleanup:
+ p->statm_size = 0;
+ p->statm_resident = 0;
+ p->statm_share = 0;
+ p->statm_text = 0;
+ p->statm_lib = 0;
+ p->statm_data = 0;
+ p->statm_dirty = 0;
+ return 0;
+}
- char filename[FILENAME_MAX + 1];
+int read_proc_pid_io(struct pid_stat *p) {
+ static procfile *ff = NULL;
+
+ if(unlikely(!p->io_filename)) {
+ char filename[FILENAME_MAX + 1];
+ snprintfz(filename, FILENAME_MAX, "%s/proc/%d/io", host_prefix, p->pid);
+ p->io_filename = strdupz(filename);
+ }
+
+ // open the file
+ ff = procfile_reopen(ff, p->io_filename, NULL, PROCFILE_FLAG_NO_ERROR_ON_FILE_IO);
+ if(unlikely(!ff)) goto cleanup;
+
+ ff = procfile_readall(ff);
+ if(unlikely(!ff)) goto cleanup;
+
+ file_counter++;
+
+ p->last_io_collected_usec = p->io_collected_usec;
+ p->io_collected_usec = time_usec();
+
+ unsigned long long last;
+
+ last = p->io_logical_bytes_read_raw;
+ p->io_logical_bytes_read_raw = strtoull(procfile_lineword(ff, 0, 1), NULL, 10);
+ p->io_logical_bytes_read = (p->io_logical_bytes_read_raw - last) * (1000000ULL * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec);
+
+ last = p->io_logical_bytes_written_raw;
+ p->io_logical_bytes_written_raw = strtoull(procfile_lineword(ff, 1, 1), NULL, 10);
+ p->io_logical_bytes_written = (p->io_logical_bytes_written_raw - last) * (1000000ULL * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec);
+
+ last = p->io_read_calls_raw;
+ p->io_read_calls_raw = strtoull(procfile_lineword(ff, 2, 1), NULL, 10);
+ p->io_read_calls = (p->io_read_calls_raw - last) * (1000000ULL * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec);
+
+ last = p->io_write_calls_raw;
+ p->io_write_calls_raw = strtoull(procfile_lineword(ff, 3, 1), NULL, 10);
+ p->io_write_calls = (p->io_write_calls_raw - last) * (1000000ULL * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec);
+
+ last = p->io_storage_bytes_read_raw;
+ p->io_storage_bytes_read_raw = strtoull(procfile_lineword(ff, 4, 1), NULL, 10);
+ p->io_storage_bytes_read = (p->io_storage_bytes_read_raw - last) * (1000000ULL * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec);
+
+ last = p->io_storage_bytes_written_raw;
+ p->io_storage_bytes_written_raw = strtoull(procfile_lineword(ff, 5, 1), NULL, 10);
+ p->io_storage_bytes_written = (p->io_storage_bytes_written_raw - last) * (1000000ULL * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec);
+
+ last = p->io_cancelled_write_bytes_raw;
+ p->io_cancelled_write_bytes_raw = strtoull(procfile_lineword(ff, 6, 1), NULL, 10);
+ p->io_cancelled_write_bytes = (p->io_cancelled_write_bytes_raw - last) * (1000000ULL * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec);
+
+ if(unlikely(global_iterations_counter == 1)) {
+ p->io_logical_bytes_read = 0;
+ p->io_logical_bytes_written = 0;
+ p->io_read_calls = 0;
+ p->io_write_calls = 0;
+ p->io_storage_bytes_read = 0;
+ p->io_storage_bytes_written = 0;
+ p->io_cancelled_write_bytes = 0;
+ }
+
+ return 1;
+
+cleanup:
+ p->io_logical_bytes_read = 0;
+ p->io_logical_bytes_written = 0;
+ p->io_read_calls = 0;
+ p->io_write_calls = 0;
+ p->io_storage_bytes_read = 0;
+ p->io_storage_bytes_written = 0;
+ p->io_cancelled_write_bytes = 0;
+ return 0;
+}
- snprintfz(filename, FILENAME_MAX, "%s/proc/%d/statm", host_prefix, p->pid);
+unsigned long long global_utime = 0;
+unsigned long long global_stime = 0;
+unsigned long long global_gtime = 0;
- ff = procfile_reopen(ff, filename, NULL, PROCFILE_FLAG_NO_ERROR_ON_FILE_IO);
- if(!ff) return 1;
+int read_proc_stat() {
+ static char filename[FILENAME_MAX + 1] = "";
+ static procfile *ff = NULL;
+ static unsigned long long utime_raw = 0, stime_raw = 0, gtime_raw = 0, gntime_raw = 0, ntime_raw = 0, collected_usec = 0, last_collected_usec = 0;
- ff = procfile_readall(ff);
- if(!ff) {
- // procfile_close(ff);
- return 1;
- }
+ if(unlikely(!ff)) {
+ snprintfz(filename, FILENAME_MAX, "%s/proc/stat", host_prefix);
+ ff = procfile_open(filename, " \t:", PROCFILE_FLAG_DEFAULT);
+ if(unlikely(!ff)) goto cleanup;
+ }
- file_counter++;
+ ff = procfile_readall(ff);
+ if(unlikely(!ff)) goto cleanup;
- p->statm_size = strtoull(procfile_lineword(ff, 0, 0), NULL, 10);
- p->statm_resident = strtoull(procfile_lineword(ff, 0, 1), NULL, 10);
- p->statm_share = strtoull(procfile_lineword(ff, 0, 2), NULL, 10);
- p->statm_text = strtoull(procfile_lineword(ff, 0, 3), NULL, 10);
- p->statm_lib = strtoull(procfile_lineword(ff, 0, 4), NULL, 10);
- p->statm_data = strtoull(procfile_lineword(ff, 0, 5), NULL, 10);
- p->statm_dirty = strtoull(procfile_lineword(ff, 0, 6), NULL, 10);
+ last_collected_usec = collected_usec;
+ collected_usec = time_usec();
- // procfile_close(ff);
- return 0;
-}
+ file_counter++;
-int read_proc_pid_io(struct pid_stat *p) {
- static procfile *ff = NULL;
+ unsigned long long last;
+
+ last = utime_raw;
+ utime_raw = strtoull(procfile_lineword(ff, 0, 1), NULL, 10);
+ global_utime = (utime_raw - last) * (1000000ULL * RATES_DETAIL) / (collected_usec - last_collected_usec);
+
+ // nice time, on user time
+ last = ntime_raw;
+ ntime_raw = strtoull(procfile_lineword(ff, 0, 2), NULL, 10);
+ global_utime += (ntime_raw - last) * (1000000ULL * RATES_DETAIL) / (collected_usec - last_collected_usec);
- char filename[FILENAME_MAX + 1];
+ last = stime_raw;
+ stime_raw = strtoull(procfile_lineword(ff, 0, 3), NULL, 10);
+ global_stime = (stime_raw - last) * (1000000ULL * RATES_DETAIL) / (collected_usec - last_collected_usec);
- snprintfz(filename, FILENAME_MAX, "%s/proc/%d/io", host_prefix, p->pid);
+ last = gtime_raw;
+ gtime_raw = strtoull(procfile_lineword(ff, 0, 10), NULL, 10);
+ global_gtime = (gtime_raw - last) * (1000000ULL * RATES_DETAIL) / (collected_usec - last_collected_usec);
- ff = procfile_reopen(ff, filename, NULL, PROCFILE_FLAG_NO_ERROR_ON_FILE_IO);
- if(!ff) return 1;
+ if(enable_guest_charts) {
+ // guest nice time, on guest time
+ last = gntime_raw;
+ gntime_raw = strtoull(procfile_lineword(ff, 0, 11), NULL, 10);
+ global_gtime += (gntime_raw - last) * (1000000ULL * RATES_DETAIL) / (collected_usec - last_collected_usec);
- ff = procfile_readall(ff);
- if(!ff) {
- // procfile_close(ff);
- return 1;
- }
+ // remove guest time from user time
+ global_utime -= (global_utime > global_gtime) ? global_gtime : global_utime;
+ }
- file_counter++;
+ if(unlikely(global_iterations_counter == 1)) {
+ global_utime = 0;
+ global_stime = 0;
+ global_gtime = 0;
+ }
- p->io_logical_bytes_read = strtoull(procfile_lineword(ff, 0, 1), NULL, 10);
- p->io_logical_bytes_written = strtoull(procfile_lineword(ff, 1, 1), NULL, 10);
- p->io_read_calls = strtoull(procfile_lineword(ff, 2, 1), NULL, 10);
- p->io_write_calls = strtoull(procfile_lineword(ff, 3, 1), NULL, 10);
- p->io_storage_bytes_read = strtoull(procfile_lineword(ff, 4, 1), NULL, 10);
- p->io_storage_bytes_written = strtoull(procfile_lineword(ff, 5, 1), NULL, 10);
- p->io_cancelled_write_bytes = strtoull(procfile_lineword(ff, 6, 1), NULL, 10);
+ return 1;
- // procfile_close(ff);
- return 0;
+cleanup:
+ global_utime = 0;
+ global_stime = 0;
+ global_gtime = 0;
+ return 0;
}
@@ -967,15 +958,15 @@ int read_proc_pid_io(struct pid_stat *p) {
#define FILE_DESCRIPTORS_INCREASE_STEP 100
struct file_descriptor {
- avl avl;
+ avl avl;
#ifdef NETDATA_INTERNAL_CHECKS
- uint32_t magic;
+ uint32_t magic;
#endif /* NETDATA_INTERNAL_CHECKS */
- uint32_t hash;
- const char *name;
- int type;
- int count;
- int pos;
+ uint32_t hash;
+ const char *name;
+ int type;
+ int count;
+ int pos;
} *all_files = NULL;
int all_files_len = 0;
@@ -983,38 +974,38 @@ int all_files_size = 0;
int file_descriptor_compare(void* a, void* b) {
#ifdef NETDATA_INTERNAL_CHECKS
- if(((struct file_descriptor *)a)->magic != 0x0BADCAFE || ((struct file_descriptor *)b)->magic != 0x0BADCAFE)
- error("Corrupted index data detected. Please report this.");
+ if(((struct file_descriptor *)a)->magic != 0x0BADCAFE || ((struct file_descriptor *)b)->magic != 0x0BADCAFE)
+ error("Corrupted index data detected. Please report this.");
#endif /* NETDATA_INTERNAL_CHECKS */
- if(((struct file_descriptor *)a)->hash < ((struct file_descriptor *)b)->hash)
- return -1;
+ if(((struct file_descriptor *)a)->hash < ((struct file_descriptor *)b)->hash)
+ return -1;
- else if(((struct file_descriptor *)a)->hash > ((struct file_descriptor *)b)->hash)
- return 1;
+ else if(((struct file_descriptor *)a)->hash > ((struct file_descriptor *)b)->hash)
+ return 1;
- else
- return strcmp(((struct file_descriptor *)a)->name, ((struct file_descriptor *)b)->name);
+ else
+ return strcmp(((struct file_descriptor *)a)->name, ((struct file_descriptor *)b)->name);
}
int file_descriptor_iterator(avl *a) { if(a) {}; return 0; }
avl_tree all_files_index = {
- NULL,
- file_descriptor_compare
+ NULL,
+ file_descriptor_compare
};
static struct file_descriptor *file_descriptor_find(const char *name, uint32_t hash) {
- struct file_descriptor tmp;
- tmp.hash = (hash)?hash:simple_hash(name);
- tmp.name = name;
- tmp.count = 0;
- tmp.pos = 0;
+ struct file_descriptor tmp;
+ tmp.hash = (hash)?hash:simple_hash(name);
+ tmp.name = name;
+ tmp.count = 0;
+ tmp.pos = 0;
#ifdef NETDATA_INTERNAL_CHECKS
- tmp.magic = 0x0BADCAFE;
+ tmp.magic = 0x0BADCAFE;
#endif /* NETDATA_INTERNAL_CHECKS */
- return (struct file_descriptor *)avl_search(&all_files_index, (avl *) &tmp);
+ return (struct file_descriptor *)avl_search(&all_files_index, (avl *) &tmp);
}
#define file_descriptor_add(fd) avl_insert(&all_files_index, (avl *)(fd))
@@ -1032,223 +1023,526 @@ static struct file_descriptor *file_descriptor_find(const char *name, uint32_t h
void file_descriptor_not_used(int id)
{
- if(id > 0 && id < all_files_size) {
+ if(id > 0 && id < all_files_size) {
#ifdef NETDATA_INTERNAL_CHECKS
- if(all_files[id].magic != 0x0BADCAFE) {
- error("Ignoring request to remove empty file id %d.", id);
- return;
- }
+ if(all_files[id].magic != 0x0BADCAFE) {
+ error("Ignoring request to remove empty file id %d.", id);
+ return;
+ }
#endif /* NETDATA_INTERNAL_CHECKS */
- if(debug) fprintf(stderr, "apps.plugin: decreasing slot %d (count = %d).\n", id, all_files[id].count);
+ if(unlikely(debug))
+ fprintf(stderr, "apps.plugin: decreasing slot %d (count = %d).\n", id, all_files[id].count);
- if(all_files[id].count > 0) {
- all_files[id].count--;
+ if(all_files[id].count > 0) {
+ all_files[id].count--;
- if(!all_files[id].count) {
- if(debug) fprintf(stderr, "apps.plugin: >> slot %d is empty.\n", id);
- file_descriptor_remove(&all_files[id]);
+ if(!all_files[id].count) {
+ if(unlikely(debug))
+ fprintf(stderr, "apps.plugin: >> slot %d is empty.\n", id);
+
+ file_descriptor_remove(&all_files[id]);
#ifdef NETDATA_INTERNAL_CHECKS
- all_files[id].magic = 0x00000000;
+ all_files[id].magic = 0x00000000;
#endif /* NETDATA_INTERNAL_CHECKS */
- all_files_len--;
- }
- }
- else
- error("Request to decrease counter of fd %d (%s), while the use counter is 0", id, all_files[id].name);
- }
- else error("Request to decrease counter of fd %d, which is outside the array size (1 to %d)", id, all_files_size);
+ all_files_len--;
+ }
+ }
+ else
+ error("Request to decrease counter of fd %d (%s), while the use counter is 0", id, all_files[id].name);
+ }
+ else error("Request to decrease counter of fd %d, which is outside the array size (1 to %d)", id, all_files_size);
}
int file_descriptor_find_or_add(const char *name)
{
- static int last_pos = 0;
- uint32_t hash = simple_hash(name);
-
- if(debug) fprintf(stderr, "apps.plugin: adding or finding name '%s' with hash %u\n", name, hash);
-
- struct file_descriptor *fd = file_descriptor_find(name, hash);
- if(fd) {
- // found
- if(debug) fprintf(stderr, "apps.plugin: >> found on slot %d\n", fd->pos);
- fd->count++;
- return fd->pos;
- }
- // not found
-
- // check we have enough memory to add it
- if(!all_files || all_files_len == all_files_size) {
- void *old = all_files;
- int i;
-
- // there is no empty slot
- if(debug) fprintf(stderr, "apps.plugin: extending fd array to %d entries\n", all_files_size + FILE_DESCRIPTORS_INCREASE_STEP);
- all_files = realloc(all_files, (all_files_size + FILE_DESCRIPTORS_INCREASE_STEP) * sizeof(struct file_descriptor));
-
- // if the address changed, we have to rebuild the index
- // since all pointers are now invalid
- if(old && old != (void *)all_files) {
- if(debug) fprintf(stderr, "apps.plugin: >> re-indexing.\n");
- all_files_index.root = NULL;
- for(i = 0; i < all_files_size; i++) {
- if(!all_files[i].count) continue;
- file_descriptor_add(&all_files[i]);
- }
- if(debug) fprintf(stderr, "apps.plugin: >> re-indexing done.\n");
- }
-
- for(i = all_files_size; i < (all_files_size + FILE_DESCRIPTORS_INCREASE_STEP); i++) {
- all_files[i].count = 0;
- all_files[i].name = NULL;
+ static int last_pos = 0;
+ uint32_t hash = simple_hash(name);
+
+ if(unlikely(debug))
+ fprintf(stderr, "apps.plugin: adding or finding name '%s' with hash %u\n", name, hash);
+
+ struct file_descriptor *fd = file_descriptor_find(name, hash);
+ if(fd) {
+ // found
+ if(unlikely(debug))
+ fprintf(stderr, "apps.plugin: >> found on slot %d\n", fd->pos);
+
+ fd->count++;
+ return fd->pos;
+ }
+ // not found
+
+ // check we have enough memory to add it
+ if(!all_files || all_files_len == all_files_size) {
+ void *old = all_files;
+ int i;
+
+ // there is no empty slot
+ if(unlikely(debug))
+ fprintf(stderr, "apps.plugin: extending fd array to %d entries\n", all_files_size + FILE_DESCRIPTORS_INCREASE_STEP);
+
+ all_files = reallocz(all_files, (all_files_size + FILE_DESCRIPTORS_INCREASE_STEP) * sizeof(struct file_descriptor));
+
+ // if the address changed, we have to rebuild the index
+ // since all pointers are now invalid
+ if(old && old != (void *)all_files) {
+ if(unlikely(debug))
+ fprintf(stderr, "apps.plugin: >> re-indexing.\n");
+
+ all_files_index.root = NULL;
+ for(i = 0; i < all_files_size; i++) {
+ if(!all_files[i].count) continue;
+ file_descriptor_add(&all_files[i]);
+ }
+
+ if(unlikely(debug))
+ fprintf(stderr, "apps.plugin: >> re-indexing done.\n");
+ }
+
+ for(i = all_files_size; i < (all_files_size + FILE_DESCRIPTORS_INCREASE_STEP); i++) {
+ all_files[i].count = 0;
+ all_files[i].name = NULL;
#ifdef NETDATA_INTERNAL_CHECKS
- all_files[i].magic = 0x00000000;
+ all_files[i].magic = 0x00000000;
#endif /* NETDATA_INTERNAL_CHECKS */
- all_files[i].pos = i;
- }
+ all_files[i].pos = i;
+ }
- if(!all_files_size) all_files_len = 1;
- all_files_size += FILE_DESCRIPTORS_INCREASE_STEP;
- }
+ if(!all_files_size) all_files_len = 1;
+ all_files_size += FILE_DESCRIPTORS_INCREASE_STEP;
+ }
- if(debug) fprintf(stderr, "apps.plugin: >> searching for empty slot.\n");
+ if(unlikely(debug))
+ fprintf(stderr, "apps.plugin: >> searching for empty slot.\n");
- // search for an empty slot
- int i, c;
- for(i = 0, c = last_pos ; i < all_files_size ; i++, c++) {
- if(c >= all_files_size) c = 0;
- if(c == 0) continue;
+ // search for an empty slot
+ int i, c;
+ for(i = 0, c = last_pos ; i < all_files_size ; i++, c++) {
+ if(c >= all_files_size) c = 0;
+ if(c == 0) continue;
- if(!all_files[c].count) {
- if(debug) fprintf(stderr, "apps.plugin: >> Examining slot %d.\n", c);
+ if(!all_files[c].count) {
+ if(unlikely(debug))
+ fprintf(stderr, "apps.plugin: >> Examining slot %d.\n", c);
#ifdef NETDATA_INTERNAL_CHECKS
- if(all_files[c].magic == 0x0BADCAFE && all_files[c].name && file_descriptor_find(all_files[c].name, all_files[c].hash))
- error("fd on position %d is not cleared properly. It still has %s in it.\n", c, all_files[c].name);
+ if(all_files[c].magic == 0x0BADCAFE && all_files[c].name && file_descriptor_find(all_files[c].name, all_files[c].hash))
+ error("fd on position %d is not cleared properly. It still has %s in it.\n", c, all_files[c].name);
#endif /* NETDATA_INTERNAL_CHECKS */
- if(debug) fprintf(stderr, "apps.plugin: >> %s fd position %d for %s (last name: %s)\n", all_files[c].name?"re-using":"using", c, name, all_files[c].name);
- if(all_files[c].name) free((void *)all_files[c].name);
- all_files[c].name = NULL;
- last_pos = c;
- break;
- }
- }
- if(i == all_files_size) {
- fatal("We should find an empty slot, but there isn't any");
- exit(1);
- }
- if(debug) fprintf(stderr, "apps.plugin: >> updating slot %d.\n", c);
-
- all_files_len++;
-
- // else we have an empty slot in 'c'
-
- int type;
- if(name[0] == '/') type = FILETYPE_FILE;
- else if(strncmp(name, "pipe:", 5) == 0) type = FILETYPE_PIPE;
- else if(strncmp(name, "socket:", 7) == 0) type = FILETYPE_SOCKET;
- else if(strcmp(name, "anon_inode:inotify") == 0 || strcmp(name, "inotify") == 0) type = FILETYPE_INOTIFY;
- else if(strcmp(name, "anon_inode:[eventfd]") == 0) type = FILETYPE_EVENTFD;
- else if(strcmp(name, "anon_inode:[eventpoll]") == 0) type = FILETYPE_EVENTPOLL;
- else if(strcmp(name, "anon_inode:[timerfd]") == 0) type = FILETYPE_TIMERFD;
- else if(strcmp(name, "anon_inode:[signalfd]") == 0) type = FILETYPE_SIGNALFD;
- else if(strncmp(name, "anon_inode:", 11) == 0) {
- if(debug) fprintf(stderr, "apps.plugin: FIXME: unknown anonymous inode: %s\n", name);
- type = FILETYPE_OTHER;
- }
- else {
- if(debug) fprintf(stderr, "apps.plugin: FIXME: cannot understand linkname: %s\n", name);
- type = FILETYPE_OTHER;
- }
-
- all_files[c].name = strdup(name);
- all_files[c].hash = hash;
- all_files[c].type = type;
- all_files[c].pos = c;
- all_files[c].count = 1;
+ if(unlikely(debug))
+ fprintf(stderr, "apps.plugin: >> %s fd position %d for %s (last name: %s)\n", all_files[c].name?"re-using":"using", c, name, all_files[c].name);
+
+ if(all_files[c].name) freez((void *)all_files[c].name);
+ all_files[c].name = NULL;
+ last_pos = c;
+ break;
+ }
+ }
+ if(i == all_files_size) {
+ fatal("We should find an empty slot, but there isn't any");
+ exit(1);
+ }
+
+ if(unlikely(debug))
+ fprintf(stderr, "apps.plugin: >> updating slot %d.\n", c);
+
+ all_files_len++;
+
+ // else we have an empty slot in 'c'
+
+ int type;
+ if(name[0] == '/') type = FILETYPE_FILE;
+ else if(strncmp(name, "pipe:", 5) == 0) type = FILETYPE_PIPE;
+ else if(strncmp(name, "socket:", 7) == 0) type = FILETYPE_SOCKET;
+ else if(strcmp(name, "anon_inode:inotify") == 0 || strcmp(name, "inotify") == 0) type = FILETYPE_INOTIFY;
+ else if(strcmp(name, "anon_inode:[eventfd]") == 0) type = FILETYPE_EVENTFD;
+ else if(strcmp(name, "anon_inode:[eventpoll]") == 0) type = FILETYPE_EVENTPOLL;
+ else if(strcmp(name, "anon_inode:[timerfd]") == 0) type = FILETYPE_TIMERFD;
+ else if(strcmp(name, "anon_inode:[signalfd]") == 0) type = FILETYPE_SIGNALFD;
+ else if(strncmp(name, "anon_inode:", 11) == 0) {
+ if(unlikely(debug))
+ fprintf(stderr, "apps.plugin: FIXME: unknown anonymous inode: %s\n", name);
+
+ type = FILETYPE_OTHER;
+ }
+ else {
+ if(unlikely(debug))
+ fprintf(stderr, "apps.plugin: FIXME: cannot understand linkname: %s\n", name);
+
+ type = FILETYPE_OTHER;
+ }
+
+ all_files[c].name = strdupz(name);
+ all_files[c].hash = hash;
+ all_files[c].type = type;
+ all_files[c].pos = c;
+ all_files[c].count = 1;
#ifdef NETDATA_INTERNAL_CHECKS
- all_files[c].magic = 0x0BADCAFE;
+ all_files[c].magic = 0x0BADCAFE;
#endif /* NETDATA_INTERNAL_CHECKS */
- file_descriptor_add(&all_files[c]);
+ file_descriptor_add(&all_files[c]);
- if(debug) fprintf(stderr, "apps.plugin: using fd position %d (name: %s)\n", c, all_files[c].name);
+ if(unlikely(debug))
+ fprintf(stderr, "apps.plugin: using fd position %d (name: %s)\n", c, all_files[c].name);
- return c;
+ return c;
}
int read_pid_file_descriptors(struct pid_stat *p) {
- char dirname[FILENAME_MAX+1];
-
- snprintfz(dirname, FILENAME_MAX, "%s/proc/%d/fd", host_prefix, p->pid);
- DIR *fds = opendir(dirname);
- if(fds) {
- int c;
- struct dirent *de;
- char fdname[FILENAME_MAX + 1];
- char linkname[FILENAME_MAX + 1];
-
- // make the array negative
- for(c = 0 ; c < p->fds_size ; c++)
- p->fds[c] = -p->fds[c];
-
- while((de = readdir(fds))) {
- if(strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
- continue;
-
- // check if the fds array is small
- int fdid = atoi(de->d_name);
- if(fdid < 0) continue;
- if(fdid >= p->fds_size) {
- // it is small, extend it
- if(debug) fprintf(stderr, "apps.plugin: extending fd memory slots for %s from %d to %d\n", p->comm, p->fds_size, fdid + 100);
- p->fds = realloc(p->fds, (fdid + 100) * sizeof(int));
- if(!p->fds) {
- fatal("Cannot re-allocate fds for %s", p->comm);
- break;
- }
-
- // and initialize it
- for(c = p->fds_size ; c < (fdid + 100) ; c++) p->fds[c] = 0;
- p->fds_size = fdid + 100;
- }
-
- if(p->fds[fdid] == 0) {
- // we don't know this fd, get it
-
- sprintf(fdname, "%s/proc/%d/fd/%s", host_prefix, p->pid, de->d_name);
- ssize_t l = readlink(fdname, linkname, FILENAME_MAX);
- if(l == -1) {
- if(debug || (p->target && p->target->debug)) {
- if(debug || (p->target && p->target->debug))
- error("Cannot read link %s", fdname);
- }
- continue;
- }
- linkname[l] = '\0';
- file_counter++;
-
- // if another process already has this, we will get
- // the same id
- p->fds[fdid] = file_descriptor_find_or_add(linkname);
- }
-
- // else make it positive again, we need it
- // of course, the actual file may have changed, but we don't care so much
- // FIXME: we could compare the inode as returned by readdir direct structure
- else p->fds[fdid] = -p->fds[fdid];
- }
- closedir(fds);
-
- // remove all the negative file descriptors
- for(c = 0 ; c < p->fds_size ; c++) if(p->fds[c] < 0) {
- file_descriptor_not_used(-p->fds[c]);
- p->fds[c] = 0;
- }
- }
- else return 1;
-
- return 0;
+ char dirname[FILENAME_MAX+1];
+
+ snprintfz(dirname, FILENAME_MAX, "%s/proc/%d/fd", host_prefix, p->pid);
+ DIR *fds = opendir(dirname);
+ if(fds) {
+ int c;
+ struct dirent *de;
+ char fdname[FILENAME_MAX + 1];
+ char linkname[FILENAME_MAX + 1];
+
+ // make the array negative
+ for(c = 0 ; c < p->fds_size ; c++)
+ p->fds[c] = -p->fds[c];
+
+ while((de = readdir(fds))) {
+ if(strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
+ continue;
+
+ // check if the fds array is small
+ int fdid = atoi(de->d_name);
+ if(fdid < 0) continue;
+ if(fdid >= p->fds_size) {
+ // it is small, extend it
+ if(unlikely(debug))
+ fprintf(stderr, "apps.plugin: extending fd memory slots for %s from %d to %d\n", p->comm, p->fds_size, fdid + 100);
+
+ p->fds = reallocz(p->fds, (fdid + 100) * sizeof(int));
+ if(!p->fds) {
+ fatal("Cannot re-allocate fds for %s", p->comm);
+ break;
+ }
+
+ // and initialize it
+ for(c = p->fds_size ; c < (fdid + 100) ; c++) p->fds[c] = 0;
+ p->fds_size = fdid + 100;
+ }
+
+ if(p->fds[fdid] == 0) {
+ // we don't know this fd, get it
+
+ sprintf(fdname, "%s/proc/%d/fd/%s", host_prefix, p->pid, de->d_name);
+ ssize_t l = readlink(fdname, linkname, FILENAME_MAX);
+ if(l == -1) {
+ if(debug || (p->target && p->target->debug)) {
+ if(debug || (p->target && p->target->debug))
+ error("Cannot read link %s", fdname);
+ }
+ continue;
+ }
+ linkname[l] = '\0';
+ file_counter++;
+
+ // if another process already has this, we will get
+ // the same id
+ p->fds[fdid] = file_descriptor_find_or_add(linkname);
+ }
+
+ // else make it positive again, we need it
+ // of course, the actual file may have changed, but we don't care so much
+ // FIXME: we could compare the inode as returned by readdir dirent structure
+ else p->fds[fdid] = -p->fds[fdid];
+ }
+ closedir(fds);
+
+ // remove all the negative file descriptors
+ for(c = 0 ; c < p->fds_size ; c++) if(p->fds[c] < 0) {
+ file_descriptor_not_used(-p->fds[c]);
+ p->fds[c] = 0;
+ }
+ }
+ else return 0;
+
+ return 1;
+}
+
+// ----------------------------------------------------------------------------
+
+int print_process_and_parents(struct pid_stat *p, unsigned long long time) {
+ char *prefix = "\\_ ";
+ int indent = 0;
+
+ if(p->parent)
+ indent = print_process_and_parents(p->parent, p->stat_collected_usec);
+ else
+ prefix = " > ";
+
+ char buffer[indent + 1];
+ int i;
+
+ for(i = 0; i < indent ;i++) buffer[i] = ' ';
+ buffer[i] = '\0';
+
+ fprintf(stderr, " %s %s%s (%d %s %lld"
+ , buffer
+ , prefix
+ , p->comm
+ , p->pid
+ , p->updated?"running":"exited"
+ , (long long)p->stat_collected_usec - (long long)time
+ );
+
+ if(p->utime) fprintf(stderr, " utime=%llu", p->utime);
+ if(p->stime) fprintf(stderr, " stime=%llu", p->stime);
+ if(p->gtime) fprintf(stderr, " gtime=%llu", p->gtime);
+ if(p->cutime) fprintf(stderr, " cutime=%llu", p->cutime);
+ if(p->cstime) fprintf(stderr, " cstime=%llu", p->cstime);
+ if(p->cgtime) fprintf(stderr, " cgtime=%llu", p->cgtime);
+ if(p->minflt) fprintf(stderr, " minflt=%llu", p->minflt);
+ if(p->cminflt) fprintf(stderr, " cminflt=%llu", p->cminflt);
+ if(p->majflt) fprintf(stderr, " majflt=%llu", p->majflt);
+ if(p->cmajflt) fprintf(stderr, " cmajflt=%llu", p->cmajflt);
+ fprintf(stderr, ")\n");
+
+ return indent + 1;
+}
+
+void print_process_tree(struct pid_stat *p, char *msg) {
+ log_date(stderr);
+ fprintf(stderr, "%s: process %s (%d, %s) with parents:\n", msg, p->comm, p->pid, p->updated?"running":"exited");
+ print_process_and_parents(p, p->stat_collected_usec);
+}
+
+void find_lost_child_debug(struct pid_stat *pe, unsigned long long lost, int type) {
+ int found = 0;
+ struct pid_stat *p = NULL;
+
+ for(p = root_of_pids; p ; p = p->next) {
+ if(p == pe) continue;
+
+ switch(type) {
+ case 1:
+ if(p->cminflt > lost) {
+ fprintf(stderr, " > process %d (%s) could use the lost exited child minflt %llu of process %d (%s)\n", p->pid, p->comm, lost, pe->pid, pe->comm);
+ found++;
+ }
+ break;
+
+ case 2:
+ if(p->cmajflt > lost) {
+ fprintf(stderr, " > process %d (%s) could use the lost exited child majflt %llu of process %d (%s)\n", p->pid, p->comm, lost, pe->pid, pe->comm);
+ found++;
+ }
+ break;
+
+ case 3:
+ if(p->cutime > lost) {
+ fprintf(stderr, " > process %d (%s) could use the lost exited child utime %llu of process %d (%s)\n", p->pid, p->comm, lost, pe->pid, pe->comm);
+ found++;
+ }
+ break;
+
+ case 4:
+ if(p->cstime > lost) {
+ fprintf(stderr, " > process %d (%s) could use the lost exited child stime %llu of process %d (%s)\n", p->pid, p->comm, lost, pe->pid, pe->comm);
+ found++;
+ }
+ break;
+
+ case 5:
+ if(p->cgtime > lost) {
+ fprintf(stderr, " > process %d (%s) could use the lost exited child gtime %llu of process %d (%s)\n", p->pid, p->comm, lost, pe->pid, pe->comm);
+ found++;
+ }
+ break;
+ }
+ }
+
+ if(!found) {
+ switch(type) {
+ case 1:
+ fprintf(stderr, " > cannot find any process to use the lost exited child minflt %llu of process %d (%s)\n", lost, pe->pid, pe->comm);
+ break;
+
+ case 2:
+ fprintf(stderr, " > cannot find any process to use the lost exited child majflt %llu of process %d (%s)\n", lost, pe->pid, pe->comm);
+ break;
+
+ case 3:
+ fprintf(stderr, " > cannot find any process to use the lost exited child utime %llu of process %d (%s)\n", lost, pe->pid, pe->comm);
+ break;
+
+ case 4:
+ fprintf(stderr, " > cannot find any process to use the lost exited child stime %llu of process %d (%s)\n", lost, pe->pid, pe->comm);
+ break;
+
+ case 5:
+ fprintf(stderr, " > cannot find any process to use the lost exited child gtime %llu of process %d (%s)\n", lost, pe->pid, pe->comm);
+ break;
+ }
+ }
+}
+
+unsigned long long remove_exited_child_from_parent(unsigned long long *field, unsigned long long *pfield) {
+ unsigned long long absorbed = 0;
+
+ if(*field > *pfield) {
+ absorbed += *pfield;
+ *field -= *pfield;
+ *pfield = 0;
+ }
+ else {
+ absorbed += *field;
+ *pfield -= *field;
+ *field = 0;
+ }
+
+ return absorbed;
+}
+
+void process_exited_processes() {
+ struct pid_stat *p;
+
+ for(p = root_of_pids; p ; p = p->next) {
+ if(p->updated || !p->stat_collected_usec)
+ continue;
+
+ struct pid_stat *pp = p->parent;
+
+ unsigned long long utime = (p->utime_raw + p->cutime_raw) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
+ unsigned long long stime = (p->stime_raw + p->cstime_raw) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
+ unsigned long long gtime = (p->gtime_raw + p->cgtime_raw) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
+ unsigned long long minflt = (p->minflt_raw + p->cminflt_raw) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
+ unsigned long long majflt = (p->majflt_raw + p->cmajflt_raw) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
+
+ if(utime + stime + gtime + minflt + majflt == 0)
+ continue;
+
+ if(unlikely(debug)) {
+ log_date(stderr);
+ fprintf(stderr, "Absorb %s (%d %s total resources: utime=%llu stime=%llu gtime=%llu minflt=%llu majflt=%llu)\n"
+ , p->comm
+ , p->pid
+ , p->updated?"running":"exited"
+ , utime
+ , stime
+ , gtime
+ , minflt
+ , majflt
+ );
+ print_process_tree(p, "Searching parents");
+ }
+
+ for(pp = p->parent; pp ; pp = pp->parent) {
+ if(!pp->updated) continue;
+
+ unsigned long long absorbed;
+ absorbed = remove_exited_child_from_parent(&utime, &pp->cutime);
+ if(unlikely(debug && absorbed))
+ fprintf(stderr, " > process %s (%d %s) absorbed %llu utime (remaining: %llu)\n", pp->comm, pp->pid, pp->updated?"running":"exited", absorbed, utime);
+
+ absorbed = remove_exited_child_from_parent(&stime, &pp->cstime);
+ if(unlikely(debug && absorbed))
+ fprintf(stderr, " > process %s (%d %s) absorbed %llu stime (remaining: %llu)\n", pp->comm, pp->pid, pp->updated?"running":"exited", absorbed, stime);
+
+ absorbed = remove_exited_child_from_parent(&gtime, &pp->cgtime);
+ if(unlikely(debug && absorbed))
+ fprintf(stderr, " > process %s (%d %s) absorbed %llu gtime (remaining: %llu)\n", pp->comm, pp->pid, pp->updated?"running":"exited", absorbed, gtime);
+
+ absorbed = remove_exited_child_from_parent(&minflt, &pp->cminflt);
+ if(unlikely(debug && absorbed))
+ fprintf(stderr, " > process %s (%d %s) absorbed %llu minflt (remaining: %llu)\n", pp->comm, pp->pid, pp->updated?"running":"exited", absorbed, minflt);
+
+ absorbed = remove_exited_child_from_parent(&majflt, &pp->cmajflt);
+ if(unlikely(debug && absorbed))
+ fprintf(stderr, " > process %s (%d %s) absorbed %llu majflt (remaining: %llu)\n", pp->comm, pp->pid, pp->updated?"running":"exited", absorbed, majflt);
+ }
+
+ if(unlikely(utime + stime + gtime + minflt + majflt > 0)) {
+ if(unlikely(debug)) {
+ if(utime) find_lost_child_debug(p, utime, 3);
+ if(stime) find_lost_child_debug(p, stime, 4);
+ if(gtime) find_lost_child_debug(p, gtime, 5);
+ if(minflt) find_lost_child_debug(p, minflt, 1);
+ if(majflt) find_lost_child_debug(p, majflt, 2);
+ }
+
+ p->keep = 1;
+
+ if(unlikely(debug))
+ fprintf(stderr, " > remaining resources - KEEP - for another loop: %s (%d %s total resources: utime=%llu stime=%llu gtime=%llu minflt=%llu majflt=%llu)\n"
+ , p->comm
+ , p->pid
+ , p->updated?"running":"exited"
+ , utime
+ , stime
+ , gtime
+ , minflt
+ , majflt
+ );
+
+ for(pp = p->parent; pp ; pp = pp->parent) {
+ if(pp->updated) break;
+ pp->keep = 1;
+
+ if(unlikely(debug))
+ fprintf(stderr, " > - KEEP - parent for another loop: %s (%d %s)\n"
+ , pp->comm
+ , pp->pid
+ , pp->updated?"running":"exited"
+ );
+ }
+
+ p->utime_raw = utime * (p->stat_collected_usec - p->last_stat_collected_usec) / (1000000ULL * RATES_DETAIL);
+ p->stime_raw = stime * (p->stat_collected_usec - p->last_stat_collected_usec) / (1000000ULL * RATES_DETAIL);
+ p->gtime_raw = gtime * (p->stat_collected_usec - p->last_stat_collected_usec) / (1000000ULL * RATES_DETAIL);
+ p->minflt_raw = minflt * (p->stat_collected_usec - p->last_stat_collected_usec) / (1000000ULL * RATES_DETAIL);
+ p->majflt_raw = majflt * (p->stat_collected_usec - p->last_stat_collected_usec) / (1000000ULL * RATES_DETAIL);
+ p->cutime_raw = p->cstime_raw = p->cgtime_raw = p->cminflt_raw = p->cmajflt_raw = 0;
+
+ if(unlikely(debug))
+ fprintf(stderr, "\n");
+ }
+ else if(unlikely(debug)) {
+ fprintf(stderr, " > totally absorbed - DONE - %s (%d %s)\n"
+ , p->comm
+ , p->pid
+ , p->updated?"running":"exited"
+ );
+ }
+ }
+}
+
+void link_all_processes_to_their_parents(void) {
+ struct pid_stat *p, *pp;
+
+ // link all children to their parents
+ // and update children count on parents
+ for(p = root_of_pids; p ; p = p->next) {
+ // for each process found
+
+ p->sortlist = 0;
+ p->parent = NULL;
+
+ if(unlikely(!p->ppid)) {
+ p->parent = NULL;
+ continue;
+ }
+
+ pp = all_pids[p->ppid];
+ if(likely(pp)) {
+ p->parent = pp;
+ pp->children_count++;
+
+ if(unlikely(debug || (p->target && p->target->debug)))
+ fprintf(stderr, "apps.plugin: \tchild %d (%s, %s) on target '%s' has parent %d (%s, %s). Parent: utime=%llu, stime=%llu, gtime=%llu, minflt=%llu, majflt=%llu, cutime=%llu, cstime=%llu, cgtime=%llu, cminflt=%llu, cmajflt=%llu\n", p->pid, p->comm, p->updated?"running":"exited", (p->target)?p->target->name:"UNSET", pp->pid, pp->comm, pp->updated?"running":"exited", pp->utime, pp->stime, pp->gtime, pp->minflt, pp->majflt, pp->cutime, pp->cstime, pp->cgtime, pp->cminflt, pp->cmajflt);
+ }
+ else {
+ p->parent = NULL;
+ error("pid %d %s states parent %d, but the later does not exist.", p->pid, p->comm, p->ppid);
+ }
+ }
}
// ----------------------------------------------------------------------------
@@ -1269,206 +1563,224 @@ int read_pid_file_descriptors(struct pid_stat *p) {
// to avoid filling up all disk space
// if debug is enabled, all errors are printed
-int collect_data_for_all_processes_from_proc(void)
-{
- char dirname[FILENAME_MAX + 1];
+static int compar_pid(const void *pid1, const void *pid2) {
+
+ struct pid_stat *p1 = all_pids[*((pid_t *)pid1)];
+ struct pid_stat *p2 = all_pids[*((pid_t *)pid2)];
- snprintfz(dirname, FILENAME_MAX, "%s/proc", host_prefix);
- DIR *dir = opendir(dirname);
- if(!dir) return 0;
+ if(p1->sortlist > p2->sortlist)
+ return -1;
+ else
+ return 1;
+}
- struct dirent *file = NULL;
- struct pid_stat *p = NULL;
+static inline int managed_log(struct pid_stat *p, uint32_t log, int status) {
+ if(unlikely(!status)) {
+ // error("command failed log %u, errno %d", log, errno);
+
+ if(unlikely(debug || errno != ENOENT)) {
+ if(unlikely(debug || !(p->log_thrown & log))) {
+ p->log_thrown |= log;
+ switch(log) {
+ case PID_LOG_IO:
+ error("Cannot process %s/proc/%d/io (command '%s')", host_prefix, p->pid, p->comm);
+ break;
+
+ case PID_LOG_STATM:
+ error("Cannot process %s/proc/%d/statm (command '%s')", host_prefix, p->pid, p->comm);
+ break;
+
+ case PID_LOG_CMDLINE:
+ error("Cannot process %s/proc/%d/cmdline (command '%s')", host_prefix, p->pid, p->comm);
+ break;
+
+ case PID_LOG_FDS:
+ error("Cannot process entries in %s/proc/%d/fd (command '%s')", host_prefix, p->pid, p->comm);
+ break;
+
+ case PID_LOG_STAT:
+ break;
+
+ default:
+ error("unhandled error for pid %d, command '%s'", p->pid, p->comm);
+ break;
+ }
+ }
+ }
+ errno = 0;
+ }
+ else if(unlikely(p->log_thrown & log)) {
+ // error("unsetting log %u on pid %d", log, p->pid);
+ p->log_thrown &= ~log;
+ }
+
+ return status;
+}
- // mark them all as un-updated
- all_pids_count = 0;
- for(p = root_of_pids; p ; p = p->next) {
- all_pids_count++;
- p->parent = NULL;
- p->updated = 0;
- p->children_count = 0;
- p->merged = 0;
- p->new_entry = 0;
+void collect_data_for_pid(pid_t pid) {
+ if(unlikely(pid <= 0 || pid > pid_max)) {
+ error("Invalid pid %d read (expected 1 to %d). Ignoring process.", pid, pid_max);
+ return;
+ }
- p->last_minflt = p->minflt;
- p->last_cminflt = p->cminflt;
- p->last_majflt = p->majflt;
- p->last_cmajflt = p->cmajflt;
- p->last_utime = p->utime;
- p->last_stime = p->stime;
- p->last_cutime = p->cutime;
- p->last_cstime = p->cstime;
-
- p->last_io_logical_bytes_read = p->io_logical_bytes_read;
- p->last_io_logical_bytes_written = p->io_logical_bytes_written;
- p->last_io_read_calls = p->io_read_calls;
- p->last_io_write_calls = p->io_write_calls;
- p->last_io_storage_bytes_read = p->io_storage_bytes_read;
- p->last_io_storage_bytes_written = p->io_storage_bytes_written;
- p->last_io_cancelled_write_bytes = p->io_cancelled_write_bytes;
- }
-
- while((file = readdir(dir))) {
- char *endptr = file->d_name;
- pid_t pid = (pid_t) strtoul(file->d_name, &endptr, 10);
-
- // make sure we read a valid number
- if(unlikely(pid <= 0 || pid > pid_max || endptr == file->d_name || *endptr != '\0'))
- continue;
-
- p = get_pid_entry(pid);
- if(unlikely(!p)) continue;
-
-
- // --------------------------------------------------------------------
- // /proc/<pid>/stat
-
- if(unlikely(read_proc_pid_stat(p))) {
- error("Cannot process %s/proc/%d/stat", host_prefix, pid);
-
- // there is no reason to proceed if we cannot get its status
- continue;
- }
-
- // check its parent pid
- if(unlikely(p->ppid < 0 || p->ppid > pid_max)) {
- error("Pid %d states invalid parent pid %d. Using 0.", pid, p->ppid);
-
- p->ppid = 0;
- }
-
- // --------------------------------------------------------------------
- // /proc/<pid>/cmdline
-
- if(proc_pid_cmdline_is_needed) {
- if(unlikely(read_proc_pid_cmdline(p))) {
- error("Cannot process %s/proc/%d/cmdline", host_prefix, pid);
- }
- }
-
- // --------------------------------------------------------------------
- // /proc/<pid>/statm
-
- if(unlikely(read_proc_pid_statm(p))) {
- error("Cannot process %s/proc/%d/statm", host_prefix, pid);
-
- // there is no reason to proceed if we cannot get its memory status
- continue;
- }
-
-
- // --------------------------------------------------------------------
- // /proc/<pid>/io
-
- if(unlikely(read_proc_pid_io(p))) {
- error("Cannot process %s/proc/%d/io", host_prefix, pid);
-
- // on systems without /proc/X/io
- // allow proceeding without I/O information
- // continue;
- }
-
- // --------------------------------------------------------------------
- // <pid> ownership
-
- if(unlikely(read_proc_pid_ownership(p))) {
- error("Cannot stat %s/proc/%d", host_prefix, pid);
- }
-
- // --------------------------------------------------------------------
- // link it
-
- // check if it is target
- // we do this only once, the first time this pid is loaded
- if(unlikely(p->new_entry)) {
- if(debug) fprintf(stderr, "apps.plugin: \tJust added %s\n", p->comm);
- uint32_t hash = simple_hash(p->comm);
- size_t pclen = strlen(p->comm);
-
- struct target *w;
- for(w = apps_groups_root_target; w ; w = w->next) {
- // if(debug || (p->target && p->target->debug)) fprintf(stderr, "apps.plugin: \t\tcomparing '%s' with '%s'\n", w->compare, p->comm);
-
- // find it - 4 cases:
- // 1. the target is not a pattern
- // 2. the target has the prefix
- // 3. the target has the suffix
- // 4. the target is something inside cmdline
- if( (!w->starts_with && !w->ends_with && w->comparehash == hash && !strcmp(w->compare, p->comm))
- || (w->starts_with && !w->ends_with && !strncmp(w->compare, p->comm, w->comparelen))
- || (!w->starts_with && w->ends_with && pclen >= w->comparelen && !strcmp(w->compare, &p->comm[pclen - w->comparelen]))
- || (proc_pid_cmdline_is_needed && w->starts_with && w->ends_with && strstr(p->cmdline, w->compare))
- ) {
- if(w->target) p->target = w->target;
- else p->target = w;
+ struct pid_stat *p = get_pid_entry(pid);
+ if(unlikely(!p || p->read)) return;
+ p->read = 1;
- if(debug || (p->target && p->target->debug))
- fprintf(stderr, "apps.plugin: \t\t%s linked to target %s\n", p->comm, p->target->name);
- }
- }
- }
+ // fprintf(stderr, "Reading process %d (%s), sortlist %d\n", p->pid, p->comm, p->sortlist);
- // --------------------------------------------------------------------
- // /proc/<pid>/fd
+ // --------------------------------------------------------------------
+ // /proc/<pid>/stat
- if(unlikely(read_pid_file_descriptors(p))) {
- error("Cannot process entries in %s/proc/%d/fd", host_prefix, pid);
- }
+ if(unlikely(!managed_log(p, PID_LOG_STAT, read_proc_pid_stat(p))))
+ // there is no reason to proceed if we cannot get its status
+ return;
- // --------------------------------------------------------------------
- // done!
+ read_proc_pid_ownership(p);
- // mark it as updated
- p->updated = 1;
- }
+ // check its parent pid
+ if(unlikely(p->ppid < 0 || p->ppid > pid_max)) {
+ error("Pid %d (command '%s') states invalid parent pid %d. Using 0.", pid, p->comm, p->ppid);
+ p->ppid = 0;
+ }
- closedir(dir);
+ // --------------------------------------------------------------------
+ // /proc/<pid>/io
- return 1;
-}
+ managed_log(p, PID_LOG_IO, read_proc_pid_io(p));
+ // --------------------------------------------------------------------
+ // /proc/<pid>/statm
-// ----------------------------------------------------------------------------
+ if(unlikely(!managed_log(p, PID_LOG_STATM, read_proc_pid_statm(p))))
+ // there is no reason to proceed if we cannot get its memory status
+ return;
-#ifdef AGGREGATE_CHILDREN_TO_PARENTS
-// print a tree view of all processes
-int debug_childrens_aggregations(pid_t pid, int level) {
- struct pid_stat *p = NULL;
- char b[level+3];
- int i, ret = 0;
-
- for(i = 0; i < level; i++) b[i] = '\t';
- b[level] = '|';
- b[level+1] = '-';
- b[level+2] = '\0';
-
- for(p = root_of_pids; p ; p = p->next) {
- if(p->ppid == pid) {
- ret += debug_childrens_aggregations(p->pid, level+1);
- }
- }
-
- p = all_pids[pid];
- if(p) {
- if(!p->updated) ret += 1;
- if(ret) fprintf(stderr, "%s %s %d [%s, %s] c=%d u=%llu+%llu, s=%llu+%llu, cu=%llu+%llu, cs=%llu+%llu, n=%llu+%llu, j=%llu+%llu, cn=%llu+%llu, cj=%llu+%llu\n"
- , b, p->comm, p->pid, p->updated?"OK":"KILLED", p->target->name, p->children_count
- , p->utime, p->utime - p->old_utime
- , p->stime, p->stime - p->old_stime
- , p->cutime, p->cutime - p->old_cutime
- , p->cstime, p->cstime - p->old_cstime
- , p->minflt, p->minflt - p->old_minflt
- , p->majflt, p->majflt - p->old_majflt
- , p->cminflt, p->cminflt - p->old_cminflt
- , p->cmajflt, p->cmajflt - p->old_cmajflt
- );
- }
-
- return ret;
-}
-#endif /* AGGREGATE_CHILDREN_TO_PARENTS */
+ // --------------------------------------------------------------------
+ // link it
+ // check if it is target
+ // we do this only once, the first time this pid is loaded
+ if(unlikely(p->new_entry)) {
+ // /proc/<pid>/cmdline
+ if(likely(proc_pid_cmdline_is_needed))
+ managed_log(p, PID_LOG_CMDLINE, read_proc_pid_cmdline(p));
+ if(unlikely(debug))
+ fprintf(stderr, "apps.plugin: \tJust added %d (%s)\n", pid, p->comm);
+
+ uint32_t hash = simple_hash(p->comm);
+ size_t pclen = strlen(p->comm);
+
+ struct target *w;
+ for(w = apps_groups_root_target; w ; w = w->next) {
+ // if(debug || (p->target && p->target->debug)) fprintf(stderr, "apps.plugin: \t\tcomparing '%s' with '%s'\n", w->compare, p->comm);
+
+ // find it - 4 cases:
+ // 1. the target is not a pattern
+ // 2. the target has the prefix
+ // 3. the target has the suffix
+ // 4. the target is something inside cmdline
+ if( (!w->starts_with && !w->ends_with && w->comparehash == hash && !strcmp(w->compare, p->comm))
+ || (w->starts_with && !w->ends_with && !strncmp(w->compare, p->comm, w->comparelen))
+ || (!w->starts_with && w->ends_with && pclen >= w->comparelen && !strcmp(w->compare, &p->comm[pclen - w->comparelen]))
+ || (proc_pid_cmdline_is_needed && w->starts_with && w->ends_with && strstr(p->cmdline, w->compare))
+ ) {
+ if(w->target) p->target = w->target;
+ else p->target = w;
+
+ if(debug || (p->target && p->target->debug))
+ fprintf(stderr, "apps.plugin: \t\t%s linked to target %s\n", p->comm, p->target->name);
+
+ break;
+ }
+ }
+ }
+
+ // --------------------------------------------------------------------
+ // /proc/<pid>/fd
+
+ if(enable_file_charts)
+ managed_log(p, PID_LOG_FDS, read_pid_file_descriptors(p));
+
+ // --------------------------------------------------------------------
+ // done!
+
+ if(unlikely(debug && include_exited_childs && all_pids_count && p->ppid && all_pids[p->ppid] && !all_pids[p->ppid]->read))
+ fprintf(stderr, "Read process %d (%s) sortlisted %d, but its parent %d (%s) sortlisted %d, is not read\n", p->pid, p->comm, p->sortlist, all_pids[p->ppid]->pid, all_pids[p->ppid]->comm, all_pids[p->ppid]->sortlist);
+
+ // mark it as updated
+ p->updated = 1;
+ p->keep = 0;
+ p->keeploops = 0;
+}
+
+int collect_data_for_all_processes_from_proc(void) {
+ struct pid_stat *p = NULL;
+
+ if(all_pids_count) {
+ // read parents before childs
+ // this is needed to prevent a situation where
+ // a child is found running, but until we read
+ // its parent, it has exited and its parent
+ // has accumulated its resources
+
+ long slc = 0;
+ for(p = root_of_pids; p ; p = p->next) {
+ p->read = 0;
+ p->updated = 0;
+ p->new_entry = 0;
+ p->merged = 0;
+ p->children_count = 0;
+ p->parent = NULL;
+
+ all_pids_sortlist[slc++] = p->pid;
+ }
+
+ if(unlikely(slc != all_pids_count)) {
+ error("Internal error: I was thinking I had %ld processes in my arrays, but it seems there are more.", all_pids_count);
+ all_pids_count = slc;
+ }
+
+ if(include_exited_childs) {
+ qsort((void *)all_pids_sortlist, all_pids_count, sizeof(pid_t), compar_pid);
+ for(slc = 0; slc < all_pids_count; slc++)
+ collect_data_for_pid(all_pids_sortlist[slc]);
+ }
+ }
+
+ char dirname[FILENAME_MAX + 1];
+
+ snprintfz(dirname, FILENAME_MAX, "%s/proc", host_prefix);
+ DIR *dir = opendir(dirname);
+ if(!dir) return 0;
+
+ struct dirent *file = NULL;
+
+ while((file = readdir(dir))) {
+ char *endptr = file->d_name;
+ pid_t pid = (pid_t) strtoul(file->d_name, &endptr, 10);
+
+ // make sure we read a valid number
+ if(unlikely(endptr == file->d_name || *endptr != '\0'))
+ continue;
+
+ collect_data_for_pid(pid);
+ }
+ closedir(dir);
+
+ // normally this is done
+ // however we may have processes exited while we collected values
+ // so let's find the exited ones
+ // we do this by collecting the ownership of process
+ // if we manage to get the ownership, the process still runs
+
+ read_proc_stat();
+ link_all_processes_to_their_parents();
+ process_exited_processes();
+
+ return 1;
+}
// ----------------------------------------------------------------------------
// update statistics on the targets
@@ -1486,729 +1798,760 @@ int debug_childrens_aggregations(pid_t pid, int level) {
// 9. find the unique file count for each target
// check: update_apps_groups_statistics()
-void link_all_processes_to_their_parents(void) {
- struct pid_stat *p = NULL;
-
- // link all children to their parents
- // and update children count on parents
- for(p = root_of_pids; p ; p = p->next) {
- // for each process found running
-
- if(p->ppid > 0
- && p->ppid <= pid_max
- && all_pids[p->ppid]
- ) {
- // for valid processes
-
- if(debug || (p->target && p->target->debug))
- fprintf(stderr, "apps.plugin: \tparent of %d (%s) is %d (%s)\n", p->pid, p->comm, p->ppid, all_pids[p->ppid]->comm);
-
- p->parent = all_pids[p->ppid];
- p->parent->children_count++;
- }
- else if(p->ppid != 0)
- error("pid %d %s states parent %d, but the later does not exist.", p->pid, p->comm, p->ppid);
- }
-}
-
-#ifdef AGGREGATE_CHILDREN_TO_PARENTS
-void aggregate_children_to_parents(void) {
- struct pid_stat *p = NULL;
-
- // for each killed process, remove its values from the parents
- // sums (we had already added them in a previous loop)
- for(p = root_of_pids; p ; p = p->next) {
- if(p->updated) continue;
-
- if(debug) fprintf(stderr, "apps.plugin: UNMERGING %d %s\n", p->pid, p->comm);
-
- unsigned long long diff_utime = p->utime + p->cutime + p->fix_cutime;
- unsigned long long diff_stime = p->stime + p->cstime + p->fix_cstime;
- unsigned long long diff_minflt = p->minflt + p->cminflt + p->fix_cminflt;
- unsigned long long diff_majflt = p->majflt + p->cmajflt + p->fix_cmajflt;
-
- struct pid_stat *t = p;
- while((t = t->parent)) {
- if(!t->updated) continue;
-
- unsigned long long x;
- if(diff_utime && t->diff_cutime) {
- x = (t->diff_cutime < diff_utime)?t->diff_cutime:diff_utime;
- diff_utime -= x;
- t->diff_cutime -= x;
- t->fix_cutime += x;
- if(debug) fprintf(stderr, "apps.plugin: \t cutime %llu from %d %s %s\n", x, t->pid, t->comm, t->target->name);
- }
- if(diff_stime && t->diff_cstime) {
- x = (t->diff_cstime < diff_stime)?t->diff_cstime:diff_stime;
- diff_stime -= x;
- t->diff_cstime -= x;
- t->fix_cstime += x;
- if(debug) fprintf(stderr, "apps.plugin: \t cstime %llu from %d %s %s\n", x, t->pid, t->comm, t->target->name);
- }
- if(diff_minflt && t->diff_cminflt) {
- x = (t->diff_cminflt < diff_minflt)?t->diff_cminflt:diff_minflt;
- diff_minflt -= x;
- t->diff_cminflt -= x;
- t->fix_cminflt += x;
- if(debug) fprintf(stderr, "apps.plugin: \t cminflt %llu from %d %s %s\n", x, t->pid, t->comm, t->target->name);
- }
- if(diff_majflt && t->diff_cmajflt) {
- x = (t->diff_cmajflt < diff_majflt)?t->diff_cmajflt:diff_majflt;
- diff_majflt -= x;
- t->diff_cmajflt -= x;
- t->fix_cmajflt += x;
- if(debug) fprintf(stderr, "apps.plugin: \t cmajflt %llu from %d %s %s\n", x, t->pid, t->comm, t->target->name);
- }
- }
-
- if(diff_utime) error("Cannot fix up utime %llu", diff_utime);
- if(diff_stime) error("Cannot fix up stime %llu", diff_stime);
- if(diff_minflt) error("Cannot fix up minflt %llu", diff_minflt);
- if(diff_majflt) error("Cannot fix up majflt %llu", diff_majflt);
- }
-}
-#endif /* AGGREGATE_CHILDREN_TO_PARENTS */
-
-void cleanup_non_existing_pids(void) {
- int c;
- struct pid_stat *p = NULL;
-
- for(p = root_of_pids; p ;) {
- if(!p->updated) {
-// fprintf(stderr, "\tEXITED %d %s [parent %d %s, target %s] utime=%llu, stime=%llu, cutime=%llu, cstime=%llu, minflt=%llu, majflt=%llu, cminflt=%llu, cmajflt=%llu\n", p->pid, p->comm, p->parent->pid, p->parent->comm, p->target->name, p->utime, p->stime, p->cutime, p->cstime, p->minflt, p->majflt, p->cminflt, p->cmajflt);
-
- for(c = 0 ; c < p->fds_size ; c++) if(p->fds[c] > 0) {
- file_descriptor_not_used(p->fds[c]);
- p->fds[c] = 0;
- }
-
- pid_t r = p->pid;
- p = p->next;
- del_pid_entry(r);
- }
- else p = p->next;
- }
+void cleanup_exited_pids(void) {
+ int c;
+ struct pid_stat *p = NULL;
+
+ for(p = root_of_pids; p ;) {
+ if(!p->updated && (!p->keep || p->keeploops > 0)) {
+// fprintf(stderr, "\tEXITED %d %s [parent %d %s, target %s] utime=%llu, stime=%llu, gtime=%llu, cutime=%llu, cstime=%llu, cgtime=%llu, minflt=%llu, majflt=%llu, cminflt=%llu, cmajflt=%llu\n", p->pid, p->comm, p->parent->pid, p->parent->comm, p->target->name, p->utime, p->stime, p->gtime, p->cutime, p->cstime, p->cgtime, p->minflt, p->majflt, p->cminflt, p->cmajflt);
+
+ if(unlikely(debug && (p->keep || p->keeploops)))
+ fprintf(stderr, " > CLEANUP cannot keep exited process %d (%s) anymore - removing it.\n", p->pid, p->comm);
+
+ for(c = 0 ; c < p->fds_size ; c++) if(p->fds[c] > 0) {
+ file_descriptor_not_used(p->fds[c]);
+ p->fds[c] = 0;
+ }
+
+ pid_t r = p->pid;
+ p = p->next;
+ del_pid_entry(r);
+ }
+ else {
+ if(unlikely(p->keep)) p->keeploops++;
+ p->keep = 0;
+ p = p->next;
+ }
+ }
}
void apply_apps_groups_targets_inheritance(void) {
- struct pid_stat *p = NULL;
-
- // children that do not have a target
- // inherit their target from their parent
- int found = 1;
- while(found) {
- found = 0;
- for(p = root_of_pids; p ; p = p->next) {
- // if this process does not have a target
- // and it has a parent
- // and its parent has a target
- // then, set the parent's target to this process
- if(unlikely(!p->target && p->parent && p->parent->target)) {
- p->target = p->parent->target;
- found++;
-
- if(debug || (p->target && p->target->debug))
- fprintf(stderr, "apps.plugin: \t\tTARGET INHERITANCE: %s is inherited by %d (%s) from its parent %d (%s).\n", p->target->name, p->pid, p->comm, p->parent->pid, p->parent->comm);
- }
- }
- }
-
-
- // find all the procs with 0 childs and merge them to their parents
- // repeat, until nothing more can be done.
- found = 1;
- while(found) {
- found = 0;
- for(p = root_of_pids; p ; p = p->next) {
- // if this process does not have any children
- // and is not already merged
- // and has a parent
- // and its parent has children
- // and the target of this process and its parent is the same, or the parent does not have a target
- // and its parent is not init
- // then, mark them as merged.
- if(unlikely(
- !p->children_count
- && !p->merged
- && p->parent
- && p->parent->children_count
- && (p->target == p->parent->target || !p->parent->target)
- && p->ppid != 1
- )) {
- p->parent->children_count--;
- p->merged = 1;
-
- // the parent inherits the child's target, if it does not have a target itself
- if(unlikely(p->target && !p->parent->target)) {
- p->parent->target = p->target;
-
- if(debug || (p->target && p->target->debug))
- fprintf(stderr, "apps.plugin: \t\tTARGET INHERITANCE: %s is inherited by %d (%s) from its child %d (%s).\n", p->target->name, p->parent->pid, p->parent->comm, p->pid, p->comm);
- }
-
- found++;
- }
- }
-
- if(debug)
- fprintf(stderr, "apps.plugin: merged %d processes\n", found);
- }
-
- // init goes always to default target
- if(all_pids[1])
- all_pids[1]->target = apps_groups_default_target;
-
- // give a default target on all top level processes
- for(p = root_of_pids; p ; p = p->next) {
- // if the process is not merged itself
- // then is is a top level process
- if(!p->merged && !p->target)
- p->target = apps_groups_default_target;
-
-#ifdef AGGREGATE_CHILDREN_TO_PARENTS
- // by the way, update the diffs
- // will be used later for subtracting killed process times
- p->diff_cutime = p->utime - p->cutime;
- p->diff_cstime = p->stime - p->cstime;
- p->diff_cminflt = p->minflt - p->cminflt;
- p->diff_cmajflt = p->majflt - p->cmajflt;
-#endif /* AGGREGATE_CHILDREN_TO_PARENTS */
- }
-
- // give a target to all merged child processes
- found = 1;
- while(found) {
- found = 0;
- for(p = root_of_pids; p ; p = p->next) {
- if(unlikely(!p->target && p->merged && p->parent && p->parent->target)) {
- p->target = p->parent->target;
- found++;
-
- if(debug || (p->target && p->target->debug))
- fprintf(stderr, "apps.plugin: \t\tTARGET INHERITANCE: %s is inherited by %d (%s) from its parent %d (%s) at phase 2.\n", p->target->name, p->pid, p->comm, p->parent->pid, p->parent->comm);
- }
- }
- }
+ struct pid_stat *p = NULL;
+
+ // children that do not have a target
+ // inherit their target from their parent
+ int found = 1, loops = 0;
+ while(found) {
+ if(unlikely(debug)) loops++;
+ found = 0;
+ for(p = root_of_pids; p ; p = p->next) {
+ // if this process does not have a target
+ // and it has a parent
+ // and its parent has a target
+ // then, set the parent's target to this process
+ if(unlikely(!p->target && p->parent && p->parent->target)) {
+ p->target = p->parent->target;
+ found++;
+
+ if(debug || (p->target && p->target->debug))
+ fprintf(stderr, "apps.plugin: \t\tTARGET INHERITANCE: %s is inherited by %d (%s) from its parent %d (%s).\n", p->target->name, p->pid, p->comm, p->parent->pid, p->parent->comm);
+ }
+ }
+ }
+
+ // find all the procs with 0 childs and merge them to their parents
+ // repeat, until nothing more can be done.
+ int sortlist = 1;
+ found = 1;
+ while(found) {
+ if(unlikely(debug)) loops++;
+ found = 0;
+
+ for(p = root_of_pids; p ; p = p->next) {
+ if(unlikely(!p->sortlist && !p->children_count))
+ p->sortlist = sortlist++;
+
+ // if this process does not have any children
+ // and is not already merged
+ // and has a parent
+ // and its parent has children
+ // and the target of this process and its parent is the same, or the parent does not have a target
+ // and its parent is not init
+ // then, mark them as merged.
+ if(unlikely(
+ !p->children_count
+ && !p->merged
+ && p->parent
+ && p->parent->children_count
+ && (p->target == p->parent->target || !p->parent->target)
+ && p->ppid != 1
+ )) {
+ p->parent->children_count--;
+ p->merged = 1;
+
+ // the parent inherits the child's target, if it does not have a target itself
+ if(unlikely(p->target && !p->parent->target)) {
+ p->parent->target = p->target;
+
+ if(debug || (p->target && p->target->debug))
+ fprintf(stderr, "apps.plugin: \t\tTARGET INHERITANCE: %s is inherited by %d (%s) from its child %d (%s).\n", p->target->name, p->parent->pid, p->parent->comm, p->pid, p->comm);
+ }
+
+ found++;
+ }
+ }
+
+ if(unlikely(debug))
+ fprintf(stderr, "apps.plugin: TARGET INHERITANCE: merged %d processes\n", found);
+ }
+
+ // init goes always to default target
+ if(all_pids[1])
+ all_pids[1]->target = apps_groups_default_target;
+
+ // give a default target on all top level processes
+ if(unlikely(debug)) loops++;
+ for(p = root_of_pids; p ; p = p->next) {
+ // if the process is not merged itself
+ // then is is a top level process
+ if(unlikely(!p->merged && !p->target))
+ p->target = apps_groups_default_target;
+
+ // make sure all processes have a sortlist
+ if(unlikely(!p->sortlist))
+ p->sortlist = sortlist++;
+ }
+
+ if(all_pids[1])
+ all_pids[1]->sortlist = sortlist++;
+
+ // give a target to all merged child processes
+ found = 1;
+ while(found) {
+ if(unlikely(debug)) loops++;
+ found = 0;
+ for(p = root_of_pids; p ; p = p->next) {
+ if(unlikely(!p->target && p->merged && p->parent && p->parent->target)) {
+ p->target = p->parent->target;
+ found++;
+
+ if(debug || (p->target && p->target->debug))
+ fprintf(stderr, "apps.plugin: \t\tTARGET INHERITANCE: %s is inherited by %d (%s) from its parent %d (%s) at phase 2.\n", p->target->name, p->pid, p->comm, p->parent->pid, p->parent->comm);
+ }
+ }
+ }
+
+ if(unlikely(debug))
+ fprintf(stderr, "apps.plugin: apply_apps_groups_targets_inheritance() made %d loops on the process tree\n", loops);
}
long zero_all_targets(struct target *root) {
- struct target *w;
- long count = 0;
-
- for (w = root; w ; w = w->next) {
- count++;
-
- if(w->fds) free(w->fds);
- w->fds = NULL;
-
- w->minflt = 0;
- w->majflt = 0;
- w->utime = 0;
- w->stime = 0;
- w->cminflt = 0;
- w->cmajflt = 0;
- w->cutime = 0;
- w->cstime = 0;
- w->num_threads = 0;
- w->rss = 0;
- w->processes = 0;
-
- w->statm_size = 0;
- w->statm_resident = 0;
- w->statm_share = 0;
- w->statm_text = 0;
- w->statm_lib = 0;
- w->statm_data = 0;
- w->statm_dirty = 0;
-
- w->io_logical_bytes_read = 0;
- w->io_logical_bytes_written = 0;
- w->io_read_calls = 0;
- w->io_write_calls = 0;
- w->io_storage_bytes_read = 0;
- w->io_storage_bytes_written = 0;
- w->io_cancelled_write_bytes = 0;
- }
-
- return count;
+ struct target *w;
+ long count = 0;
+
+ for (w = root; w ; w = w->next) {
+ count++;
+
+ if(w->fds) freez(w->fds);
+ w->fds = NULL;
+
+ w->minflt = 0;
+ w->majflt = 0;
+ w->utime = 0;
+ w->stime = 0;
+ w->gtime = 0;
+ w->cminflt = 0;
+ w->cmajflt = 0;
+ w->cutime = 0;
+ w->cstime = 0;
+ w->cgtime = 0;
+ w->num_threads = 0;
+ w->rss = 0;
+ w->processes = 0;
+
+ w->statm_size = 0;
+ w->statm_resident = 0;
+ w->statm_share = 0;
+ w->statm_text = 0;
+ w->statm_lib = 0;
+ w->statm_data = 0;
+ w->statm_dirty = 0;
+
+ w->io_logical_bytes_read = 0;
+ w->io_logical_bytes_written = 0;
+ w->io_read_calls = 0;
+ w->io_write_calls = 0;
+ w->io_storage_bytes_read = 0;
+ w->io_storage_bytes_written = 0;
+ w->io_cancelled_write_bytes = 0;
+ }
+
+ return count;
}
void aggregate_pid_on_target(struct target *w, struct pid_stat *p, struct target *o) {
- if(unlikely(!w->fds)) {
- w->fds = calloc(sizeof(int), (size_t) all_files_size);
- if(unlikely(!w->fds))
- error("Cannot allocate memory for fds in %s", w->name);
- }
-
- if(likely(p->updated)) {
- w->cutime += p->cutime; // - p->fix_cutime;
- w->cstime += p->cstime; // - p->fix_cstime;
- w->cminflt += p->cminflt; // - p->fix_cminflt;
- w->cmajflt += p->cmajflt; // - p->fix_cmajflt;
-
- w->utime += p->utime; //+ (p->pid != 1)?(p->cutime - p->fix_cutime):0;
- w->stime += p->stime; //+ (p->pid != 1)?(p->cstime - p->fix_cstime):0;
- w->minflt += p->minflt; //+ (p->pid != 1)?(p->cminflt - p->fix_cminflt):0;
- w->majflt += p->majflt; //+ (p->pid != 1)?(p->cmajflt - p->fix_cmajflt):0;
-
- //if(p->num_threads < 0)
- // error("Negative threads number for pid '%s' (%d): %d", p->comm, p->pid, p->num_threads);
-
- //if(p->num_threads > 10000)
- // error("Excessive threads number for pid '%s' (%d): %d", p->comm, p->pid, p->num_threads);
-
- w->num_threads += p->num_threads;
- w->rss += p->rss;
-
- w->statm_size += p->statm_size;
- w->statm_resident += p->statm_resident;
- w->statm_share += p->statm_share;
- w->statm_text += p->statm_text;
- w->statm_lib += p->statm_lib;
- w->statm_data += p->statm_data;
- w->statm_dirty += p->statm_dirty;
-
- w->io_logical_bytes_read += p->io_logical_bytes_read;
- w->io_logical_bytes_written += p->io_logical_bytes_written;
- w->io_read_calls += p->io_read_calls;
- w->io_write_calls += p->io_write_calls;
- w->io_storage_bytes_read += p->io_storage_bytes_read;
- w->io_storage_bytes_written += p->io_storage_bytes_written;
- w->io_cancelled_write_bytes += p->io_cancelled_write_bytes;
-
- w->processes++;
-
- if(likely(w->fds)) {
- int c;
- for(c = 0; c < p->fds_size ;c++) {
- if(p->fds[c] == 0) continue;
-
- if(likely(p->fds[c] < all_files_size)) {
- if(w->fds) w->fds[p->fds[c]]++;
- }
- else
- error("Invalid fd number %d", p->fds[c]);
- }
- }
-
- if(unlikely(debug || w->debug))
- fprintf(stderr, "apps.plugin: \tAgregating %s pid %d on %s utime=%llu, stime=%llu, cutime=%llu, cstime=%llu, minflt=%llu, majflt=%llu, cminflt=%llu, cmajflt=%llu\n", p->comm, p->pid, w->name, p->utime, p->stime, p->cutime, p->cstime, p->minflt, p->majflt, p->cminflt, p->cmajflt);
-
-/* if(p->utime - p->old_utime > 100) fprintf(stderr, "BIG CHANGE: %d %s utime increased by %llu from %llu to %llu\n", p->pid, p->comm, p->utime - p->old_utime, p->old_utime, p->utime);
- if(p->cutime - p->old_cutime > 100) fprintf(stderr, "BIG CHANGE: %d %s cutime increased by %llu from %llu to %llu\n", p->pid, p->comm, p->cutime - p->old_cutime, p->old_cutime, p->cutime);
- if(p->stime - p->old_stime > 100) fprintf(stderr, "BIG CHANGE: %d %s stime increased by %llu from %llu to %llu\n", p->pid, p->comm, p->stime - p->old_stime, p->old_stime, p->stime);
- if(p->cstime - p->old_cstime > 100) fprintf(stderr, "BIG CHANGE: %d %s cstime increased by %llu from %llu to %llu\n", p->pid, p->comm, p->cstime - p->old_cstime, p->old_cstime, p->cstime);
- if(p->minflt - p->old_minflt > 5000) fprintf(stderr, "BIG CHANGE: %d %s minflt increased by %llu from %llu to %llu\n", p->pid, p->comm, p->minflt - p->old_minflt, p->old_minflt, p->minflt);
- if(p->majflt - p->old_majflt > 5000) fprintf(stderr, "BIG CHANGE: %d %s majflt increased by %llu from %llu to %llu\n", p->pid, p->comm, p->majflt - p->old_majflt, p->old_majflt, p->majflt);
- if(p->cminflt - p->old_cminflt > 15000) fprintf(stderr, "BIG CHANGE: %d %s cminflt increased by %llu from %llu to %llu\n", p->pid, p->comm, p->cminflt - p->old_cminflt, p->old_cminflt, p->cminflt);
- if(p->cmajflt - p->old_cmajflt > 15000) fprintf(stderr, "BIG CHANGE: %d %s cmajflt increased by %llu from %llu to %llu\n", p->pid, p->comm, p->cmajflt - p->old_cmajflt, p->old_cmajflt, p->cmajflt);
-*/
-#ifdef AGGREGATE_CHILDREN_TO_PARENTS
- p->old_utime = p->utime;
- p->old_cutime = p->cutime;
- p->old_stime = p->stime;
- p->old_cstime = p->cstime;
- p->old_minflt = p->minflt;
- p->old_majflt = p->majflt;
- p->old_cminflt = p->cminflt;
- p->old_cmajflt = p->cmajflt;
-#endif /* AGGREGATE_CHILDREN_TO_PARENTS */
-
- if(o) {
- // since the process switched target
- // for all incremental values
- // we have to subtract its OLD values from the new target
- // and add its OLD values to the old target
-
- // IMPORTANT
- // We add/subtract the last/OLD values we added to the target
-
- w->fix_cutime -= p->last_cutime;
- w->fix_cstime -= p->last_cstime;
- w->fix_cminflt -= p->last_cminflt;
- w->fix_cmajflt -= p->last_cmajflt;
-
- w->fix_utime -= p->last_utime;
- w->fix_stime -= p->last_stime;
- w->fix_minflt -= p->last_minflt;
- w->fix_majflt -= p->last_majflt;
-
-
- w->fix_io_logical_bytes_read -= p->last_io_logical_bytes_read;
- w->fix_io_logical_bytes_written -= p->last_io_logical_bytes_written;
- w->fix_io_read_calls -= p->last_io_read_calls;
- w->fix_io_write_calls -= p->last_io_write_calls;
- w->fix_io_storage_bytes_read -= p->last_io_storage_bytes_read;
- w->fix_io_storage_bytes_written -= p->last_io_storage_bytes_written;
- w->fix_io_cancelled_write_bytes -= p->last_io_cancelled_write_bytes;
-
- // ---
-
- o->fix_cutime += p->last_cutime;
- o->fix_cstime += p->last_cstime;
- o->fix_cminflt += p->last_cminflt;
- o->fix_cmajflt += p->last_cmajflt;
-
- o->fix_utime += p->last_utime;
- o->fix_stime += p->last_stime;
- o->fix_minflt += p->last_minflt;
- o->fix_majflt += p->last_majflt;
-
- o->fix_io_logical_bytes_read += p->last_io_logical_bytes_read;
- o->fix_io_logical_bytes_written += p->last_io_logical_bytes_written;
- o->fix_io_read_calls += p->last_io_read_calls;
- o->fix_io_write_calls += p->last_io_write_calls;
- o->fix_io_storage_bytes_read += p->last_io_storage_bytes_read;
- o->fix_io_storage_bytes_written += p->last_io_storage_bytes_written;
- o->fix_io_cancelled_write_bytes += p->last_io_cancelled_write_bytes;
- }
- }
- else {
- // if(o) fprintf(stderr, "apps.plugin: \t\tpid %d (%s) is not updated by OLD target %s (%s) is present.\n", p->pid, p->comm, o->id, o->name);
-
- // since the process has exited, the user
- // will see a drop in our charts, because the incremental
- // values of this process will not be there
-
- // add them to the fix_* values and they will be added to
- // the reported values, so that the report goes steady
- w->fix_minflt += p->minflt;
- w->fix_majflt += p->majflt;
- w->fix_utime += p->utime;
- w->fix_stime += p->stime;
- w->fix_cminflt += p->cminflt;
- w->fix_cmajflt += p->cmajflt;
- w->fix_cutime += p->cutime;
- w->fix_cstime += p->cstime;
-
- w->fix_io_logical_bytes_read += p->io_logical_bytes_read;
- w->fix_io_logical_bytes_written += p->io_logical_bytes_written;
- w->fix_io_read_calls += p->io_read_calls;
- w->fix_io_write_calls += p->io_write_calls;
- w->fix_io_storage_bytes_read += p->io_storage_bytes_read;
- w->fix_io_storage_bytes_written += p->io_storage_bytes_written;
- w->fix_io_cancelled_write_bytes += p->io_cancelled_write_bytes;
- }
-
+ (void)o;
+
+ if(unlikely(!w->fds))
+ w->fds = callocz(sizeof(int), (size_t) all_files_size);
+
+ if(likely(p->updated)) {
+ w->cutime += p->cutime;
+ w->cstime += p->cstime;
+ w->cgtime += p->cgtime;
+ w->cminflt += p->cminflt;
+ w->cmajflt += p->cmajflt;
+
+ w->utime += p->utime;
+ w->stime += p->stime;
+ w->gtime += p->gtime;
+ w->minflt += p->minflt;
+ w->majflt += p->majflt;
+
+ w->rss += p->rss;
+
+ w->statm_size += p->statm_size;
+ w->statm_resident += p->statm_resident;
+ w->statm_share += p->statm_share;
+ w->statm_text += p->statm_text;
+ w->statm_lib += p->statm_lib;
+ w->statm_data += p->statm_data;
+ w->statm_dirty += p->statm_dirty;
+
+ w->io_logical_bytes_read += p->io_logical_bytes_read;
+ w->io_logical_bytes_written += p->io_logical_bytes_written;
+ w->io_read_calls += p->io_read_calls;
+ w->io_write_calls += p->io_write_calls;
+ w->io_storage_bytes_read += p->io_storage_bytes_read;
+ w->io_storage_bytes_written += p->io_storage_bytes_written;
+ w->io_cancelled_write_bytes += p->io_cancelled_write_bytes;
+
+ w->processes++;
+ w->num_threads += p->num_threads;
+
+ if(likely(w->fds)) {
+ int c;
+ for(c = 0; c < p->fds_size ;c++) {
+ if(p->fds[c] == 0) continue;
+
+ if(likely(p->fds[c] < all_files_size)) {
+ if(w->fds) w->fds[p->fds[c]]++;
+ }
+ else
+ error("Invalid fd number %d", p->fds[c]);
+ }
+ }
+
+ if(unlikely(debug || w->debug))
+ fprintf(stderr, "apps.plugin: \taggregating '%s' pid %d on target '%s' utime=%llu, stime=%llu, gtime=%llu, cutime=%llu, cstime=%llu, cgtime=%llu, minflt=%llu, majflt=%llu, cminflt=%llu, cmajflt=%llu\n", p->comm, p->pid, w->name, p->utime, p->stime, p->gtime, p->cutime, p->cstime, p->cgtime, p->minflt, p->majflt, p->cminflt, p->cmajflt);
+ }
}
void count_targets_fds(struct target *root) {
- int c;
- struct target *w;
-
- for (w = root; w ; w = w->next) {
- if(!w->fds) continue;
-
- w->openfiles = 0;
- w->openpipes = 0;
- w->opensockets = 0;
- w->openinotifies = 0;
- w->openeventfds = 0;
- w->opentimerfds = 0;
- w->opensignalfds = 0;
- w->openeventpolls = 0;
- w->openother = 0;
-
- for(c = 1; c < all_files_size ;c++) {
- if(w->fds[c] > 0)
- switch(all_files[c].type) {
- case FILETYPE_FILE:
- w->openfiles++;
- break;
-
- case FILETYPE_PIPE:
- w->openpipes++;
- break;
-
- case FILETYPE_SOCKET:
- w->opensockets++;
- break;
-
- case FILETYPE_INOTIFY:
- w->openinotifies++;
- break;
-
- case FILETYPE_EVENTFD:
- w->openeventfds++;
- break;
-
- case FILETYPE_TIMERFD:
- w->opentimerfds++;
- break;
-
- case FILETYPE_SIGNALFD:
- w->opensignalfds++;
- break;
-
- case FILETYPE_EVENTPOLL:
- w->openeventpolls++;
- break;
-
- default:
- w->openother++;
- }
- }
-
- free(w->fds);
- w->fds = NULL;
- }
+ int c;
+ struct target *w;
+
+ for (w = root; w ; w = w->next) {
+ if(!w->fds) continue;
+
+ w->openfiles = 0;
+ w->openpipes = 0;
+ w->opensockets = 0;
+ w->openinotifies = 0;
+ w->openeventfds = 0;
+ w->opentimerfds = 0;
+ w->opensignalfds = 0;
+ w->openeventpolls = 0;
+ w->openother = 0;
+
+ for(c = 1; c < all_files_size ;c++) {
+ if(w->fds[c] > 0)
+ switch(all_files[c].type) {
+ case FILETYPE_FILE:
+ w->openfiles++;
+ break;
+
+ case FILETYPE_PIPE:
+ w->openpipes++;
+ break;
+
+ case FILETYPE_SOCKET:
+ w->opensockets++;
+ break;
+
+ case FILETYPE_INOTIFY:
+ w->openinotifies++;
+ break;
+
+ case FILETYPE_EVENTFD:
+ w->openeventfds++;
+ break;
+
+ case FILETYPE_TIMERFD:
+ w->opentimerfds++;
+ break;
+
+ case FILETYPE_SIGNALFD:
+ w->opensignalfds++;
+ break;
+
+ case FILETYPE_EVENTPOLL:
+ w->openeventpolls++;
+ break;
+
+ default:
+ w->openother++;
+ }
+ }
+
+ freez(w->fds);
+ w->fds = NULL;
+ }
}
-void calculate_netdata_statistics(void)
-{
- link_all_processes_to_their_parents();
- apply_apps_groups_targets_inheritance();
-
-#ifdef AGGREGATE_CHILDREN_TO_PARENTS
- aggregate_children_to_parents();
-#endif /* AGGREGATE_CHILDREN_TO_PARENTS */
-
- zero_all_targets(users_root_target);
- zero_all_targets(groups_root_target);
- apps_groups_targets = zero_all_targets(apps_groups_root_target);
-
-#ifdef AGGREGATE_CHILDREN_TO_PARENTS
- if(debug)
- debug_childrens_aggregations(0, 1);
-#endif /* AGGREGATE_CHILDREN_TO_PARENTS */
-
- // this has to be done, before the cleanup
- struct pid_stat *p = NULL;
- struct target *w = NULL, *o = NULL;
-
- // concentrate everything on the apps_groups_targets
- for(p = root_of_pids; p ; p = p->next) {
-
- // --------------------------------------------------------------------
- // apps_groups targets
- if(likely(p->target))
- aggregate_pid_on_target(p->target, p, NULL);
- else
- error("pid %d %s was left without a target!", p->pid, p->comm);
-
-
- // --------------------------------------------------------------------
- // user targets
- o = p->user_target;
- if(likely(p->user_target && p->user_target->uid == p->uid))
- w = p->user_target;
- else {
- if(unlikely(debug && p->user_target))
- fprintf(stderr, "apps.plugin: \t\tpid %d (%s) switched user from %d (%s) to %d.\n", p->pid, p->comm, p->user_target->uid, p->user_target->name, p->uid);
-
- w = p->user_target = get_users_target(p->uid);
- }
-
- if(likely(w))
- aggregate_pid_on_target(w, p, o);
- else
- error("pid %d %s was left without a user target!", p->pid, p->comm);
-
-
- // --------------------------------------------------------------------
- // group targets
- o = p->group_target;
- if(likely(p->group_target && p->group_target->gid == p->gid))
- w = p->group_target;
- else {
- if(unlikely(debug && p->group_target))
- fprintf(stderr, "apps.plugin: \t\tpid %d (%s) switched group from %d (%s) to %d.\n", p->pid, p->comm, p->group_target->gid, p->group_target->name, p->gid);
-
- w = p->group_target = get_groups_target(p->gid);
- }
-
- if(likely(w))
- aggregate_pid_on_target(w, p, o);
- else
- error("pid %d %s was left without a group target!", p->pid, p->comm);
-
- }
-
- count_targets_fds(apps_groups_root_target);
- count_targets_fds(users_root_target);
- count_targets_fds(groups_root_target);
-
- cleanup_non_existing_pids();
-}
+void calculate_netdata_statistics(void) {
+ apply_apps_groups_targets_inheritance();
-// ----------------------------------------------------------------------------
-// update chart dimensions
+ zero_all_targets(users_root_target);
+ zero_all_targets(groups_root_target);
+ apps_groups_targets = zero_all_targets(apps_groups_root_target);
-unsigned long long send_resource_usage_to_netdata() {
- static struct timeval last = { 0, 0 };
- static struct rusage me_last;
-
- struct timeval now;
- struct rusage me;
-
- unsigned long long usec;
- unsigned long long cpuuser;
- unsigned long long cpusyst;
-
- if(!last.tv_sec) {
- gettimeofday(&last, NULL);
- getrusage(RUSAGE_SELF, &me_last);
-
- // the first time, give a zero to allow
- // netdata calibrate to the current time
- // usec = update_every * 1000000ULL;
- usec = 0ULL;
- cpuuser = 0;
- cpusyst = 0;
- }
- else {
- gettimeofday(&now, NULL);
- getrusage(RUSAGE_SELF, &me);
-
- usec = usecdiff(&now, &last);
- cpuuser = me.ru_utime.tv_sec * 1000000ULL + me.ru_utime.tv_usec;
- cpusyst = me.ru_stime.tv_sec * 1000000ULL + me.ru_stime.tv_usec;
-
- bcopy(&now, &last, sizeof(struct timeval));
- bcopy(&me, &me_last, sizeof(struct rusage));
- }
-
- fprintf(stdout, "BEGIN netdata.apps_cpu %llu\n", usec);
- fprintf(stdout, "SET user = %llu\n", cpuuser);
- fprintf(stdout, "SET system = %llu\n", cpusyst);
- fprintf(stdout, "END\n");
-
- fprintf(stdout, "BEGIN netdata.apps_files %llu\n", usec);
- fprintf(stdout, "SET files = %llu\n", file_counter);
- fprintf(stdout, "SET pids = %ld\n", all_pids_count);
- fprintf(stdout, "SET fds = %d\n", all_files_len);
- fprintf(stdout, "SET targets = %ld\n", apps_groups_targets);
- fprintf(stdout, "END\n");
-
- return usec;
-}
-
-void send_collected_data_to_netdata(struct target *root, const char *type, unsigned long long usec)
-{
- struct target *w;
-
- fprintf(stdout, "BEGIN %s.cpu %llu\n", type, usec);
- for (w = root; w ; w = w->next) {
- if(w->target || (!w->processes && !w->exposed)) continue;
-
- fprintf(stdout, "SET %s = %llu\n", w->name, w->utime + w->stime + w->fix_utime + w->fix_stime);
- }
- fprintf(stdout, "END\n");
-
- fprintf(stdout, "BEGIN %s.cpu_user %llu\n", type, usec);
- for (w = root; w ; w = w->next) {
- if(w->target || (!w->processes && !w->exposed)) continue;
-
- fprintf(stdout, "SET %s = %llu\n", w->name, w->utime + w->fix_utime);
- }
- fprintf(stdout, "END\n");
-
- fprintf(stdout, "BEGIN %s.cpu_system %llu\n", type, usec);
- for (w = root; w ; w = w->next) {
- if(w->target || (!w->processes && !w->exposed)) continue;
-
- fprintf(stdout, "SET %s = %llu\n", w->name, w->stime + w->fix_stime);
- }
- fprintf(stdout, "END\n");
-
- fprintf(stdout, "BEGIN %s.threads %llu\n", type, usec);
- for (w = root; w ; w = w->next) {
- if(w->target || (!w->processes && !w->exposed)) continue;
+ // this has to be done, before the cleanup
+ struct pid_stat *p = NULL;
+ struct target *w = NULL, *o = NULL;
- fprintf(stdout, "SET %s = %llu\n", w->name, w->num_threads);
- }
- fprintf(stdout, "END\n");
+ // concentrate everything on the apps_groups_targets
+ for(p = root_of_pids; p ; p = p->next) {
- fprintf(stdout, "BEGIN %s.processes %llu\n", type, usec);
- for (w = root; w ; w = w->next) {
- if(w->target || (!w->processes && !w->exposed)) continue;
+ // --------------------------------------------------------------------
+ // apps_groups targets
+ if(likely(p->target))
+ aggregate_pid_on_target(p->target, p, NULL);
+ else
+ error("pid %d %s was left without a target!", p->pid, p->comm);
- fprintf(stdout, "SET %s = %lu\n", w->name, w->processes);
- }
- fprintf(stdout, "END\n");
- fprintf(stdout, "BEGIN %s.mem %llu\n", type, usec);
- for (w = root; w ; w = w->next) {
- if(w->target || (!w->processes && !w->exposed)) continue;
+ // --------------------------------------------------------------------
+ // user targets
+ o = p->user_target;
+ if(likely(p->user_target && p->user_target->uid == p->uid))
+ w = p->user_target;
+ else {
+ if(unlikely(debug && p->user_target))
+ fprintf(stderr, "apps.plugin: \t\tpid %d (%s) switched user from %u (%s) to %u.\n", p->pid, p->comm, p->user_target->uid, p->user_target->name, p->uid);
- fprintf(stdout, "SET %s = %lld\n", w->name, (long long)w->statm_resident - (long long)w->statm_share);
- }
- fprintf(stdout, "END\n");
+ w = p->user_target = get_users_target(p->uid);
+ }
- fprintf(stdout, "BEGIN %s.minor_faults %llu\n", type, usec);
- for (w = root; w ; w = w->next) {
- if(w->target || (!w->processes && !w->exposed)) continue;
+ if(likely(w))
+ aggregate_pid_on_target(w, p, o);
+ else
+ error("pid %d %s was left without a user target!", p->pid, p->comm);
- fprintf(stdout, "SET %s = %llu\n", w->name, w->minflt + w->fix_minflt);
- }
- fprintf(stdout, "END\n");
- fprintf(stdout, "BEGIN %s.major_faults %llu\n", type, usec);
- for (w = root; w ; w = w->next) {
- if(w->target || (!w->processes && !w->exposed)) continue;
+ // --------------------------------------------------------------------
+ // group targets
+ o = p->group_target;
+ if(likely(p->group_target && p->group_target->gid == p->gid))
+ w = p->group_target;
+ else {
+ if(unlikely(debug && p->group_target))
+ fprintf(stderr, "apps.plugin: \t\tpid %d (%s) switched group from %u (%s) to %u.\n", p->pid, p->comm, p->group_target->gid, p->group_target->name, p->gid);
- fprintf(stdout, "SET %s = %llu\n", w->name, w->majflt + w->fix_majflt);
- }
- fprintf(stdout, "END\n");
+ w = p->group_target = get_groups_target(p->gid);
+ }
- fprintf(stdout, "BEGIN %s.lreads %llu\n", type, usec);
- for (w = root; w ; w = w->next) {
- if(w->target || (!w->processes && !w->exposed)) continue;
+ if(likely(w))
+ aggregate_pid_on_target(w, p, o);
+ else
+ error("pid %d %s was left without a group target!", p->pid, p->comm);
- fprintf(stdout, "SET %s = %llu\n", w->name, w->io_logical_bytes_read + w->fix_io_logical_bytes_read);
- }
- fprintf(stdout, "END\n");
+ }
- fprintf(stdout, "BEGIN %s.lwrites %llu\n", type, usec);
- for (w = root; w ; w = w->next) {
- if(w->target || (!w->processes && !w->exposed)) continue;
+ count_targets_fds(apps_groups_root_target);
+ count_targets_fds(users_root_target);
+ count_targets_fds(groups_root_target);
- fprintf(stdout, "SET %s = %llu\n", w->name, w->io_logical_bytes_written + w->fix_io_logical_bytes_written);
- }
- fprintf(stdout, "END\n");
-
- fprintf(stdout, "BEGIN %s.preads %llu\n", type, usec);
- for (w = root; w ; w = w->next) {
- if(w->target || (!w->processes && !w->exposed)) continue;
-
- fprintf(stdout, "SET %s = %llu\n", w->name, w->io_storage_bytes_read + w->fix_io_storage_bytes_read);
- }
- fprintf(stdout, "END\n");
-
- fprintf(stdout, "BEGIN %s.pwrites %llu\n", type, usec);
- for (w = root; w ; w = w->next) {
- if(w->target || (!w->processes && !w->exposed)) continue;
+ cleanup_exited_pids();
+}
- fprintf(stdout, "SET %s = %llu\n", w->name, w->io_storage_bytes_written + w->fix_io_storage_bytes_written);
- }
- fprintf(stdout, "END\n");
+// ----------------------------------------------------------------------------
+// update chart dimensions
- fprintf(stdout, "BEGIN %s.files %llu\n", type, usec);
- for (w = root; w ; w = w->next) {
- if(w->target || (!w->processes && !w->exposed)) continue;
+BUFFER *output = NULL;
+int print_calculated_number(char *str, calculated_number value) { (void)str; (void)value; return 0; }
+
+static inline void send_BEGIN(const char *type, const char *id, unsigned long long usec) {
+ // fprintf(stdout, "BEGIN %s.%s %llu\n", type, id, usec);
+ buffer_strcat(output, "BEGIN ");
+ buffer_strcat(output, type);
+ buffer_strcat(output, ".");
+ buffer_strcat(output, id);
+ buffer_strcat(output, " ");
+ buffer_print_llu(output, usec);
+ buffer_strcat(output, "\n");
+}
- fprintf(stdout, "SET %s = %llu\n", w->name, w->openfiles);
- }
- fprintf(stdout, "END\n");
+static inline void send_SET(const char *name, unsigned long long value) {
+ // fprintf(stdout, "SET %s = %llu\n", name, value);
+ buffer_strcat(output, "SET ");
+ buffer_strcat(output, name);
+ buffer_strcat(output, " = ");
+ buffer_print_llu(output, value);
+ buffer_strcat(output, "\n");
+}
- fprintf(stdout, "BEGIN %s.sockets %llu\n", type, usec);
- for (w = root; w ; w = w->next) {
- if(w->target || (!w->processes && !w->exposed)) continue;
+static inline void send_END(void) {
+ // fprintf(stdout, "END\n");
+ buffer_strcat(output, "END\n");
+}
- fprintf(stdout, "SET %s = %llu\n", w->name, w->opensockets);
- }
- fprintf(stdout, "END\n");
+double utime_fix_ratio = 1.0, stime_fix_ratio = 1.0, gtime_fix_ratio = 1.0, cutime_fix_ratio = 1.0, cstime_fix_ratio = 1.0, cgtime_fix_ratio = 1.0;
+double minflt_fix_ratio = 1.0, majflt_fix_ratio = 1.0, cminflt_fix_ratio = 1.0, cmajflt_fix_ratio = 1.0;
- fprintf(stdout, "BEGIN %s.pipes %llu\n", type, usec);
- for (w = root; w ; w = w->next) {
- if(w->target || (!w->processes && !w->exposed)) continue;
+unsigned long long send_resource_usage_to_netdata() {
+ static struct timeval last = { 0, 0 };
+ static struct rusage me_last;
+
+ struct timeval now;
+ struct rusage me;
+
+ unsigned long long usec;
+ unsigned long long cpuuser;
+ unsigned long long cpusyst;
+
+ if(!last.tv_sec) {
+ gettimeofday(&last, NULL);
+ getrusage(RUSAGE_SELF, &me_last);
+
+ // the first time, give a zero to allow
+ // netdata calibrate to the current time
+ // usec = update_every * 1000000ULL;
+ usec = 0ULL;
+ cpuuser = 0;
+ cpusyst = 0;
+ }
+ else {
+ gettimeofday(&now, NULL);
+ getrusage(RUSAGE_SELF, &me);
+
+ usec = usec_dt(&now, &last);
+ cpuuser = me.ru_utime.tv_sec * 1000000ULL + me.ru_utime.tv_usec;
+ cpusyst = me.ru_stime.tv_sec * 1000000ULL + me.ru_stime.tv_usec;
+
+ bcopy(&now, &last, sizeof(struct timeval));
+ bcopy(&me, &me_last, sizeof(struct rusage));
+ }
+
+ buffer_sprintf(output,
+ "BEGIN netdata.apps_cpu %llu\n"
+ "SET user = %llu\n"
+ "SET system = %llu\n"
+ "END\n"
+ "BEGIN netdata.apps_files %llu\n"
+ "SET files = %llu\n"
+ "SET pids = %ld\n"
+ "SET fds = %d\n"
+ "SET targets = %ld\n"
+ "END\n"
+ "BEGIN netdata.apps_fix %llu\n"
+ "SET utime = %llu\n"
+ "SET stime = %llu\n"
+ "SET gtime = %llu\n"
+ "SET minflt = %llu\n"
+ "SET majflt = %llu\n"
+ "END\n"
+ , usec
+ , cpuuser
+ , cpusyst
+ , usec
+ , file_counter
+ , all_pids_count
+ , all_files_len
+ , apps_groups_targets
+ , usec
+ , (unsigned long long)(utime_fix_ratio * 100 * RATES_DETAIL)
+ , (unsigned long long)(stime_fix_ratio * 100 * RATES_DETAIL)
+ , (unsigned long long)(gtime_fix_ratio * 100 * RATES_DETAIL)
+ , (unsigned long long)(minflt_fix_ratio * 100 * RATES_DETAIL)
+ , (unsigned long long)(majflt_fix_ratio * 100 * RATES_DETAIL)
+ );
+
+ if(include_exited_childs)
+ buffer_sprintf(output,
+ "BEGIN netdata.apps_children_fix %llu\n"
+ "SET cutime = %llu\n"
+ "SET cstime = %llu\n"
+ "SET cgtime = %llu\n"
+ "SET cminflt = %llu\n"
+ "SET cmajflt = %llu\n"
+ "END\n"
+ , usec
+ , (unsigned long long)(cutime_fix_ratio * 100 * RATES_DETAIL)
+ , (unsigned long long)(cstime_fix_ratio * 100 * RATES_DETAIL)
+ , (unsigned long long)(cgtime_fix_ratio * 100 * RATES_DETAIL)
+ , (unsigned long long)(cminflt_fix_ratio * 100 * RATES_DETAIL)
+ , (unsigned long long)(cmajflt_fix_ratio * 100 * RATES_DETAIL)
+ );
+
+ return usec;
+}
- fprintf(stdout, "SET %s = %llu\n", w->name, w->openpipes);
- }
- fprintf(stdout, "END\n");
+void normalize_data(struct target *root) {
+ struct target *w;
+
+ // childs processing introduces spikes
+ // here we try to eliminate them by disabling childs processing either for specific dimensions
+ // or entirely. Of course, either way, we disable it just a single iteration.
+
+ unsigned long long max = processors * hz * RATES_DETAIL;
+ unsigned long long utime = 0, cutime = 0, stime = 0, cstime = 0, gtime = 0, cgtime = 0, minflt = 0, cminflt = 0, majflt = 0, cmajflt = 0;
+
+ if(global_utime > max) global_utime = max;
+ if(global_stime > max) global_stime = max;
+ if(global_gtime > max) global_gtime = max;
+
+ for(w = root; w ; w = w->next) {
+ if(w->target || (!w->processes && !w->exposed)) continue;
+
+ utime += w->utime;
+ stime += w->stime;
+ gtime += w->gtime;
+ cutime += w->cutime;
+ cstime += w->cstime;
+ cgtime += w->cgtime;
+
+ minflt += w->minflt;
+ majflt += w->majflt;
+ cminflt += w->cminflt;
+ cmajflt += w->cmajflt;
+ }
+
+ if((global_utime || global_stime || global_gtime) && (utime || stime || gtime)) {
+ if(global_utime + global_stime + global_gtime > utime + cutime + stime + cstime + gtime + cgtime) {
+ // everything we collected fits
+ utime_fix_ratio =
+ stime_fix_ratio =
+ gtime_fix_ratio =
+ cutime_fix_ratio =
+ cstime_fix_ratio =
+ cgtime_fix_ratio = 1.0; //(double)(global_utime + global_stime) / (double)(utime + cutime + stime + cstime);
+ }
+ else if(global_utime + global_stime > utime + stime) {
+ // childrens resources are too high
+ // lower only the children resources
+ utime_fix_ratio =
+ stime_fix_ratio =
+ gtime_fix_ratio = 1.0;
+ cutime_fix_ratio =
+ cstime_fix_ratio =
+ cgtime_fix_ratio = (double)((global_utime + global_stime) - (utime + stime)) / (double)(cutime + cstime);
+ }
+ else {
+ // even running processes are unrealistic
+ // zero the children resources
+ // lower the running processes resources
+ utime_fix_ratio =
+ stime_fix_ratio =
+ gtime_fix_ratio = (double)(global_utime + global_stime) / (double)(utime + stime);
+ cutime_fix_ratio =
+ cstime_fix_ratio =
+ cgtime_fix_ratio = 0.0;
+ }
+ }
+ else {
+ utime_fix_ratio =
+ stime_fix_ratio =
+ gtime_fix_ratio =
+ cutime_fix_ratio =
+ cstime_fix_ratio =
+ cgtime_fix_ratio = 0.0;
+ }
+
+ if(utime_fix_ratio > 1.0) utime_fix_ratio = 1.0;
+ if(cutime_fix_ratio > 1.0) cutime_fix_ratio = 1.0;
+ if(stime_fix_ratio > 1.0) stime_fix_ratio = 1.0;
+ if(cstime_fix_ratio > 1.0) cstime_fix_ratio = 1.0;
+ if(gtime_fix_ratio > 1.0) gtime_fix_ratio = 1.0;
+ if(cgtime_fix_ratio > 1.0) cgtime_fix_ratio = 1.0;
+
+ // if(utime_fix_ratio < 0.0) utime_fix_ratio = 0.0;
+ // if(cutime_fix_ratio < 0.0) cutime_fix_ratio = 0.0;
+ // if(stime_fix_ratio < 0.0) stime_fix_ratio = 0.0;
+ // if(cstime_fix_ratio < 0.0) cstime_fix_ratio = 0.0;
+ // if(gtime_fix_ratio < 0.0) gtime_fix_ratio = 0.0;
+ // if(cgtime_fix_ratio < 0.0) cgtime_fix_ratio = 0.0;
+
+ // FIXME
+ // we use cpu time to normalize page faults
+ // the problem is that to find the proper max values
+ // for page faults we have to parse /proc/vmstat
+ // which is quite big to do it again (netdata does it already)
+ //
+ // a better solution could be to somehow have netdata
+ // do this normalization for us
+
+ if(utime || stime || gtime)
+ majflt_fix_ratio =
+ minflt_fix_ratio = (double)(utime * utime_fix_ratio + stime * stime_fix_ratio + gtime * gtime_fix_ratio) / (double)(utime + stime + gtime);
+ else
+ minflt_fix_ratio =
+ majflt_fix_ratio = 1.0;
+
+ if(cutime || cstime || cgtime)
+ cmajflt_fix_ratio =
+ cminflt_fix_ratio = (double)(cutime * cutime_fix_ratio + cstime * cstime_fix_ratio + cgtime * cgtime_fix_ratio) / (double)(cutime + cstime + cgtime);
+ else
+ cminflt_fix_ratio =
+ cmajflt_fix_ratio = 1.0;
+
+ // the report
+
+ if(unlikely(debug)) {
+ fprintf(stderr,
+ "SYSTEM: u=%llu s=%llu g=%llu "
+ "COLLECTED: u=%llu s=%llu g=%llu cu=%llu cs=%llu cg=%llu "
+ "DELTA: u=%lld s=%lld g=%lld "
+ "FIX: u=%0.2f s=%0.2f g=%0.2f cu=%0.2f cs=%0.2f cg=%0.2f "
+ "FINALLY: u=%llu s=%llu g=%llu cu=%llu cs=%llu cg=%llu "
+ "\n"
+ , global_utime
+ , global_stime
+ , global_gtime
+ , utime
+ , stime
+ , gtime
+ , cutime
+ , cstime
+ , cgtime
+ , (long long)utime + (long long)cutime - (long long)global_utime
+ , (long long)stime + (long long)cstime - (long long)global_stime
+ , (long long)gtime + (long long)cgtime - (long long)global_gtime
+ , utime_fix_ratio
+ , stime_fix_ratio
+ , gtime_fix_ratio
+ , cutime_fix_ratio
+ , cstime_fix_ratio
+ , cgtime_fix_ratio
+ , (unsigned long long)(utime * utime_fix_ratio)
+ , (unsigned long long)(stime * stime_fix_ratio)
+ , (unsigned long long)(gtime * gtime_fix_ratio)
+ , (unsigned long long)(cutime * cutime_fix_ratio)
+ , (unsigned long long)(cstime * cstime_fix_ratio)
+ , (unsigned long long)(cgtime * cgtime_fix_ratio)
+ );
+ }
+}
- fflush(stdout);
+void send_collected_data_to_netdata(struct target *root, const char *type, unsigned long long usec) {
+ struct target *w;
+
+ send_BEGIN(type, "cpu", usec);
+ for (w = root; w ; w = w->next) {
+ if(unlikely(w->exposed))
+ send_SET(w->name, (unsigned long long)(w->utime * utime_fix_ratio) + (unsigned long long)(w->stime * stime_fix_ratio) + (unsigned long long)(w->gtime * gtime_fix_ratio) + (include_exited_childs?((unsigned long long)(w->cutime * cutime_fix_ratio) + (unsigned long long)(w->cstime * cstime_fix_ratio) + (unsigned long long)(w->cgtime * cgtime_fix_ratio)):0ULL));
+ }
+ send_END();
+
+ send_BEGIN(type, "cpu_user", usec);
+ for (w = root; w ; w = w->next) {
+ if(unlikely(w->exposed))
+ send_SET(w->name, (unsigned long long)(w->utime * utime_fix_ratio) + (include_exited_childs?((unsigned long long)(w->cutime * cutime_fix_ratio)):0ULL));
+ }
+ send_END();
+
+ send_BEGIN(type, "cpu_system", usec);
+ for (w = root; w ; w = w->next) {
+ if(unlikely(w->exposed))
+ send_SET(w->name, (unsigned long long)(w->stime * stime_fix_ratio) + (include_exited_childs?((unsigned long long)(w->cstime * cstime_fix_ratio)):0ULL));
+ }
+ send_END();
+
+ if(show_guest_time) {
+ send_BEGIN(type, "cpu_guest", usec);
+ for (w = root; w ; w = w->next) {
+ if(unlikely(w->exposed))
+ send_SET(w->name, (unsigned long long)(w->gtime * gtime_fix_ratio) + (include_exited_childs?((unsigned long long)(w->cgtime * cgtime_fix_ratio)):0ULL));
+ }
+ send_END();
+ }
+
+ send_BEGIN(type, "threads", usec);
+ for (w = root; w ; w = w->next) {
+ if(unlikely(w->exposed))
+ send_SET(w->name, w->num_threads);
+ }
+ send_END();
+
+ send_BEGIN(type, "processes", usec);
+ for (w = root; w ; w = w->next) {
+ if(unlikely(w->exposed))
+ send_SET(w->name, w->processes);
+ }
+ send_END();
+
+ send_BEGIN(type, "mem", usec);
+ for (w = root; w ; w = w->next) {
+ if(unlikely(w->exposed))
+ send_SET(w->name, (w->statm_resident > w->statm_share)?(w->statm_resident - w->statm_share):0ULL);
+ }
+ send_END();
+
+ send_BEGIN(type, "minor_faults", usec);
+ for (w = root; w ; w = w->next) {
+ if(unlikely(w->exposed))
+ send_SET(w->name, (unsigned long long)(w->minflt * minflt_fix_ratio) + (include_exited_childs?((unsigned long long)(w->cminflt * cminflt_fix_ratio)):0ULL));
+ }
+ send_END();
+
+ send_BEGIN(type, "major_faults", usec);
+ for (w = root; w ; w = w->next) {
+ if(unlikely(w->exposed))
+ send_SET(w->name, (unsigned long long)(w->majflt * majflt_fix_ratio) + (include_exited_childs?((unsigned long long)(w->cmajflt * cmajflt_fix_ratio)):0ULL));
+ }
+ send_END();
+
+ send_BEGIN(type, "lreads", usec);
+ for (w = root; w ; w = w->next) {
+ if(unlikely(w->exposed))
+ send_SET(w->name, w->io_logical_bytes_read);
+ }
+ send_END();
+
+ send_BEGIN(type, "lwrites", usec);
+ for (w = root; w ; w = w->next) {
+ if(unlikely(w->exposed))
+ send_SET(w->name, w->io_logical_bytes_written);
+ }
+ send_END();
+
+ send_BEGIN(type, "preads", usec);
+ for (w = root; w ; w = w->next) {
+ if(unlikely(w->exposed))
+ send_SET(w->name, w->io_storage_bytes_read);
+ }
+ send_END();
+
+ send_BEGIN(type, "pwrites", usec);
+ for (w = root; w ; w = w->next) {
+ if(unlikely(w->exposed))
+ send_SET(w->name, w->io_storage_bytes_written);
+ }
+ send_END();
+
+ if(enable_file_charts) {
+ send_BEGIN(type, "files", usec);
+ for (w = root; w; w = w->next) {
+ if (unlikely(w->exposed))
+ send_SET(w->name, w->openfiles);
+ }
+ send_END();
+
+ send_BEGIN(type, "sockets", usec);
+ for (w = root; w; w = w->next) {
+ if (unlikely(w->exposed))
+ send_SET(w->name, w->opensockets);
+ }
+ send_END();
+
+ send_BEGIN(type, "pipes", usec);
+ for (w = root; w; w = w->next) {
+ if (unlikely(w->exposed))
+ send_SET(w->name, w->openpipes);
+ }
+ send_END();
+ }
}
@@ -2217,125 +2560,126 @@ void send_collected_data_to_netdata(struct target *root, const char *type, unsig
void send_charts_updates_to_netdata(struct target *root, const char *type, const char *title)
{
- struct target *w;
- int newly_added = 0;
-
- for(w = root ; w ; w = w->next)
- if(!w->exposed && w->processes) {
- newly_added++;
- w->exposed = 1;
- if(debug || w->debug) fprintf(stderr, "apps.plugin: %s just added - regenerating charts.\n", w->name);
- }
-
- // nothing more to show
- if(!newly_added) return;
-
- // we have something new to show
- // update the charts
- fprintf(stdout, "CHART %s.cpu '' '%s CPU Time (%ld%% = %ld core%s)' 'cpu time %%' cpu %s.cpu stacked 20001 %d\n", type, title, (processors * 100), processors, (processors>1)?"s":"", type, update_every);
- for (w = root; w ; w = w->next) {
- if(w->target || (!w->processes && !w->exposed)) continue;
-
- fprintf(stdout, "DIMENSION %s '' incremental 100 %u %s\n", w->name, hz, w->hidden ? "hidden,noreset" : "noreset");
- }
-
- fprintf(stdout, "CHART %s.mem '' '%s Dedicated Memory (w/o shared)' 'MB' mem %s.mem stacked 20003 %d\n", type, title, type, update_every);
- for (w = root; w ; w = w->next) {
- if(w->target || (!w->processes && !w->exposed)) continue;
-
- fprintf(stdout, "DIMENSION %s '' absolute %ld %ld noreset\n", w->name, sysconf(_SC_PAGESIZE), 1024L*1024L);
- }
-
- fprintf(stdout, "CHART %s.threads '' '%s Threads' 'threads' processes %s.threads stacked 20005 %d\n", type, title, type, update_every);
- for (w = root; w ; w = w->next) {
- if(w->target || (!w->processes && !w->exposed)) continue;
-
- fprintf(stdout, "DIMENSION %s '' absolute 1 1 noreset\n", w->name);
- }
-
- fprintf(stdout, "CHART %s.processes '' '%s Processes' 'processes' processes %s.processes stacked 20004 %d\n", type, title, type, update_every);
- for (w = root; w ; w = w->next) {
- if(w->target || (!w->processes && !w->exposed)) continue;
-
- fprintf(stdout, "DIMENSION %s '' absolute 1 1 noreset\n", w->name);
- }
-
- fprintf(stdout, "CHART %s.cpu_user '' '%s CPU User Time (%ld%% = %ld core%s)' 'cpu time %%' cpu %s.cpu_user stacked 20020 %d\n", type, title, (processors * 100), processors, (processors>1)?"s":"", type, update_every);
- for (w = root; w ; w = w->next) {
- if(w->target || (!w->processes && !w->exposed)) continue;
-
- fprintf(stdout, "DIMENSION %s '' incremental 100 %ld noreset\n", w->name, hz * processors);
- }
-
- fprintf(stdout, "CHART %s.cpu_system '' '%s CPU System Time (%ld%% = %ld core%s)' 'cpu time %%' cpu %s.cpu_system stacked 20021 %d\n", type, title, (processors * 100), processors, (processors>1)?"s":"", type, update_every);
- for (w = root; w ; w = w->next) {
- if(w->target || (!w->processes && !w->exposed)) continue;
-
- fprintf(stdout, "DIMENSION %s '' incremental 100 %ld noreset\n", w->name, hz * processors);
- }
-
- fprintf(stdout, "CHART %s.major_faults '' '%s Major Page Faults (swap read)' 'page faults/s' swap %s.major_faults stacked 20010 %d\n", type, title, type, update_every);
- for (w = root; w ; w = w->next) {
- if(w->target || (!w->processes && !w->exposed)) continue;
-
- fprintf(stdout, "DIMENSION %s '' incremental 1 1 noreset\n", w->name);
- }
-
- fprintf(stdout, "CHART %s.minor_faults '' '%s Minor Page Faults' 'page faults/s' mem %s.minor_faults stacked 20011 %d\n", type, title, type, update_every);
- for (w = root; w ; w = w->next) {
- if(w->target || (!w->processes && !w->exposed)) continue;
-
- fprintf(stdout, "DIMENSION %s '' incremental 1 1 noreset\n", w->name);
- }
-
- fprintf(stdout, "CHART %s.lreads '' '%s Disk Logical Reads' 'kilobytes/s' disk %s.lreads stacked 20042 %d\n", type, title, type, update_every);
- for (w = root; w ; w = w->next) {
- if(w->target || (!w->processes && !w->exposed)) continue;
-
- fprintf(stdout, "DIMENSION %s '' incremental 1 %d noreset\n", w->name, 1024);
- }
-
- fprintf(stdout, "CHART %s.lwrites '' '%s I/O Logical Writes' 'kilobytes/s' disk %s.lwrites stacked 20042 %d\n", type, title, type, update_every);
- for (w = root; w ; w = w->next) {
- if(w->target || (!w->processes && !w->exposed)) continue;
-
- fprintf(stdout, "DIMENSION %s '' incremental 1 %d noreset\n", w->name, 1024);
- }
-
- fprintf(stdout, "CHART %s.preads '' '%s Disk Reads' 'kilobytes/s' disk %s.preads stacked 20002 %d\n", type, title, type, update_every);
- for (w = root; w ; w = w->next) {
- if(w->target || (!w->processes && !w->exposed)) continue;
-
- fprintf(stdout, "DIMENSION %s '' incremental 1 %d noreset\n", w->name, 1024);
- }
-
- fprintf(stdout, "CHART %s.pwrites '' '%s Disk Writes' 'kilobytes/s' disk %s.pwrites stacked 20002 %d\n", type, title, type, update_every);
- for (w = root; w ; w = w->next) {
- if(w->target || (!w->processes && !w->exposed)) continue;
-
- fprintf(stdout, "DIMENSION %s '' incremental 1 %d noreset\n", w->name, 1024);
- }
-
- fprintf(stdout, "CHART %s.files '' '%s Open Files' 'open files' disk %s.files stacked 20050 %d\n", type, title, type, update_every);
- for (w = root; w ; w = w->next) {
- if(w->target || (!w->processes && !w->exposed)) continue;
-
- fprintf(stdout, "DIMENSION %s '' absolute 1 1 noreset\n", w->name);
- }
-
- fprintf(stdout, "CHART %s.sockets '' '%s Open Sockets' 'open sockets' net %s.sockets stacked 20051 %d\n", type, title, type, update_every);
- for (w = root; w ; w = w->next) {
- if(w->target || (!w->processes && !w->exposed)) continue;
-
- fprintf(stdout, "DIMENSION %s '' absolute 1 1 noreset\n", w->name);
- }
-
- fprintf(stdout, "CHART %s.pipes '' '%s Pipes' 'open pipes' processes %s.pipes stacked 20053 %d\n", type, title, type, update_every);
- for (w = root; w ; w = w->next) {
- if(w->target || (!w->processes && !w->exposed)) continue;
-
- fprintf(stdout, "DIMENSION %s '' absolute 1 1 noreset\n", w->name);
- }
+ struct target *w;
+ int newly_added = 0;
+
+ for(w = root ; w ; w = w->next) {
+ if (w->target) continue;
+
+ if (!w->exposed && w->processes) {
+ newly_added++;
+ w->exposed = 1;
+ if (debug || w->debug) fprintf(stderr, "apps.plugin: %s just added - regenerating charts.\n", w->name);
+ }
+ }
+
+ // nothing more to show
+ if(!newly_added && show_guest_time == show_guest_time_old) return;
+
+ // we have something new to show
+ // update the charts
+ buffer_sprintf(output, "CHART %s.cpu '' '%s CPU Time (%d%% = %d core%s)' 'cpu time %%' cpu %s.cpu stacked 20001 %d\n", type, title, (processors * 100), processors, (processors>1)?"s":"", type, update_every);
+ for (w = root; w ; w = w->next) {
+ if(unlikely(w->exposed))
+ buffer_sprintf(output, "DIMENSION %s '' absolute 1 %llu %s\n", w->name, hz * RATES_DETAIL / 100, w->hidden ? "hidden" : "");
+ }
+
+ buffer_sprintf(output, "CHART %s.mem '' '%s Dedicated Memory (w/o shared)' 'MB' mem %s.mem stacked 20003 %d\n", type, title, type, update_every);
+ for (w = root; w ; w = w->next) {
+ if(unlikely(w->exposed))
+ buffer_sprintf(output, "DIMENSION %s '' absolute %ld %ld\n", w->name, sysconf(_SC_PAGESIZE), 1024L*1024L);
+ }
+
+ buffer_sprintf(output, "CHART %s.threads '' '%s Threads' 'threads' processes %s.threads stacked 20005 %d\n", type, title, type, update_every);
+ for (w = root; w ; w = w->next) {
+ if(unlikely(w->exposed))
+ buffer_sprintf(output, "DIMENSION %s '' absolute 1 1\n", w->name);
+ }
+
+ buffer_sprintf(output, "CHART %s.processes '' '%s Processes' 'processes' processes %s.processes stacked 20004 %d\n", type, title, type, update_every);
+ for (w = root; w ; w = w->next) {
+ if(unlikely(w->exposed))
+ buffer_sprintf(output, "DIMENSION %s '' absolute 1 1\n", w->name);
+ }
+
+ buffer_sprintf(output, "CHART %s.cpu_user '' '%s CPU User Time (%d%% = %d core%s)' 'cpu time %%' cpu %s.cpu_user stacked 20020 %d\n", type, title, (processors * 100), processors, (processors>1)?"s":"", type, update_every);
+ for (w = root; w ; w = w->next) {
+ if(unlikely(w->exposed))
+ buffer_sprintf(output, "DIMENSION %s '' absolute 1 %llu\n", w->name, hz * RATES_DETAIL / 100LLU);
+ }
+
+ buffer_sprintf(output, "CHART %s.cpu_system '' '%s CPU System Time (%d%% = %d core%s)' 'cpu time %%' cpu %s.cpu_system stacked 20021 %d\n", type, title, (processors * 100), processors, (processors>1)?"s":"", type, update_every);
+ for (w = root; w ; w = w->next) {
+ if(unlikely(w->exposed))
+ buffer_sprintf(output, "DIMENSION %s '' absolute 1 %llu\n", w->name, hz * RATES_DETAIL / 100LLU);
+ }
+
+ if(show_guest_time) {
+ buffer_sprintf(output, "CHART %s.cpu_guest '' '%s CPU Guest Time (%d%% = %d core%s)' 'cpu time %%' cpu %s.cpu_system stacked 20022 %d\n", type, title, (processors * 100), processors, (processors > 1) ? "s" : "", type, update_every);
+ for (w = root; w; w = w->next) {
+ if(unlikely(w->exposed))
+ buffer_sprintf(output, "DIMENSION %s '' absolute 1 %llu\n", w->name, hz * RATES_DETAIL / 100LLU);
+ }
+ }
+
+ buffer_sprintf(output, "CHART %s.major_faults '' '%s Major Page Faults (swap read)' 'page faults/s' swap %s.major_faults stacked 20010 %d\n", type, title, type, update_every);
+ for (w = root; w ; w = w->next) {
+ if(unlikely(w->exposed))
+ buffer_sprintf(output, "DIMENSION %s '' absolute 1 %llu\n", w->name, RATES_DETAIL);
+ }
+
+ buffer_sprintf(output, "CHART %s.minor_faults '' '%s Minor Page Faults' 'page faults/s' mem %s.minor_faults stacked 20011 %d\n", type, title, type, update_every);
+ for (w = root; w ; w = w->next) {
+ if(unlikely(w->exposed))
+ buffer_sprintf(output, "DIMENSION %s '' absolute 1 %llu\n", w->name, RATES_DETAIL);
+ }
+
+ buffer_sprintf(output, "CHART %s.lreads '' '%s Disk Logical Reads' 'kilobytes/s' disk %s.lreads stacked 20042 %d\n", type, title, type, update_every);
+ for (w = root; w ; w = w->next) {
+ if(unlikely(w->exposed))
+ buffer_sprintf(output, "DIMENSION %s '' absolute 1 %llu\n", w->name, 1024LLU * RATES_DETAIL);
+ }
+
+ buffer_sprintf(output, "CHART %s.lwrites '' '%s I/O Logical Writes' 'kilobytes/s' disk %s.lwrites stacked 20042 %d\n", type, title, type, update_every);
+ for (w = root; w ; w = w->next) {
+ if(unlikely(w->exposed))
+ buffer_sprintf(output, "DIMENSION %s '' absolute 1 %llu\n", w->name, 1024LLU * RATES_DETAIL);
+ }
+
+ buffer_sprintf(output, "CHART %s.preads '' '%s Disk Reads' 'kilobytes/s' disk %s.preads stacked 20002 %d\n", type, title, type, update_every);
+ for (w = root; w ; w = w->next) {
+ if(unlikely(w->exposed))
+ buffer_sprintf(output, "DIMENSION %s '' absolute 1 %llu\n", w->name, 1024LLU * RATES_DETAIL);
+ }
+
+ buffer_sprintf(output, "CHART %s.pwrites '' '%s Disk Writes' 'kilobytes/s' disk %s.pwrites stacked 20002 %d\n", type, title, type, update_every);
+ for (w = root; w ; w = w->next) {
+ if(unlikely(w->exposed))
+ buffer_sprintf(output, "DIMENSION %s '' absolute 1 %llu\n", w->name, 1024LLU * RATES_DETAIL);
+ }
+
+ if(enable_file_charts) {
+ buffer_sprintf(output, "CHART %s.files '' '%s Open Files' 'open files' disk %s.files stacked 20050 %d\n", type,
+ title, type, update_every);
+ for (w = root; w; w = w->next) {
+ if (unlikely(w->exposed))
+ buffer_sprintf(output, "DIMENSION %s '' absolute 1 1\n", w->name);
+ }
+
+ buffer_sprintf(output, "CHART %s.sockets '' '%s Open Sockets' 'open sockets' net %s.sockets stacked 20051 %d\n",
+ type, title, type, update_every);
+ for (w = root; w; w = w->next) {
+ if (unlikely(w->exposed))
+ buffer_sprintf(output, "DIMENSION %s '' absolute 1 1\n", w->name);
+ }
+
+ buffer_sprintf(output, "CHART %s.pipes '' '%s Pipes' 'open pipes' processes %s.pipes stacked 20053 %d\n", type,
+ title, type, update_every);
+ for (w = root; w; w = w->next) {
+ if (unlikely(w->exposed))
+ buffer_sprintf(output, "DIMENSION %s '' absolute 1 1\n", w->name);
+ }
+ }
}
@@ -2344,152 +2688,245 @@ void send_charts_updates_to_netdata(struct target *root, const char *type, const
void parse_args(int argc, char **argv)
{
- int i, freq = 0;
- char *name = NULL;
-
- for(i = 1; i < argc; i++) {
- if(!freq) {
- int n = atoi(argv[i]);
- if(n > 0) {
- freq = n;
- continue;
- }
- }
-
- if(strcmp("debug", argv[i]) == 0) {
- debug = 1;
- debug_flags = 0xffffffff;
- continue;
- }
-
- if(!name) {
- name = argv[i];
- continue;
- }
-
- error("Cannot understand option %s", argv[i]);
- exit(1);
- }
-
- if(freq > 0) update_every = freq;
- if(!name) name = "groups";
-
- if(read_apps_groups_conf(name)) {
- error("Cannot read process groups %s", name);
- exit(1);
- }
+ int i, freq = 0;
+ char *name = NULL;
+
+ for(i = 1; i < argc; i++) {
+ if(!freq) {
+ int n = atoi(argv[i]);
+ if(n > 0) {
+ freq = n;
+ continue;
+ }
+ }
+
+ if(strcmp("debug", argv[i]) == 0) {
+ debug = 1;
+ // debug_flags = 0xffffffff;
+ continue;
+ }
+
+ if(strcmp("no-childs", argv[i]) == 0 || strcmp("without-childs", argv[i]) == 0) {
+ include_exited_childs = 0;
+ continue;
+ }
+
+ if(strcmp("with-childs", argv[i]) == 0) {
+ include_exited_childs = 1;
+ continue;
+ }
+
+ if(strcmp("with-guest", argv[i]) == 0) {
+ enable_guest_charts = 1;
+ continue;
+ }
+
+ if(strcmp("no-guest", argv[i]) == 0 || strcmp("without-guest", argv[i]) == 0) {
+ enable_guest_charts = 0;
+ continue;
+ }
+
+ if(strcmp("with-files", argv[i]) == 0) {
+ enable_file_charts = 1;
+ continue;
+ }
+
+ if(strcmp("no-files", argv[i]) == 0 || strcmp("without-files", argv[i]) == 0) {
+ enable_file_charts = 0;
+ continue;
+ }
+
+ if(strcmp("-h", argv[i]) == 0 || strcmp("--help", argv[i]) == 0) {
+ fprintf(stderr,
+ "apps.plugin\n"
+ "(C) 2016 Costa Tsaousis"
+ "GPL v3+\n"
+ "This program is a data collector plugin for netdata.\n"
+ "\n"
+ "Valid command line options:\n"
+ "\n"
+ "SECONDS set the data collection frequency\n"
+ "\n"
+ "debug enable debugging (lot of output)\n"
+ "\n"
+ "with-childs\n"
+ "without-childs enable / disable aggregating exited\n"
+ " children resources into parents\n"
+ " (default is enabled)\n"
+ "\n"
+ "with-guest\n"
+ "without-guest enable / disable reporting guest charts\n"
+ " (default is disabled)\n"
+ "\n"
+ "with-files\n"
+ "without-files enable / disable reporting files, sockets, pipes\n"
+ " (default is enabled)\n"
+ "\n"
+ "NAME read apps_NAME.conf instead of\n"
+ " apps_groups.conf\n"
+ " (default NAME=groups)\n"
+ );
+ exit(1);
+ }
+
+ if(!name) {
+ name = argv[i];
+ continue;
+ }
+
+ error("Cannot understand option %s", argv[i]);
+ exit(1);
+ }
+
+ if(freq > 0) update_every = freq;
+ if(!name) name = "groups";
+
+ if(read_apps_groups_conf(name)) {
+ error("Cannot read process groups %s", name);
+ exit(1);
+ }
}
int main(int argc, char **argv)
{
- // debug_flags = D_PROCFILE;
+ // debug_flags = D_PROCFILE;
+
+ // set the name for logging
+ program_name = "apps.plugin";
- // set the name for logging
- program_name = "apps.plugin";
+ info("started on pid %d", getpid());
- // disable syslog for apps.plugin
- error_log_syslog = 0;
+ // disable syslog for apps.plugin
+ error_log_syslog = 0;
- // set errors flood protection to 100 logs per hour
- error_log_errors_per_period = 100;
- error_log_throttle_period = 3600;
+ // set errors flood protection to 100 logs per hour
+ error_log_errors_per_period = 100;
+ error_log_throttle_period = 3600;
- host_prefix = getenv("NETDATA_HOST_PREFIX");
- if(host_prefix == NULL) {
- info("NETDATA_HOST_PREFIX is not passed from netdata");
- host_prefix = "";
- }
- else info("Found NETDATA_HOST_PREFIX='%s'", host_prefix);
+ host_prefix = getenv("NETDATA_HOST_PREFIX");
+ if(host_prefix == NULL) {
+ // info("NETDATA_HOST_PREFIX is not passed from netdata");
+ host_prefix = "";
+ }
+ // else info("Found NETDATA_HOST_PREFIX='%s'", host_prefix);
- config_dir = getenv("NETDATA_CONFIG_DIR");
- if(config_dir == NULL) {
- info("NETDATA_CONFIG_DIR is not passed from netdata");
- config_dir = CONFIG_DIR;
- }
- else info("Found NETDATA_CONFIG_DIR='%s'", config_dir);
+ config_dir = getenv("NETDATA_CONFIG_DIR");
+ if(config_dir == NULL) {
+ // info("NETDATA_CONFIG_DIR is not passed from netdata");
+ config_dir = CONFIG_DIR;
+ }
+ // else info("Found NETDATA_CONFIG_DIR='%s'", config_dir);
#ifdef NETDATA_INTERNAL_CHECKS
- if(debug_flags != 0) {
- struct rlimit rl = { RLIM_INFINITY, RLIM_INFINITY };
- if(setrlimit(RLIMIT_CORE, &rl) != 0)
- info("Cannot request unlimited core dumps for debugging... Proceeding anyway...");
- prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
- }
+ if(debug_flags != 0) {
+ struct rlimit rl = { RLIM_INFINITY, RLIM_INFINITY };
+ if(setrlimit(RLIMIT_CORE, &rl) != 0)
+ info("Cannot request unlimited core dumps for debugging... Proceeding anyway...");
+ prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
+ }
#endif /* NETDATA_INTERNAL_CHECKS */
- info("starting...");
-
- procfile_adaptive_initial_allocation = 1;
-
- time_t started_t = time(NULL);
- time_t current_t;
- get_HZ();
- pid_max = get_system_pid_max();
- processors = get_system_cpus();
-
- parse_args(argc, argv);
-
- all_pids = calloc(sizeof(struct pid_stat *), (size_t) pid_max);
- if(!all_pids) {
- error("Cannot allocate %lu bytes of memory.", sizeof(struct pid_stat *) * pid_max);
- printf("DISABLE\n");
- exit(1);
- }
-
- fprintf(stdout, "CHART netdata.apps_cpu '' 'Apps Plugin CPU' 'milliseconds/s' apps.plugin netdata.apps_cpu stacked 140000 %1$d\n"
- "DIMENSION user '' incremental 1 1000\n"
- "DIMENSION system '' incremental 1 1000\n"
- "CHART netdata.apps_files '' 'Apps Plugin Files' 'files/s' apps.plugin netdata.apps_files line 140001 %1$d\n"
- "DIMENSION files '' incremental 1 1\n"
- "DIMENSION pids '' absolute 1 1\n"
- "DIMENSION fds '' absolute 1 1\n"
- "DIMENSION targets '' absolute 1 1\n", update_every);
+ procfile_adaptive_initial_allocation = 1;
+
+ time_t started_t = time(NULL);
+ time_t current_t;
+ get_HZ();
+ pid_max = get_system_pid_max();
+ processors = get_system_cpus();
+
+ parse_args(argc, argv);
+
+ all_pids_sortlist = callocz(sizeof(pid_t), (size_t)pid_max);
+ all_pids = callocz(sizeof(struct pid_stat *), (size_t) pid_max);
+
+ output = buffer_create(1024);
+ buffer_sprintf(output,
+ "CHART netdata.apps_cpu '' 'Apps Plugin CPU' 'milliseconds/s' apps.plugin netdata.apps_cpu stacked 140000 %1$d\n"
+ "DIMENSION user '' incremental 1 1000\n"
+ "DIMENSION system '' incremental 1 1000\n"
+ "CHART netdata.apps_files '' 'Apps Plugin Files' 'files/s' apps.plugin netdata.apps_files line 140001 %1$d\n"
+ "DIMENSION files '' incremental 1 1\n"
+ "DIMENSION pids '' absolute 1 1\n"
+ "DIMENSION fds '' absolute 1 1\n"
+ "DIMENSION targets '' absolute 1 1\n"
+ "CHART netdata.apps_fix '' 'Apps Plugin Normalization Ratios' 'percentage' apps.plugin netdata.apps_fix line 140002 %1$d\n"
+ "DIMENSION utime '' absolute 1 %2$llu\n"
+ "DIMENSION stime '' absolute 1 %2$llu\n"
+ "DIMENSION gtime '' absolute 1 %2$llu\n"
+ "DIMENSION minflt '' absolute 1 %2$llu\n"
+ "DIMENSION majflt '' absolute 1 %2$llu\n"
+ , update_every
+ , RATES_DETAIL
+ );
+
+ if(include_exited_childs)
+ buffer_sprintf(output,
+ "CHART netdata.apps_children_fix '' 'Apps Plugin Exited Children Normalization Ratios' 'percentage' apps.plugin netdata.apps_children_fix line 140003 %1$d\n"
+ "DIMENSION cutime '' absolute 1 %2$llu\n"
+ "DIMENSION cstime '' absolute 1 %2$llu\n"
+ "DIMENSION cgtime '' absolute 1 %2$llu\n"
+ "DIMENSION cminflt '' absolute 1 %2$llu\n"
+ "DIMENSION cmajflt '' absolute 1 %2$llu\n"
+ , update_every
+ , RATES_DETAIL
+ );
#ifndef PROFILING_MODE
- unsigned long long sunext = (time(NULL) - (time(NULL) % update_every) + update_every) * 1000000ULL;
- unsigned long long sunow;
+ unsigned long long sunext = (time(NULL) - (time(NULL) % update_every) + update_every) * 1000000ULL;
+ unsigned long long sunow;
#endif /* PROFILING_MODE */
- unsigned long long counter = 1;
- for(;1; counter++) {
+ global_iterations_counter = 1;
+ for(;1; global_iterations_counter++) {
#ifndef PROFILING_MODE
- // delay until it is our time to run
- while((sunow = timems()) < sunext)
- usleep((useconds_t)(sunext - sunow));
+ // delay until it is our time to run
+ while((sunow = time_usec()) < sunext)
+ sleep_usec(sunext - sunow);
- // find the next time we need to run
- while(timems() > sunext)
- sunext += update_every * 1000000ULL;
+ // find the next time we need to run
+ while(time_usec() > sunext)
+ sunext += update_every * 1000000ULL;
#endif /* PROFILING_MODE */
- if(!collect_data_for_all_processes_from_proc()) {
- error("Cannot collect /proc data for running processes. Disabling apps.plugin...");
- printf("DISABLE\n");
- exit(1);
- }
+ if(!collect_data_for_all_processes_from_proc()) {
+ error("Cannot collect /proc data for running processes. Disabling apps.plugin...");
+ printf("DISABLE\n");
+ exit(1);
+ }
+
+ calculate_netdata_statistics();
+ normalize_data(apps_groups_root_target);
+
+ unsigned long long dt = send_resource_usage_to_netdata();
+
+ // this is smart enough to show only newly added apps, when needed
+ send_charts_updates_to_netdata(apps_groups_root_target, "apps", "Apps");
+ send_charts_updates_to_netdata(users_root_target, "users", "Users");
+ send_charts_updates_to_netdata(groups_root_target, "groups", "User Groups");
- calculate_netdata_statistics();
+ send_collected_data_to_netdata(apps_groups_root_target, "apps", dt);
+ send_collected_data_to_netdata(users_root_target, "users", dt);
+ send_collected_data_to_netdata(groups_root_target, "groups", dt);
- unsigned long long dt = send_resource_usage_to_netdata();
+ show_guest_time_old = show_guest_time;
- // this is smart enough to show only newly added apps, when needed
- send_charts_updates_to_netdata(apps_groups_root_target, "apps", "Apps");
- send_charts_updates_to_netdata(users_root_target, "users", "Users");
- send_charts_updates_to_netdata(groups_root_target, "groups", "User Groups");
+ //if(puts(buffer_tostring(output)) == EOF)
+ if(write(STDOUT_FILENO, buffer_tostring(output), buffer_strlen(output)) == -1)
+ fatal("Cannot send chart values to netdata.");
- send_collected_data_to_netdata(apps_groups_root_target, "apps", dt);
- send_collected_data_to_netdata(users_root_target, "users", dt);
- send_collected_data_to_netdata(groups_root_target, "groups", dt);
+ // fflush(stdout);
+ buffer_flush(output);
- if(debug) fprintf(stderr, "apps.plugin: done Loop No %llu\n", counter);
+ if(unlikely(debug))
+ fprintf(stderr, "apps.plugin: done Loop No %llu\n", global_iterations_counter);
- current_t = time(NULL);
+ current_t = time(NULL);
#ifndef PROFILING_MODE
- // restart check (14400 seconds)
- if(current_t - started_t > 14400) exit(0);
+ // restart check (14400 seconds)
+ if(current_t - started_t > 14400) exit(0);
#else
- if(current_t - started_t > 10) exit(0);
+ if(current_t - started_t > 10) exit(0);
#endif /* PROFILING_MODE */
- }
+ }
}
diff --git a/src/avl.c b/src/avl.c
index 067b0b361..324afeebb 100644
--- a/src/avl.c
+++ b/src/avl.c
@@ -1,350 +1,311 @@
+#include "common.h"
+
+/* ------------------------------------------------------------------------- */
/*
- * ANSI C Library for maintainance of AVL Balanced Trees
- *
- * ref.:
- * G. M. Adelson-Velskij & E. M. Landis
- * Doklady Akad. Nauk SSSR 146 (1962), 263-266
- *
- * see also:
- * D. E. Knuth: The Art of Computer Programming Vol.3 (Sorting and Searching)
+ * avl_insert(), avl_remove() and avl_search()
+ * are adaptations (by Costa Tsaousis) of the AVL algorithm found in libavl
+ * v2.0.3, so that they do not use any memory allocations and their memory
+ * footprint is optimized (by eliminating non-necessary data members).
*
- * (C) 2000 Daniel Nagy, Budapest University of Technology and Economics
- * Released under GNU General Public License (GPL) version 2
- *
- */
+ * libavl - library for manipulation of binary trees.
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004 Free Software
+ * Foundation, Inc.
+ * GNU Lesser General Public License
+*/
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include "avl.h"
-#include "log.h"
-/* Swing to the left
- * Warning: no balance maintainance
- */
-void avl_swl(avl** root) {
- avl* a = *root;
- avl* b = a->right;
- *root = b;
- a->right = b->left;
- b->left = a;
-}
+/* Search |tree| for an item matching |item|, and return it if found.
+ Otherwise return |NULL|. */
+avl *avl_search(avl_tree *tree, avl *item) {
+ avl *p;
-/* Swing to the right
- * Warning: no balance maintainance
- */
-void avl_swr(avl** root) {
- avl* a = *root;
- avl* b = a->left;
- *root = b;
- a->left = b->right;
- b->right = a;
-}
+ // assert (tree != NULL && item != NULL);
-/* Balance maintainance after especially nasty swings
- */
-void avl_nasty(avl* root) {
- switch (root->balance) {
- case -1:
- root->left->balance = 0;
- root->right->balance = 1;
- break;
- case 1:
- root->left->balance = -1;
- root->right->balance = 0;
- break;
- case 0:
- root->left->balance = 0;
- root->right->balance = 0;
- }
- root->balance = 0;
-}
+ for (p = tree->root; p != NULL; ) {
+ int cmp = tree->compar(item, p);
-/* Public methods */
+ if (cmp < 0)
+ p = p->avl_link[0];
+ else if (cmp > 0)
+ p = p->avl_link[1];
+ else /* |cmp == 0| */
+ return p;
+ }
-/* Insert element a into the AVL tree t
- * returns 1 if the depth of the tree has grown
- * Warning: do not insert elements already present
- */
-int avl_insert(avl_tree* t, avl* a) {
- /* initialize */
- a->left = 0;
- a->right = 0;
- a->balance = 0;
- /* insert into an empty tree */
- if (!t->root) {
- t->root = a;
- return 1;
- }
-
- if (t->compar(t->root, a) > 0) {
- /* insert into the left subtree */
- if (t->root->left) {
- avl_tree left_subtree;
- left_subtree.root = t->root->left;
- left_subtree.compar = t->compar;
- if (avl_insert(&left_subtree, a)) {
- switch (t->root->balance--) {
- case 1:
- return 0;
- case 0:
- return 1;
- }
- if (t->root->left->balance < 0) {
- avl_swr(&(t->root));
- t->root->balance = 0;
- t->root->right->balance = 0;
- } else {
- avl_swl(&(t->root->left));
- avl_swr(&(t->root));
- avl_nasty(t->root);
- }
- } else
- t->root->left = left_subtree.root;
- return 0;
- } else {
- t->root->left = a;
- if (t->root->balance--)
- return 0;
- return 1;
- }
- } else {
- /* insert into the right subtree */
- if (t->root->right) {
- avl_tree right_subtree;
- right_subtree.root = t->root->right;
- right_subtree.compar = t->compar;
- if (avl_insert(&right_subtree, a)) {
- switch (t->root->balance++) {
- case -1:
- return 0;
- case 0:
- return 1;
- }
- if (t->root->right->balance > 0) {
- avl_swl(&(t->root));
- t->root->balance = 0;
- t->root->left->balance = 0;
- } else {
- avl_swr(&(t->root->right));
- avl_swl(&(t->root));
- avl_nasty(t->root);
- }
- } else
- t->root->right = right_subtree.root;
- return 0;
- } else {
- t->root->right = a;
- if (t->root->balance++)
- return 0;
- return 1;
- }
- }
-}
-
-/* Remove an element a from the AVL tree t
- * returns -1 if the depth of the tree has shrunk
- * Warning: if the element is not present in the tree,
- * returns 0 as if it had been removed succesfully.
- */
-int avl_remove(avl_tree* t, avl* a) {
- int b;
- if (t->root == a)
- return avl_removeroot(t);
- b = t->compar(t->root, a);
- if (b >= 0) {
- /* remove from the left subtree */
- int ch;
- avl_tree left_subtree;
- if ((left_subtree.root = t->root->left)) {
- left_subtree.compar = t->compar;
- ch = avl_remove(&left_subtree, a);
- t->root->left = left_subtree.root;
- if (ch) {
- switch (t->root->balance++) {
- case -1:
- return -1;
- case 0:
- return 0;
- }
- switch (t->root->right->balance) {
- case 0:
- avl_swl(&(t->root));
- t->root->balance = -1;
- t->root->left->balance = 1;
- return 0;
- case 1:
- avl_swl(&(t->root));
- t->root->balance = 0;
- t->root->left->balance = 0;
- return -1;
- }
- avl_swr(&(t->root->right));
- avl_swl(&(t->root));
- avl_nasty(t->root);
- return -1;
- }
- }
- }
- if (b <= 0) {
- /* remove from the right subtree */
- int ch;
- avl_tree right_subtree;
- if ((right_subtree.root = t->root->right)) {
- right_subtree.compar = t->compar;
- ch = avl_remove(&right_subtree, a);
- t->root->right = right_subtree.root;
- if (ch) {
- switch (t->root->balance--) {
- case 1:
- return -1;
- case 0:
- return 0;
- }
- switch (t->root->left->balance) {
- case 0:
- avl_swr(&(t->root));
- t->root->balance = 1;
- t->root->right->balance = -1;
- return 0;
- case -1:
- avl_swr(&(t->root));
- t->root->balance = 0;
- t->root->right->balance = 0;
- return -1;
- }
- avl_swl(&(t->root->left));
- avl_swr(&(t->root));
- avl_nasty(t->root);
- return -1;
- }
- }
- }
- return 0;
+ return NULL;
}
-/* Remove the root of the AVL tree t
- * Warning: dumps core if t is empty
+/* Inserts |item| into |tree| and returns a pointer to |item|'s address.
+ If a duplicate item is found in the tree,
+ returns a pointer to the duplicate without inserting |item|.
*/
-int avl_removeroot(avl_tree* t) {
- int ch;
- avl* a;
- if (!t->root->left) {
- if (!t->root->right) {
- t->root = 0;
- return -1;
- }
- t->root = t->root->right;
- return -1;
- }
- if (!t->root->right) {
- t->root = t->root->left;
- return -1;
- }
- if (t->root->balance < 0) {
- /* remove from the left subtree */
- a = t->root->left;
- while (a->right)
- a = a->right;
- } else {
- /* remove from the right subtree */
- a = t->root->right;
- while (a->left)
- a = a->left;
- }
- ch = avl_remove(t, a);
- a->left = t->root->left;
- a->right = t->root->right;
- a->balance = t->root->balance;
- t->root = a;
- if (a->balance == 0)
- return ch;
- return 0;
+avl *avl_insert(avl_tree *tree, avl *item) {
+ avl *y, *z; /* Top node to update balance factor, and parent. */
+ avl *p, *q; /* Iterator, and parent. */
+ avl *n; /* Newly inserted node. */
+ avl *w; /* New root of rebalanced subtree. */
+ int dir; /* Direction to descend. */
+
+ unsigned char da[AVL_MAX_HEIGHT]; /* Cached comparison results. */
+ int k = 0; /* Number of cached results. */
+
+ // assert(tree != NULL && item != NULL);
+
+ z = (avl *) &tree->root;
+ y = tree->root;
+ dir = 0;
+ for (q = z, p = y; p != NULL; q = p, p = p->avl_link[dir]) {
+ int cmp = tree->compar(item, p);
+ if (cmp == 0)
+ return p;
+
+ if (p->avl_balance != 0)
+ z = q, y = p, k = 0;
+ da[k++] = dir = (cmp > 0);
+ }
+
+ n = q->avl_link[dir] = item;
+
+ // tree->avl_count++;
+ n->avl_link[0] = n->avl_link[1] = NULL;
+ n->avl_balance = 0;
+ if (y == NULL) return n;
+
+ for (p = y, k = 0; p != n; p = p->avl_link[da[k]], k++)
+ if (da[k] == 0)
+ p->avl_balance--;
+ else
+ p->avl_balance++;
+
+ if (y->avl_balance == -2) {
+ avl *x = y->avl_link[0];
+ if (x->avl_balance == -1) {
+ w = x;
+ y->avl_link[0] = x->avl_link[1];
+ x->avl_link[1] = y;
+ x->avl_balance = y->avl_balance = 0;
+ }
+ else {
+ // assert (x->avl_balance == +1);
+ w = x->avl_link[1];
+ x->avl_link[1] = w->avl_link[0];
+ w->avl_link[0] = x;
+ y->avl_link[0] = w->avl_link[1];
+ w->avl_link[1] = y;
+ if (w->avl_balance == -1)
+ x->avl_balance = 0, y->avl_balance = +1;
+ else if (w->avl_balance == 0)
+ x->avl_balance = y->avl_balance = 0;
+ else /* |w->avl_balance == +1| */
+ x->avl_balance = -1, y->avl_balance = 0;
+ w->avl_balance = 0;
+ }
+ }
+ else if (y->avl_balance == +2) {
+ avl *x = y->avl_link[1];
+ if (x->avl_balance == +1) {
+ w = x;
+ y->avl_link[1] = x->avl_link[0];
+ x->avl_link[0] = y;
+ x->avl_balance = y->avl_balance = 0;
+ }
+ else {
+ // assert (x->avl_balance == -1);
+ w = x->avl_link[0];
+ x->avl_link[0] = w->avl_link[1];
+ w->avl_link[1] = x;
+ y->avl_link[1] = w->avl_link[0];
+ w->avl_link[0] = y;
+ if (w->avl_balance == +1)
+ x->avl_balance = 0, y->avl_balance = -1;
+ else if (w->avl_balance == 0)
+ x->avl_balance = y->avl_balance = 0;
+ else /* |w->avl_balance == -1| */
+ x->avl_balance = +1, y->avl_balance = 0;
+ w->avl_balance = 0;
+ }
+ }
+ else return n;
+
+ z->avl_link[y != z->avl_link[0]] = w;
+
+ // tree->avl_generation++;
+ return n;
}
-/* Iterate through elements in t from a range between a and b (inclusive)
- * for each element calls iter(a) until it returns 0
- * returns the last value returned by iterator or 0 if there were no calls
- * Warning: a<=b must hold
- */
-int avl_range(avl_tree* t, avl* a, avl* b, int (*iter)(avl*), avl** ret) {
- int x, c = 0;
- if (!t->root)
- return 0;
- x = t->compar(t->root, a);
- if (a != b) {
- if (x < 0) {
- x = t->compar(t->root, b);
- if (x > 0)
- x = 0;
- }
- }
- if (x >= 0) {
- /* search in the left subtree */
- avl_tree left_subtree;
- if ((left_subtree.root = t->root->left)) {
- left_subtree.compar = t->compar;
- if (!(c = avl_range(&left_subtree, a, b, iter, ret)))
- if (x > 0)
- return 0;
- }
- }
- if (x == 0) {
- if (!(c = iter(t->root))) {
- if (ret)
- *ret = t->root;
- return 0;
- }
- }
- if (x <= 0) {
- /* search in the right subtree */
- avl_tree right_subtree;
- if ((right_subtree.root = t->root->right)) {
- right_subtree.compar = t->compar;
- if (!(c = avl_range(&right_subtree, a, b, iter, ret)))
- if (x < 0)
- return 0;
- }
- }
- return c;
+/* Deletes from |tree| and returns an item matching |item|.
+ Returns a null pointer if no matching item found. */
+avl *avl_remove(avl_tree *tree, avl *item) {
+ /* Stack of nodes. */
+ avl *pa[AVL_MAX_HEIGHT]; /* Nodes. */
+ unsigned char da[AVL_MAX_HEIGHT]; /* |avl_link[]| indexes. */
+ int k; /* Stack pointer. */
+
+ avl *p; /* Traverses tree to find node to delete. */
+ int cmp; /* Result of comparison between |item| and |p|. */
+
+ // assert (tree != NULL && item != NULL);
+
+ k = 0;
+ p = (avl *) &tree->root;
+ for(cmp = -1; cmp != 0; cmp = tree->compar(item, p)) {
+ int dir = (cmp > 0);
+
+ pa[k] = p;
+ da[k++] = dir;
+
+ p = p->avl_link[dir];
+ if(p == NULL) return NULL;
+ }
+
+ item = p;
+
+ if (p->avl_link[1] == NULL)
+ pa[k - 1]->avl_link[da[k - 1]] = p->avl_link[0];
+ else {
+ avl *r = p->avl_link[1];
+ if (r->avl_link[0] == NULL) {
+ r->avl_link[0] = p->avl_link[0];
+ r->avl_balance = p->avl_balance;
+ pa[k - 1]->avl_link[da[k - 1]] = r;
+ da[k] = 1;
+ pa[k++] = r;
+ }
+ else {
+ avl *s;
+ int j = k++;
+
+ for (;;) {
+ da[k] = 0;
+ pa[k++] = r;
+ s = r->avl_link[0];
+ if (s->avl_link[0] == NULL) break;
+
+ r = s;
+ }
+
+ s->avl_link[0] = p->avl_link[0];
+ r->avl_link[0] = s->avl_link[1];
+ s->avl_link[1] = p->avl_link[1];
+ s->avl_balance = p->avl_balance;
+
+ pa[j - 1]->avl_link[da[j - 1]] = s;
+ da[j] = 1;
+ pa[j] = s;
+ }
+ }
+
+ // assert (k > 0);
+ while (--k > 0) {
+ avl *y = pa[k];
+
+ if (da[k] == 0) {
+ y->avl_balance++;
+ if (y->avl_balance == +1) break;
+ else if (y->avl_balance == +2) {
+ avl *x = y->avl_link[1];
+ if (x->avl_balance == -1) {
+ avl *w;
+ // assert (x->avl_balance == -1);
+ w = x->avl_link[0];
+ x->avl_link[0] = w->avl_link[1];
+ w->avl_link[1] = x;
+ y->avl_link[1] = w->avl_link[0];
+ w->avl_link[0] = y;
+ if (w->avl_balance == +1)
+ x->avl_balance = 0, y->avl_balance = -1;
+ else if (w->avl_balance == 0)
+ x->avl_balance = y->avl_balance = 0;
+ else /* |w->avl_balance == -1| */
+ x->avl_balance = +1, y->avl_balance = 0;
+ w->avl_balance = 0;
+ pa[k - 1]->avl_link[da[k - 1]] = w;
+ }
+ else {
+ y->avl_link[1] = x->avl_link[0];
+ x->avl_link[0] = y;
+ pa[k - 1]->avl_link[da[k - 1]] = x;
+ if (x->avl_balance == 0) {
+ x->avl_balance = -1;
+ y->avl_balance = +1;
+ break;
+ }
+ else x->avl_balance = y->avl_balance = 0;
+ }
+ }
+ }
+ else
+ {
+ y->avl_balance--;
+ if (y->avl_balance == -1) break;
+ else if (y->avl_balance == -2) {
+ avl *x = y->avl_link[0];
+ if (x->avl_balance == +1) {
+ avl *w;
+ // assert (x->avl_balance == +1);
+ w = x->avl_link[1];
+ x->avl_link[1] = w->avl_link[0];
+ w->avl_link[0] = x;
+ y->avl_link[0] = w->avl_link[1];
+ w->avl_link[1] = y;
+ if (w->avl_balance == -1)
+ x->avl_balance = 0, y->avl_balance = +1;
+ else if (w->avl_balance == 0)
+ x->avl_balance = y->avl_balance = 0;
+ else /* |w->avl_balance == +1| */
+ x->avl_balance = -1, y->avl_balance = 0;
+ w->avl_balance = 0;
+ pa[k - 1]->avl_link[da[k - 1]] = w;
+ }
+ else {
+ y->avl_link[0] = x->avl_link[1];
+ x->avl_link[1] = y;
+ pa[k - 1]->avl_link[da[k - 1]] = x;
+ if (x->avl_balance == 0) {
+ x->avl_balance = +1;
+ y->avl_balance = -1;
+ break;
+ }
+ else x->avl_balance = y->avl_balance = 0;
+ }
+ }
+ }
+ }
+
+ // tree->avl_count--;
+ // tree->avl_generation++;
+ return item;
}
-/* high performance searching - by ktsaou */
-avl *avl_search(avl_tree *t, avl *a) {
- avl *root = t->root;
-
- while(root) {
- int x = t->compar(root, a);
+/* ------------------------------------------------------------------------- */
+// below are functions by (C) Costa Tsaousis
- if(x > 0) {
- root = root->left;
- continue;
- }
+// ---------------------------
+// traversing
- if(x < 0) {
- root = root->right;
- continue;
- }
+void avl_walker(avl *node, void (*callback)(void *)) {
+ if(node->avl_link[0])
+ avl_walker(node->avl_link[0], callback);
- return root;
- }
+ callback(node);
- return NULL;
+ if(node->avl_link[1])
+ avl_walker(node->avl_link[1], callback);
}
-void avl_init(avl_tree *t, int (*compar)(void *a, void *b)) {
- t->root = NULL;
- t->compar = compar;
+void avl_traverse(avl_tree *t, void (*callback)(void *)) {
+ avl_walker(t->root, callback);
}
-/* ------------------------------------------------------------------------- */
+// ---------------------------
+// locks
void avl_read_lock(avl_tree_lock *t) {
#ifndef AVL_WITHOUT_PTHREADS
#ifdef AVL_LOCK_WITH_MUTEX
- pthread_mutex_lock(&t->mutex);
+ pthread_mutex_lock(&t->mutex);
#else
- pthread_rwlock_rdlock(&t->rwlock);
+ pthread_rwlock_rdlock(&t->rwlock);
#endif
#endif /* AVL_WITHOUT_PTHREADS */
}
@@ -352,9 +313,9 @@ void avl_read_lock(avl_tree_lock *t) {
void avl_write_lock(avl_tree_lock *t) {
#ifndef AVL_WITHOUT_PTHREADS
#ifdef AVL_LOCK_WITH_MUTEX
- pthread_mutex_lock(&t->mutex);
+ pthread_mutex_lock(&t->mutex);
#else
- pthread_rwlock_wrlock(&t->rwlock);
+ pthread_rwlock_wrlock(&t->rwlock);
#endif
#endif /* AVL_WITHOUT_PTHREADS */
}
@@ -362,64 +323,64 @@ void avl_write_lock(avl_tree_lock *t) {
void avl_unlock(avl_tree_lock *t) {
#ifndef AVL_WITHOUT_PTHREADS
#ifdef AVL_LOCK_WITH_MUTEX
- pthread_mutex_unlock(&t->mutex);
+ pthread_mutex_unlock(&t->mutex);
#else
- pthread_rwlock_unlock(&t->rwlock);
+ pthread_rwlock_unlock(&t->rwlock);
#endif
#endif /* AVL_WITHOUT_PTHREADS */
}
-/* ------------------------------------------------------------------------- */
+// ---------------------------
+// operations with locking
void avl_init_lock(avl_tree_lock *t, int (*compar)(void *a, void *b)) {
- avl_init(&t->avl_tree, compar);
+ avl_init(&t->avl_tree, compar);
#ifndef AVL_WITHOUT_PTHREADS
- int lock;
+ int lock;
#ifdef AVL_LOCK_WITH_MUTEX
- lock = pthread_mutex_init(&t->mutex, NULL);
+ lock = pthread_mutex_init(&t->mutex, NULL);
#else
- lock = pthread_rwlock_init(&t->rwlock, NULL);
+ lock = pthread_rwlock_init(&t->rwlock, NULL);
#endif
- if(lock != 0)
- fatal("Failed to initialize AVL mutex/rwlock, error: %d", lock);
+ if(lock != 0)
+ fatal("Failed to initialize AVL mutex/rwlock, error: %d", lock);
#endif /* AVL_WITHOUT_PTHREADS */
}
avl *avl_search_lock(avl_tree_lock *t, avl *a) {
- avl_read_lock(t);
- avl *ret = avl_search(&t->avl_tree, a);
- avl_unlock(t);
- return ret;
+ avl_read_lock(t);
+ avl *ret = avl_search(&t->avl_tree, a);
+ avl_unlock(t);
+ return ret;
}
-int avl_range_lock(avl_tree_lock *t, avl *a, avl *b, int (*iter)(avl *), avl **ret) {
- avl_read_lock(t);
- int ret2 = avl_range(&t->avl_tree, a, b, iter, ret);
- avl_unlock(t);
- return ret2;
+avl * avl_remove_lock(avl_tree_lock *t, avl *a) {
+ avl_write_lock(t);
+ avl *ret = avl_remove(&t->avl_tree, a);
+ avl_unlock(t);
+ return ret;
}
-int avl_removeroot_lock(avl_tree_lock *t) {
- avl_write_lock(t);
- int ret = avl_removeroot(&t->avl_tree);
- avl_unlock(t);
- return ret;
+avl *avl_insert_lock(avl_tree_lock *t, avl *a) {
+ avl_write_lock(t);
+ avl * ret = avl_insert(&t->avl_tree, a);
+ avl_unlock(t);
+ return ret;
}
-int avl_remove_lock(avl_tree_lock *t, avl *a) {
- avl_write_lock(t);
- int ret = avl_remove(&t->avl_tree, a);
- avl_unlock(t);
- return ret;
+void avl_traverse_lock(avl_tree_lock *t, void (*callback)(void *)) {
+ avl_read_lock(t);
+ avl_traverse(&t->avl_tree, callback);
+ avl_unlock(t);
}
-int avl_insert_lock(avl_tree_lock *t, avl *a) {
- avl_write_lock(t);
- int ret = avl_insert(&t->avl_tree, a);
- avl_unlock(t);
- return ret;
+void avl_init(avl_tree *t, int (*compar)(void *a, void *b)) {
+ t->root = NULL;
+ t->compar = compar;
}
+
+// ------------------ \ No newline at end of file
diff --git a/src/avl.h b/src/avl.h
index 5397b196e..973d68fb1 100644
--- a/src/avl.h
+++ b/src/avl.h
@@ -1,20 +1,12 @@
-/*
- * ANSI C Library for maintainance of AVL Balanced Trees
- *
- * ref.:
- * G. M. Adelson-Velskij & E. M. Landis
- * Doklady Akad. Nauk SSSR 146 (1962), 263-266
- *
- * see also:
- * D. E. Knuth: The Art of Computer Programming Vol.3 (Sorting and Searching)
- *
- * (C) 2000 Daniel Nagy, Budapest University of Technology and Economics
- * Released under GNU General Public License (GPL) version 2
- *
- */
+
#ifndef _AVL_H
#define _AVL_H 1
+/* Maximum AVL tree height. */
+#ifndef AVL_MAX_HEIGHT
+#define AVL_MAX_HEIGHT 92
+#endif
+
#ifndef AVL_WITHOUT_PTHREADS
#include <pthread.h>
@@ -34,26 +26,24 @@
/* One element of the AVL tree */
typedef struct avl {
- struct avl* left;
- struct avl* right;
- signed char balance;
+ struct avl *avl_link[2]; /* Subtrees. */
+ signed char avl_balance; /* Balance factor. */
} avl;
/* An AVL tree */
typedef struct avl_tree {
- avl *root;
-
- int (*compar)(void *a, void *b);
+ avl *root;
+ int (*compar)(void *a, void *b);
} avl_tree;
typedef struct avl_tree_lock {
- avl_tree avl_tree;
+ avl_tree avl_tree;
#ifndef AVL_WITHOUT_PTHREADS
#ifdef AVL_LOCK_WITH_MUTEX
- pthread_mutex_t mutex;
+ pthread_mutex_t mutex;
#else /* AVL_LOCK_WITH_MUTEX */
- pthread_rwlock_t rwlock;
+ pthread_rwlock_t rwlock;
#endif /* AVL_LOCK_WITH_MUTEX */
#endif /* AVL_WITHOUT_PTHREADS */
} avl_tree_lock;
@@ -61,37 +51,25 @@ typedef struct avl_tree_lock {
/* Public methods */
/* Insert element a into the AVL tree t
- * returns 1 if the depth of the tree has grown
- * Warning: do not insert elements already present
+ * returns the added element a, or a pointer the
+ * element that is equal to a (as returned by t->compar())
+ * a is linked directly to the tree, so it has to
+ * be properly allocated by the caller.
*/
-int avl_insert_lock(avl_tree_lock *t, avl *a);
-int avl_insert(avl_tree *t, avl *a);
+avl *avl_insert_lock(avl_tree_lock *t, avl *a);
+avl *avl_insert(avl_tree *t, avl *a);
/* Remove an element a from the AVL tree t
- * returns -1 if the depth of the tree has shrunk
- * Warning: if the element is not present in the tree,
- * returns 0 as if it had been removed succesfully.
- */
-int avl_remove_lock(avl_tree_lock *t, avl *a);
-int avl_remove(avl_tree *t, avl *a);
-
-/* Remove the root of the AVL tree t
- * Warning: dumps core if t is empty
- */
-int avl_removeroot_lock(avl_tree_lock *t);
-int avl_removeroot(avl_tree *t);
-
-/* Iterate through elements in t from a range between a and b (inclusive)
- * for each element calls iter(a) until it returns 0
- * returns the last value returned by iterator or 0 if there were no calls
- * Warning: a<=b must hold
+ * returns a pointer to the removed element
+ * or NULL if an element equal to a is not found
+ * (equal as returned by t->compar())
*/
-int avl_range_lock(avl_tree_lock *t, avl *a, avl *b, int (*iter)(avl *), avl **ret);
-int avl_range(avl_tree *t, avl *a, avl *b, int (*iter)(avl *), avl **ret);
+avl *avl_remove_lock(avl_tree_lock *t, avl *a);
+avl *avl_remove(avl_tree *t, avl *a);
-/* Iterate through elements in t equal to a
- * for each element calls iter(a) until it returns 0
- * returns the last value returned by iterator or 0 if there were no calls
+/* Find the element into the tree that equal to a
+ * (equal as returned by t->compar())
+ * returns NULL is no element is equal to a
*/
avl *avl_search_lock(avl_tree_lock *t, avl *a);
avl *avl_search(avl_tree *t, avl *a);
@@ -101,4 +79,8 @@ avl *avl_search(avl_tree *t, avl *a);
void avl_init_lock(avl_tree_lock *t, int (*compar)(void *a, void *b));
void avl_init(avl_tree *t, int (*compar)(void *a, void *b));
+
+void avl_traverse_lock(avl_tree_lock *t, void (*callback)(void *));
+void avl_traverse(avl_tree *t, void (*callback)(void *));
+
#endif /* avl.h */
diff --git a/src/common.c b/src/common.c
index a2b0d940f..7d0fac9aa 100644
--- a/src/common.c
+++ b/src/common.c
@@ -1,752 +1,859 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <sys/syscall.h>
-#include <string.h>
-#include <ctype.h>
-#include <errno.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <sys/mman.h>
-
-
-#include "log.h"
#include "common.h"
-#include "appconfig.h"
-#include "../config.h"
char *global_host_prefix = "";
int enable_ksm = 1;
-// time(NULL) in milliseconds
-unsigned long long timems(void) {
- struct timeval now;
- gettimeofday(&now, NULL);
- return now.tv_sec * 1000000ULL + now.tv_usec;
+volatile sig_atomic_t netdata_exit = 0;
+
+// ----------------------------------------------------------------------------
+// memory allocation functions that handle failures
+
+// although netdata does not use memory allocations too often (netdata tries to
+// maintain its memory footprint stable during runtime, i.e. all buffers are
+// allocated during initialization and are adapted to current use throughout
+// its lifetime), these can be used to override the default system allocation
+// routines.
+
+char *strdupz(const char *s) {
+ char *t = strdup(s);
+ if (unlikely(!t)) fatal("Cannot strdup() string '%s'", s);
+ return t;
+}
+
+void *mallocz(size_t size) {
+ void *p = malloc(size);
+ if (unlikely(!p)) fatal("Cannot allocate %zu bytes of memory.", size);
+ return p;
+}
+
+void *callocz(size_t nmemb, size_t size) {
+ void *p = calloc(nmemb, size);
+ if (unlikely(!p)) fatal("Cannot allocate %zu bytes of memory.", nmemb * size);
+ return p;
+}
+
+void *reallocz(void *ptr, size_t size) {
+ void *p = realloc(ptr, size);
+ if (unlikely(!p)) fatal("Cannot re-allocate memory to %zu bytes.", size);
+ return p;
+}
+
+void freez(void *ptr) {
+ free(ptr);
+}
+
+// ----------------------------------------------------------------------------
+// time functions
+
+inline unsigned long long timeval_usec(struct timeval *tv) {
+ return tv->tv_sec * 1000000ULL + tv->tv_usec;
+}
+
+// time(NULL) in nanoseconds
+inline unsigned long long time_usec(void) {
+ struct timeval now;
+ gettimeofday(&now, NULL);
+ return timeval_usec(&now);
+}
+
+inline unsigned long long usec_dt(struct timeval *now, struct timeval *old) {
+ unsigned long long tv1 = timeval_usec(now);
+ unsigned long long tv2 = timeval_usec(old);
+ return (tv1 > tv2) ? (tv1 - tv2) : (tv2 - tv1);
+}
+
+int sleep_usec(unsigned long long usec) {
+
+#ifndef NETDATA_WITH_USLEEP
+ // we expect microseconds (1.000.000 per second)
+ // but timespec is nanoseconds (1.000.000.000 per second)
+ struct timespec rem, req = {
+ .tv_sec = (time_t) (usec / 1000000),
+ .tv_nsec = (suseconds_t) ((usec % 1000000) * 1000)
+ };
+
+ while (nanosleep(&req, &rem) == -1) {
+ if (likely(errno == EINTR)) {
+ info("nanosleep() interrupted (while sleeping for %llu microseconds).", usec);
+ req.tv_sec = rem.tv_sec;
+ req.tv_nsec = rem.tv_nsec;
+ } else {
+ error("Cannot nanosleep() for %llu microseconds.", usec);
+ break;
+ }
+ }
+
+ return 0;
+#else
+ int ret = usleep(usec);
+ if(unlikely(ret == -1 && errno == EINVAL)) {
+ // on certain systems, usec has to be up to 999999
+ if(usec > 999999) {
+ int counter = usec / 999999;
+ while(counter--)
+ usleep(999999);
+
+ usleep(usec % 999999);
+ }
+ else {
+ error("Cannot usleep() for %llu microseconds.", usec);
+ return ret;
+ }
+ }
+
+ if(ret != 0)
+ error("usleep() failed for %llu microseconds.", usec);
+
+ return ret;
+#endif
}
unsigned char netdata_map_chart_names[256] = {
- [0] = '\0', //
- [1] = '_', //
- [2] = '_', //
- [3] = '_', //
- [4] = '_', //
- [5] = '_', //
- [6] = '_', //
- [7] = '_', //
- [8] = '_', //
- [9] = '_', //
- [10] = '_', //
- [11] = '_', //
- [12] = '_', //
- [13] = '_', //
- [14] = '_', //
- [15] = '_', //
- [16] = '_', //
- [17] = '_', //
- [18] = '_', //
- [19] = '_', //
- [20] = '_', //
- [21] = '_', //
- [22] = '_', //
- [23] = '_', //
- [24] = '_', //
- [25] = '_', //
- [26] = '_', //
- [27] = '_', //
- [28] = '_', //
- [29] = '_', //
- [30] = '_', //
- [31] = '_', //
- [32] = '_', //
- [33] = '_', // !
- [34] = '_', // "
- [35] = '_', // #
- [36] = '_', // $
- [37] = '_', // %
- [38] = '_', // &
- [39] = '_', // '
- [40] = '_', // (
- [41] = '_', // )
- [42] = '_', // *
- [43] = '_', // +
- [44] = '.', // ,
- [45] = '-', // -
- [46] = '.', // .
- [47] = '/', // /
- [48] = '0', // 0
- [49] = '1', // 1
- [50] = '2', // 2
- [51] = '3', // 3
- [52] = '4', // 4
- [53] = '5', // 5
- [54] = '6', // 6
- [55] = '7', // 7
- [56] = '8', // 8
- [57] = '9', // 9
- [58] = '_', // :
- [59] = '_', // ;
- [60] = '_', // <
- [61] = '_', // =
- [62] = '_', // >
- [63] = '_', // ?
- [64] = '_', // @
- [65] = 'a', // A
- [66] = 'b', // B
- [67] = 'c', // C
- [68] = 'd', // D
- [69] = 'e', // E
- [70] = 'f', // F
- [71] = 'g', // G
- [72] = 'h', // H
- [73] = 'i', // I
- [74] = 'j', // J
- [75] = 'k', // K
- [76] = 'l', // L
- [77] = 'm', // M
- [78] = 'n', // N
- [79] = 'o', // O
- [80] = 'p', // P
- [81] = 'q', // Q
- [82] = 'r', // R
- [83] = 's', // S
- [84] = 't', // T
- [85] = 'u', // U
- [86] = 'v', // V
- [87] = 'w', // W
- [88] = 'x', // X
- [89] = 'y', // Y
- [90] = 'z', // Z
- [91] = '_', // [
- [92] = '/', // backslash
- [93] = '_', // ]
- [94] = '_', // ^
- [95] = '_', // _
- [96] = '_', // `
- [97] = 'a', // a
- [98] = 'b', // b
- [99] = 'c', // c
- [100] = 'd', // d
- [101] = 'e', // e
- [102] = 'f', // f
- [103] = 'g', // g
- [104] = 'h', // h
- [105] = 'i', // i
- [106] = 'j', // j
- [107] = 'k', // k
- [108] = 'l', // l
- [109] = 'm', // m
- [110] = 'n', // n
- [111] = 'o', // o
- [112] = 'p', // p
- [113] = 'q', // q
- [114] = 'r', // r
- [115] = 's', // s
- [116] = 't', // t
- [117] = 'u', // u
- [118] = 'v', // v
- [119] = 'w', // w
- [120] = 'x', // x
- [121] = 'y', // y
- [122] = 'z', // z
- [123] = '_', // {
- [124] = '_', // |
- [125] = '_', // }
- [126] = '_', // ~
- [127] = '_', //
- [128] = '_', //
- [129] = '_', //
- [130] = '_', //
- [131] = '_', //
- [132] = '_', //
- [133] = '_', //
- [134] = '_', //
- [135] = '_', //
- [136] = '_', //
- [137] = '_', //
- [138] = '_', //
- [139] = '_', //
- [140] = '_', //
- [141] = '_', //
- [142] = '_', //
- [143] = '_', //
- [144] = '_', //
- [145] = '_', //
- [146] = '_', //
- [147] = '_', //
- [148] = '_', //
- [149] = '_', //
- [150] = '_', //
- [151] = '_', //
- [152] = '_', //
- [153] = '_', //
- [154] = '_', //
- [155] = '_', //
- [156] = '_', //
- [157] = '_', //
- [158] = '_', //
- [159] = '_', //
- [160] = '_', //
- [161] = '_', //
- [162] = '_', //
- [163] = '_', //
- [164] = '_', //
- [165] = '_', //
- [166] = '_', //
- [167] = '_', //
- [168] = '_', //
- [169] = '_', //
- [170] = '_', //
- [171] = '_', //
- [172] = '_', //
- [173] = '_', //
- [174] = '_', //
- [175] = '_', //
- [176] = '_', //
- [177] = '_', //
- [178] = '_', //
- [179] = '_', //
- [180] = '_', //
- [181] = '_', //
- [182] = '_', //
- [183] = '_', //
- [184] = '_', //
- [185] = '_', //
- [186] = '_', //
- [187] = '_', //
- [188] = '_', //
- [189] = '_', //
- [190] = '_', //
- [191] = '_', //
- [192] = '_', //
- [193] = '_', //
- [194] = '_', //
- [195] = '_', //
- [196] = '_', //
- [197] = '_', //
- [198] = '_', //
- [199] = '_', //
- [200] = '_', //
- [201] = '_', //
- [202] = '_', //
- [203] = '_', //
- [204] = '_', //
- [205] = '_', //
- [206] = '_', //
- [207] = '_', //
- [208] = '_', //
- [209] = '_', //
- [210] = '_', //
- [211] = '_', //
- [212] = '_', //
- [213] = '_', //
- [214] = '_', //
- [215] = '_', //
- [216] = '_', //
- [217] = '_', //
- [218] = '_', //
- [219] = '_', //
- [220] = '_', //
- [221] = '_', //
- [222] = '_', //
- [223] = '_', //
- [224] = '_', //
- [225] = '_', //
- [226] = '_', //
- [227] = '_', //
- [228] = '_', //
- [229] = '_', //
- [230] = '_', //
- [231] = '_', //
- [232] = '_', //
- [233] = '_', //
- [234] = '_', //
- [235] = '_', //
- [236] = '_', //
- [237] = '_', //
- [238] = '_', //
- [239] = '_', //
- [240] = '_', //
- [241] = '_', //
- [242] = '_', //
- [243] = '_', //
- [244] = '_', //
- [245] = '_', //
- [246] = '_', //
- [247] = '_', //
- [248] = '_', //
- [249] = '_', //
- [250] = '_', //
- [251] = '_', //
- [252] = '_', //
- [253] = '_', //
- [254] = '_', //
- [255] = '_' //
+ [0] = '\0', //
+ [1] = '_', //
+ [2] = '_', //
+ [3] = '_', //
+ [4] = '_', //
+ [5] = '_', //
+ [6] = '_', //
+ [7] = '_', //
+ [8] = '_', //
+ [9] = '_', //
+ [10] = '_', //
+ [11] = '_', //
+ [12] = '_', //
+ [13] = '_', //
+ [14] = '_', //
+ [15] = '_', //
+ [16] = '_', //
+ [17] = '_', //
+ [18] = '_', //
+ [19] = '_', //
+ [20] = '_', //
+ [21] = '_', //
+ [22] = '_', //
+ [23] = '_', //
+ [24] = '_', //
+ [25] = '_', //
+ [26] = '_', //
+ [27] = '_', //
+ [28] = '_', //
+ [29] = '_', //
+ [30] = '_', //
+ [31] = '_', //
+ [32] = '_', //
+ [33] = '_', // !
+ [34] = '_', // "
+ [35] = '_', // #
+ [36] = '_', // $
+ [37] = '_', // %
+ [38] = '_', // &
+ [39] = '_', // '
+ [40] = '_', // (
+ [41] = '_', // )
+ [42] = '_', // *
+ [43] = '_', // +
+ [44] = '.', // ,
+ [45] = '-', // -
+ [46] = '.', // .
+ [47] = '/', // /
+ [48] = '0', // 0
+ [49] = '1', // 1
+ [50] = '2', // 2
+ [51] = '3', // 3
+ [52] = '4', // 4
+ [53] = '5', // 5
+ [54] = '6', // 6
+ [55] = '7', // 7
+ [56] = '8', // 8
+ [57] = '9', // 9
+ [58] = '_', // :
+ [59] = '_', // ;
+ [60] = '_', // <
+ [61] = '_', // =
+ [62] = '_', // >
+ [63] = '_', // ?
+ [64] = '_', // @
+ [65] = 'a', // A
+ [66] = 'b', // B
+ [67] = 'c', // C
+ [68] = 'd', // D
+ [69] = 'e', // E
+ [70] = 'f', // F
+ [71] = 'g', // G
+ [72] = 'h', // H
+ [73] = 'i', // I
+ [74] = 'j', // J
+ [75] = 'k', // K
+ [76] = 'l', // L
+ [77] = 'm', // M
+ [78] = 'n', // N
+ [79] = 'o', // O
+ [80] = 'p', // P
+ [81] = 'q', // Q
+ [82] = 'r', // R
+ [83] = 's', // S
+ [84] = 't', // T
+ [85] = 'u', // U
+ [86] = 'v', // V
+ [87] = 'w', // W
+ [88] = 'x', // X
+ [89] = 'y', // Y
+ [90] = 'z', // Z
+ [91] = '_', // [
+ [92] = '/', // backslash
+ [93] = '_', // ]
+ [94] = '_', // ^
+ [95] = '_', // _
+ [96] = '_', // `
+ [97] = 'a', // a
+ [98] = 'b', // b
+ [99] = 'c', // c
+ [100] = 'd', // d
+ [101] = 'e', // e
+ [102] = 'f', // f
+ [103] = 'g', // g
+ [104] = 'h', // h
+ [105] = 'i', // i
+ [106] = 'j', // j
+ [107] = 'k', // k
+ [108] = 'l', // l
+ [109] = 'm', // m
+ [110] = 'n', // n
+ [111] = 'o', // o
+ [112] = 'p', // p
+ [113] = 'q', // q
+ [114] = 'r', // r
+ [115] = 's', // s
+ [116] = 't', // t
+ [117] = 'u', // u
+ [118] = 'v', // v
+ [119] = 'w', // w
+ [120] = 'x', // x
+ [121] = 'y', // y
+ [122] = 'z', // z
+ [123] = '_', // {
+ [124] = '_', // |
+ [125] = '_', // }
+ [126] = '_', // ~
+ [127] = '_', //
+ [128] = '_', //
+ [129] = '_', //
+ [130] = '_', //
+ [131] = '_', //
+ [132] = '_', //
+ [133] = '_', //
+ [134] = '_', //
+ [135] = '_', //
+ [136] = '_', //
+ [137] = '_', //
+ [138] = '_', //
+ [139] = '_', //
+ [140] = '_', //
+ [141] = '_', //
+ [142] = '_', //
+ [143] = '_', //
+ [144] = '_', //
+ [145] = '_', //
+ [146] = '_', //
+ [147] = '_', //
+ [148] = '_', //
+ [149] = '_', //
+ [150] = '_', //
+ [151] = '_', //
+ [152] = '_', //
+ [153] = '_', //
+ [154] = '_', //
+ [155] = '_', //
+ [156] = '_', //
+ [157] = '_', //
+ [158] = '_', //
+ [159] = '_', //
+ [160] = '_', //
+ [161] = '_', //
+ [162] = '_', //
+ [163] = '_', //
+ [164] = '_', //
+ [165] = '_', //
+ [166] = '_', //
+ [167] = '_', //
+ [168] = '_', //
+ [169] = '_', //
+ [170] = '_', //
+ [171] = '_', //
+ [172] = '_', //
+ [173] = '_', //
+ [174] = '_', //
+ [175] = '_', //
+ [176] = '_', //
+ [177] = '_', //
+ [178] = '_', //
+ [179] = '_', //
+ [180] = '_', //
+ [181] = '_', //
+ [182] = '_', //
+ [183] = '_', //
+ [184] = '_', //
+ [185] = '_', //
+ [186] = '_', //
+ [187] = '_', //
+ [188] = '_', //
+ [189] = '_', //
+ [190] = '_', //
+ [191] = '_', //
+ [192] = '_', //
+ [193] = '_', //
+ [194] = '_', //
+ [195] = '_', //
+ [196] = '_', //
+ [197] = '_', //
+ [198] = '_', //
+ [199] = '_', //
+ [200] = '_', //
+ [201] = '_', //
+ [202] = '_', //
+ [203] = '_', //
+ [204] = '_', //
+ [205] = '_', //
+ [206] = '_', //
+ [207] = '_', //
+ [208] = '_', //
+ [209] = '_', //
+ [210] = '_', //
+ [211] = '_', //
+ [212] = '_', //
+ [213] = '_', //
+ [214] = '_', //
+ [215] = '_', //
+ [216] = '_', //
+ [217] = '_', //
+ [218] = '_', //
+ [219] = '_', //
+ [220] = '_', //
+ [221] = '_', //
+ [222] = '_', //
+ [223] = '_', //
+ [224] = '_', //
+ [225] = '_', //
+ [226] = '_', //
+ [227] = '_', //
+ [228] = '_', //
+ [229] = '_', //
+ [230] = '_', //
+ [231] = '_', //
+ [232] = '_', //
+ [233] = '_', //
+ [234] = '_', //
+ [235] = '_', //
+ [236] = '_', //
+ [237] = '_', //
+ [238] = '_', //
+ [239] = '_', //
+ [240] = '_', //
+ [241] = '_', //
+ [242] = '_', //
+ [243] = '_', //
+ [244] = '_', //
+ [245] = '_', //
+ [246] = '_', //
+ [247] = '_', //
+ [248] = '_', //
+ [249] = '_', //
+ [250] = '_', //
+ [251] = '_', //
+ [252] = '_', //
+ [253] = '_', //
+ [254] = '_', //
+ [255] = '_' //
};
// make sure the supplied string
// is good for a netdata chart/dimension ID/NAME
void netdata_fix_chart_name(char *s) {
- while((*s = netdata_map_chart_names[(unsigned char)*s])) s++;
+ while ((*s = netdata_map_chart_names[(unsigned char) *s])) s++;
}
unsigned char netdata_map_chart_ids[256] = {
- [0] = '\0', //
- [1] = '_', //
- [2] = '_', //
- [3] = '_', //
- [4] = '_', //
- [5] = '_', //
- [6] = '_', //
- [7] = '_', //
- [8] = '_', //
- [9] = '_', //
- [10] = '_', //
- [11] = '_', //
- [12] = '_', //
- [13] = '_', //
- [14] = '_', //
- [15] = '_', //
- [16] = '_', //
- [17] = '_', //
- [18] = '_', //
- [19] = '_', //
- [20] = '_', //
- [21] = '_', //
- [22] = '_', //
- [23] = '_', //
- [24] = '_', //
- [25] = '_', //
- [26] = '_', //
- [27] = '_', //
- [28] = '_', //
- [29] = '_', //
- [30] = '_', //
- [31] = '_', //
- [32] = '_', //
- [33] = '_', // !
- [34] = '_', // "
- [35] = '_', // #
- [36] = '_', // $
- [37] = '_', // %
- [38] = '_', // &
- [39] = '_', // '
- [40] = '_', // (
- [41] = '_', // )
- [42] = '_', // *
- [43] = '_', // +
- [44] = '.', // ,
- [45] = '-', // -
- [46] = '.', // .
- [47] = '_', // /
- [48] = '0', // 0
- [49] = '1', // 1
- [50] = '2', // 2
- [51] = '3', // 3
- [52] = '4', // 4
- [53] = '5', // 5
- [54] = '6', // 6
- [55] = '7', // 7
- [56] = '8', // 8
- [57] = '9', // 9
- [58] = '_', // :
- [59] = '_', // ;
- [60] = '_', // <
- [61] = '_', // =
- [62] = '_', // >
- [63] = '_', // ?
- [64] = '_', // @
- [65] = 'a', // A
- [66] = 'b', // B
- [67] = 'c', // C
- [68] = 'd', // D
- [69] = 'e', // E
- [70] = 'f', // F
- [71] = 'g', // G
- [72] = 'h', // H
- [73] = 'i', // I
- [74] = 'j', // J
- [75] = 'k', // K
- [76] = 'l', // L
- [77] = 'm', // M
- [78] = 'n', // N
- [79] = 'o', // O
- [80] = 'p', // P
- [81] = 'q', // Q
- [82] = 'r', // R
- [83] = 's', // S
- [84] = 't', // T
- [85] = 'u', // U
- [86] = 'v', // V
- [87] = 'w', // W
- [88] = 'x', // X
- [89] = 'y', // Y
- [90] = 'z', // Z
- [91] = '_', // [
- [92] = '/', // backslash
- [93] = '_', // ]
- [94] = '_', // ^
- [95] = '_', // _
- [96] = '_', // `
- [97] = 'a', // a
- [98] = 'b', // b
- [99] = 'c', // c
- [100] = 'd', // d
- [101] = 'e', // e
- [102] = 'f', // f
- [103] = 'g', // g
- [104] = 'h', // h
- [105] = 'i', // i
- [106] = 'j', // j
- [107] = 'k', // k
- [108] = 'l', // l
- [109] = 'm', // m
- [110] = 'n', // n
- [111] = 'o', // o
- [112] = 'p', // p
- [113] = 'q', // q
- [114] = 'r', // r
- [115] = 's', // s
- [116] = 't', // t
- [117] = 'u', // u
- [118] = 'v', // v
- [119] = 'w', // w
- [120] = 'x', // x
- [121] = 'y', // y
- [122] = 'z', // z
- [123] = '_', // {
- [124] = '_', // |
- [125] = '_', // }
- [126] = '_', // ~
- [127] = '_', //
- [128] = '_', //
- [129] = '_', //
- [130] = '_', //
- [131] = '_', //
- [132] = '_', //
- [133] = '_', //
- [134] = '_', //
- [135] = '_', //
- [136] = '_', //
- [137] = '_', //
- [138] = '_', //
- [139] = '_', //
- [140] = '_', //
- [141] = '_', //
- [142] = '_', //
- [143] = '_', //
- [144] = '_', //
- [145] = '_', //
- [146] = '_', //
- [147] = '_', //
- [148] = '_', //
- [149] = '_', //
- [150] = '_', //
- [151] = '_', //
- [152] = '_', //
- [153] = '_', //
- [154] = '_', //
- [155] = '_', //
- [156] = '_', //
- [157] = '_', //
- [158] = '_', //
- [159] = '_', //
- [160] = '_', //
- [161] = '_', //
- [162] = '_', //
- [163] = '_', //
- [164] = '_', //
- [165] = '_', //
- [166] = '_', //
- [167] = '_', //
- [168] = '_', //
- [169] = '_', //
- [170] = '_', //
- [171] = '_', //
- [172] = '_', //
- [173] = '_', //
- [174] = '_', //
- [175] = '_', //
- [176] = '_', //
- [177] = '_', //
- [178] = '_', //
- [179] = '_', //
- [180] = '_', //
- [181] = '_', //
- [182] = '_', //
- [183] = '_', //
- [184] = '_', //
- [185] = '_', //
- [186] = '_', //
- [187] = '_', //
- [188] = '_', //
- [189] = '_', //
- [190] = '_', //
- [191] = '_', //
- [192] = '_', //
- [193] = '_', //
- [194] = '_', //
- [195] = '_', //
- [196] = '_', //
- [197] = '_', //
- [198] = '_', //
- [199] = '_', //
- [200] = '_', //
- [201] = '_', //
- [202] = '_', //
- [203] = '_', //
- [204] = '_', //
- [205] = '_', //
- [206] = '_', //
- [207] = '_', //
- [208] = '_', //
- [209] = '_', //
- [210] = '_', //
- [211] = '_', //
- [212] = '_', //
- [213] = '_', //
- [214] = '_', //
- [215] = '_', //
- [216] = '_', //
- [217] = '_', //
- [218] = '_', //
- [219] = '_', //
- [220] = '_', //
- [221] = '_', //
- [222] = '_', //
- [223] = '_', //
- [224] = '_', //
- [225] = '_', //
- [226] = '_', //
- [227] = '_', //
- [228] = '_', //
- [229] = '_', //
- [230] = '_', //
- [231] = '_', //
- [232] = '_', //
- [233] = '_', //
- [234] = '_', //
- [235] = '_', //
- [236] = '_', //
- [237] = '_', //
- [238] = '_', //
- [239] = '_', //
- [240] = '_', //
- [241] = '_', //
- [242] = '_', //
- [243] = '_', //
- [244] = '_', //
- [245] = '_', //
- [246] = '_', //
- [247] = '_', //
- [248] = '_', //
- [249] = '_', //
- [250] = '_', //
- [251] = '_', //
- [252] = '_', //
- [253] = '_', //
- [254] = '_', //
- [255] = '_' //
+ [0] = '\0', //
+ [1] = '_', //
+ [2] = '_', //
+ [3] = '_', //
+ [4] = '_', //
+ [5] = '_', //
+ [6] = '_', //
+ [7] = '_', //
+ [8] = '_', //
+ [9] = '_', //
+ [10] = '_', //
+ [11] = '_', //
+ [12] = '_', //
+ [13] = '_', //
+ [14] = '_', //
+ [15] = '_', //
+ [16] = '_', //
+ [17] = '_', //
+ [18] = '_', //
+ [19] = '_', //
+ [20] = '_', //
+ [21] = '_', //
+ [22] = '_', //
+ [23] = '_', //
+ [24] = '_', //
+ [25] = '_', //
+ [26] = '_', //
+ [27] = '_', //
+ [28] = '_', //
+ [29] = '_', //
+ [30] = '_', //
+ [31] = '_', //
+ [32] = '_', //
+ [33] = '_', // !
+ [34] = '_', // "
+ [35] = '_', // #
+ [36] = '_', // $
+ [37] = '_', // %
+ [38] = '_', // &
+ [39] = '_', // '
+ [40] = '_', // (
+ [41] = '_', // )
+ [42] = '_', // *
+ [43] = '_', // +
+ [44] = '.', // ,
+ [45] = '-', // -
+ [46] = '.', // .
+ [47] = '_', // /
+ [48] = '0', // 0
+ [49] = '1', // 1
+ [50] = '2', // 2
+ [51] = '3', // 3
+ [52] = '4', // 4
+ [53] = '5', // 5
+ [54] = '6', // 6
+ [55] = '7', // 7
+ [56] = '8', // 8
+ [57] = '9', // 9
+ [58] = '_', // :
+ [59] = '_', // ;
+ [60] = '_', // <
+ [61] = '_', // =
+ [62] = '_', // >
+ [63] = '_', // ?
+ [64] = '_', // @
+ [65] = 'a', // A
+ [66] = 'b', // B
+ [67] = 'c', // C
+ [68] = 'd', // D
+ [69] = 'e', // E
+ [70] = 'f', // F
+ [71] = 'g', // G
+ [72] = 'h', // H
+ [73] = 'i', // I
+ [74] = 'j', // J
+ [75] = 'k', // K
+ [76] = 'l', // L
+ [77] = 'm', // M
+ [78] = 'n', // N
+ [79] = 'o', // O
+ [80] = 'p', // P
+ [81] = 'q', // Q
+ [82] = 'r', // R
+ [83] = 's', // S
+ [84] = 't', // T
+ [85] = 'u', // U
+ [86] = 'v', // V
+ [87] = 'w', // W
+ [88] = 'x', // X
+ [89] = 'y', // Y
+ [90] = 'z', // Z
+ [91] = '_', // [
+ [92] = '/', // backslash
+ [93] = '_', // ]
+ [94] = '_', // ^
+ [95] = '_', // _
+ [96] = '_', // `
+ [97] = 'a', // a
+ [98] = 'b', // b
+ [99] = 'c', // c
+ [100] = 'd', // d
+ [101] = 'e', // e
+ [102] = 'f', // f
+ [103] = 'g', // g
+ [104] = 'h', // h
+ [105] = 'i', // i
+ [106] = 'j', // j
+ [107] = 'k', // k
+ [108] = 'l', // l
+ [109] = 'm', // m
+ [110] = 'n', // n
+ [111] = 'o', // o
+ [112] = 'p', // p
+ [113] = 'q', // q
+ [114] = 'r', // r
+ [115] = 's', // s
+ [116] = 't', // t
+ [117] = 'u', // u
+ [118] = 'v', // v
+ [119] = 'w', // w
+ [120] = 'x', // x
+ [121] = 'y', // y
+ [122] = 'z', // z
+ [123] = '_', // {
+ [124] = '_', // |
+ [125] = '_', // }
+ [126] = '_', // ~
+ [127] = '_', //
+ [128] = '_', //
+ [129] = '_', //
+ [130] = '_', //
+ [131] = '_', //
+ [132] = '_', //
+ [133] = '_', //
+ [134] = '_', //
+ [135] = '_', //
+ [136] = '_', //
+ [137] = '_', //
+ [138] = '_', //
+ [139] = '_', //
+ [140] = '_', //
+ [141] = '_', //
+ [142] = '_', //
+ [143] = '_', //
+ [144] = '_', //
+ [145] = '_', //
+ [146] = '_', //
+ [147] = '_', //
+ [148] = '_', //
+ [149] = '_', //
+ [150] = '_', //
+ [151] = '_', //
+ [152] = '_', //
+ [153] = '_', //
+ [154] = '_', //
+ [155] = '_', //
+ [156] = '_', //
+ [157] = '_', //
+ [158] = '_', //
+ [159] = '_', //
+ [160] = '_', //
+ [161] = '_', //
+ [162] = '_', //
+ [163] = '_', //
+ [164] = '_', //
+ [165] = '_', //
+ [166] = '_', //
+ [167] = '_', //
+ [168] = '_', //
+ [169] = '_', //
+ [170] = '_', //
+ [171] = '_', //
+ [172] = '_', //
+ [173] = '_', //
+ [174] = '_', //
+ [175] = '_', //
+ [176] = '_', //
+ [177] = '_', //
+ [178] = '_', //
+ [179] = '_', //
+ [180] = '_', //
+ [181] = '_', //
+ [182] = '_', //
+ [183] = '_', //
+ [184] = '_', //
+ [185] = '_', //
+ [186] = '_', //
+ [187] = '_', //
+ [188] = '_', //
+ [189] = '_', //
+ [190] = '_', //
+ [191] = '_', //
+ [192] = '_', //
+ [193] = '_', //
+ [194] = '_', //
+ [195] = '_', //
+ [196] = '_', //
+ [197] = '_', //
+ [198] = '_', //
+ [199] = '_', //
+ [200] = '_', //
+ [201] = '_', //
+ [202] = '_', //
+ [203] = '_', //
+ [204] = '_', //
+ [205] = '_', //
+ [206] = '_', //
+ [207] = '_', //
+ [208] = '_', //
+ [209] = '_', //
+ [210] = '_', //
+ [211] = '_', //
+ [212] = '_', //
+ [213] = '_', //
+ [214] = '_', //
+ [215] = '_', //
+ [216] = '_', //
+ [217] = '_', //
+ [218] = '_', //
+ [219] = '_', //
+ [220] = '_', //
+ [221] = '_', //
+ [222] = '_', //
+ [223] = '_', //
+ [224] = '_', //
+ [225] = '_', //
+ [226] = '_', //
+ [227] = '_', //
+ [228] = '_', //
+ [229] = '_', //
+ [230] = '_', //
+ [231] = '_', //
+ [232] = '_', //
+ [233] = '_', //
+ [234] = '_', //
+ [235] = '_', //
+ [236] = '_', //
+ [237] = '_', //
+ [238] = '_', //
+ [239] = '_', //
+ [240] = '_', //
+ [241] = '_', //
+ [242] = '_', //
+ [243] = '_', //
+ [244] = '_', //
+ [245] = '_', //
+ [246] = '_', //
+ [247] = '_', //
+ [248] = '_', //
+ [249] = '_', //
+ [250] = '_', //
+ [251] = '_', //
+ [252] = '_', //
+ [253] = '_', //
+ [254] = '_', //
+ [255] = '_' //
};
// make sure the supplied string
// is good for a netdata chart/dimension ID/NAME
void netdata_fix_chart_id(char *s) {
- while((*s = netdata_map_chart_ids[(unsigned char)*s])) s++;
+ while ((*s = netdata_map_chart_ids[(unsigned char) *s])) s++;
}
/*
// http://stackoverflow.com/questions/7666509/hash-function-for-string
uint32_t simple_hash(const char *name)
{
- const char *s = name;
- uint32_t hash = 5381;
- int i;
+ const char *s = name;
+ uint32_t hash = 5381;
+ int i;
- while((i = *s++)) hash = ((hash << 5) + hash) + i;
+ while((i = *s++)) hash = ((hash << 5) + hash) + i;
- // fprintf(stderr, "HASH: %lu %s\n", hash, name);
+ // fprintf(stderr, "HASH: %lu %s\n", hash, name);
- return hash;
+ return hash;
}
*/
// http://isthe.com/chongo/tech/comp/fnv/#FNV-1a
uint32_t simple_hash(const char *name) {
- unsigned char *s = (unsigned char *)name;
- uint32_t hval = 0x811c9dc5;
-
- // FNV-1a algorithm
- while (*s) {
- // multiply by the 32 bit FNV magic prime mod 2^32
- // NOTE: No need to optimize with left shifts.
- // GCC will use imul instruction anyway.
- // Tested with 'gcc -O3 -S'
- //hval += (hval<<1) + (hval<<4) + (hval<<7) + (hval<<8) + (hval<<24);
- hval *= 16777619;
-
- // xor the bottom with the current octet
- hval ^= (uint32_t)*s++;
- }
-
- // fprintf(stderr, "HASH: %u = %s\n", hval, name);
- return hval;
+ unsigned char *s = (unsigned char *) name;
+ uint32_t hval = 0x811c9dc5;
+
+ // FNV-1a algorithm
+ while (*s) {
+ // multiply by the 32 bit FNV magic prime mod 2^32
+ // NOTE: No need to optimize with left shifts.
+ // GCC will use imul instruction anyway.
+ // Tested with 'gcc -O3 -S'
+ //hval += (hval<<1) + (hval<<4) + (hval<<7) + (hval<<8) + (hval<<24);
+ hval *= 16777619;
+
+ // xor the bottom with the current octet
+ hval ^= (uint32_t) *s++;
+ }
+
+ // fprintf(stderr, "HASH: %u = %s\n", hval, name);
+ return hval;
+}
+
+uint32_t simple_uhash(const char *name) {
+ unsigned char *s = (unsigned char *) name;
+ uint32_t hval = 0x811c9dc5, c;
+
+ // FNV-1a algorithm
+ while ((c = *s++)) {
+ if (unlikely(c >= 'A' && c <= 'Z')) c += 'a' - 'A';
+ hval *= 16777619;
+ hval ^= c;
+ }
+ return hval;
}
/*
// http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx
// one at a time hash
uint32_t simple_hash(const char *name) {
- unsigned char *s = (unsigned char *)name;
- uint32_t h = 0;
+ unsigned char *s = (unsigned char *)name;
+ uint32_t h = 0;
- while(*s) {
- h += *s++;
- h += (h << 10);
- h ^= (h >> 6);
- }
+ while(*s) {
+ h += *s++;
+ h += (h << 10);
+ h ^= (h >> 6);
+ }
- h += (h << 3);
- h ^= (h >> 11);
- h += (h << 15);
+ h += (h << 3);
+ h ^= (h >> 11);
+ h += (h << 15);
- // fprintf(stderr, "HASH: %u = %s\n", h, name);
+ // fprintf(stderr, "HASH: %u = %s\n", h, name);
- return h;
+ return h;
}
*/
-void strreverse(char* begin, char* end)
-{
- char aux;
-
- while (end > begin)
- {
- // clearer code.
- aux = *end;
- *end-- = *begin;
- *begin++ = aux;
- }
+void strreverse(char *begin, char *end) {
+ char aux;
+
+ while (end > begin) {
+ // clearer code.
+ aux = *end;
+ *end-- = *begin;
+ *begin++ = aux;
+ }
}
-char *mystrsep(char **ptr, char *s)
-{
- char *p = "";
- while ( p && !p[0] && *ptr ) p = strsep(ptr, s);
- return(p);
+char *mystrsep(char **ptr, char *s) {
+ char *p = "";
+ while (p && !p[0] && *ptr) p = strsep(ptr, s);
+ return (p);
}
-char *trim(char *s)
-{
- // skip leading spaces
- // and 'comments' as well!?
- while(*s && isspace(*s)) s++;
- if(!*s || *s == '#') return NULL;
-
- // skip tailing spaces
- // this way is way faster. Writes only one NUL char.
- ssize_t l = strlen(s);
- if (--l >= 0)
- {
- char *p = s + l;
- while (p > s && isspace(*p)) p--;
- *++p = '\0';
- }
-
- if(!*s) return NULL;
-
- return s;
+char *trim(char *s) {
+ // skip leading spaces
+ // and 'comments' as well!?
+ while (*s && isspace(*s)) s++;
+ if (!*s || *s == '#') return NULL;
+
+ // skip tailing spaces
+ // this way is way faster. Writes only one NUL char.
+ ssize_t l = strlen(s);
+ if (--l >= 0) {
+ char *p = s + l;
+ while (p > s && isspace(*p)) p--;
+ *++p = '\0';
+ }
+
+ if (!*s) return NULL;
+
+ return s;
}
-void *mymmap(const char *filename, size_t size, int flags, int ksm)
-{
- int fd;
- void *mem = NULL;
+void *mymmap(const char *filename, size_t size, int flags, int ksm) {
+ static int log_madvise_1 = 1;
+#ifdef MADV_MERGEABLE
+ static int log_madvise_2 = 1, log_madvise_3 = 1;
+#endif
+ int fd;
+ void *mem = NULL;
- errno = 0;
- fd = open(filename, O_RDWR|O_CREAT|O_NOATIME, 0664);
- if(fd != -1) {
- if(lseek(fd, size, SEEK_SET) == (long)size) {
- if(write(fd, "", 1) == 1) {
- if(ftruncate(fd, size))
- error("Cannot truncate file '%s' to size %ld. Will use the larger file.", filename, size);
+ errno = 0;
+ fd = open(filename, O_RDWR | O_CREAT | O_NOATIME, 0664);
+ if (fd != -1) {
+ if (lseek(fd, size, SEEK_SET) == (off_t) size) {
+ if (write(fd, "", 1) == 1) {
+ if (ftruncate(fd, size))
+ error("Cannot truncate file '%s' to size %zu. Will use the larger file.", filename, size);
#ifdef MADV_MERGEABLE
- if(flags & MAP_SHARED || !enable_ksm || !ksm) {
+ if (flags & MAP_SHARED || !enable_ksm || !ksm) {
#endif
- mem = mmap(NULL, size, PROT_READ|PROT_WRITE, flags, fd, 0);
- if(mem) {
- int advise = MADV_SEQUENTIAL|MADV_DONTFORK;
- if(flags & MAP_SHARED) advise |= MADV_WILLNEED;
-
- if(madvise(mem, size, advise) != 0)
- error("Cannot advise the kernel about the memory usage of file '%s'.", filename);
- }
+ mem = mmap(NULL, size, PROT_READ | PROT_WRITE, flags, fd, 0);
+ if (mem != MAP_FAILED) {
+ int advise = MADV_SEQUENTIAL | MADV_DONTFORK;
+ if (flags & MAP_SHARED) advise |= MADV_WILLNEED;
+
+ if (madvise(mem, size, advise) != 0 && log_madvise_1) {
+ error("Cannot advise the kernel about the memory usage of file '%s'.", filename);
+ log_madvise_1--;
+ }
+ }
#ifdef MADV_MERGEABLE
- }
- else {
- mem = mmap(NULL, size, PROT_READ|PROT_WRITE, flags|MAP_ANONYMOUS, -1, 0);
- if(mem) {
- if(lseek(fd, 0, SEEK_SET) == 0) {
- if(read(fd, mem, size) != (ssize_t)size)
- error("Cannot read from file '%s'", filename);
- }
- else
- error("Cannot seek to beginning of file '%s'.", filename);
-
- // don't use MADV_SEQUENTIAL|MADV_DONTFORK, they disable MADV_MERGEABLE
- if(madvise(mem, size, MADV_SEQUENTIAL|MADV_DONTFORK) != 0)
- error("Cannot advise the kernel about the memory usage (MADV_SEQUENTIAL|MADV_DONTFORK) of file '%s'.", filename);
-
- if(madvise(mem, size, MADV_MERGEABLE) != 0)
- error("Cannot advise the kernel about the memory usage (MADV_MERGEABLE) of file '%s'.", filename);
- }
- else
- error("Cannot allocate PRIVATE ANONYMOUS memory for KSM for file '%s'.", filename);
- }
+ } else {
+/*
+ // test - load the file into memory
+ mem = calloc(1, size);
+ if(mem) {
+ if(lseek(fd, 0, SEEK_SET) == 0) {
+ if(read(fd, mem, size) != (ssize_t)size)
+ error("Cannot read from file '%s'", filename);
+ }
+ else
+ error("Cannot seek to beginning of file '%s'.", filename);
+ }
+*/
+ mem = mmap(NULL, size, PROT_READ | PROT_WRITE, flags | MAP_ANONYMOUS, -1, 0);
+ if (mem != MAP_FAILED) {
+ if (lseek(fd, 0, SEEK_SET) == 0) {
+ if (read(fd, mem, size) != (ssize_t) size)
+ error("Cannot read from file '%s'", filename);
+ } else
+ error("Cannot seek to beginning of file '%s'.", filename);
+
+ // don't use MADV_SEQUENTIAL|MADV_DONTFORK, they disable MADV_MERGEABLE
+ if (madvise(mem, size, MADV_SEQUENTIAL | MADV_DONTFORK) != 0 && log_madvise_2) {
+ error("Cannot advise the kernel about the memory usage (MADV_SEQUENTIAL|MADV_DONTFORK) of file '%s'.",
+ filename);
+ log_madvise_2--;
+ }
+
+ if (madvise(mem, size, MADV_MERGEABLE) != 0 && log_madvise_3) {
+ error("Cannot advise the kernel about the memory usage (MADV_MERGEABLE) of file '%s'.",
+ filename);
+ log_madvise_3--;
+ }
+ } else
+ error("Cannot allocate PRIVATE ANONYMOUS memory for KSM for file '%s'.", filename);
+ }
#endif
- }
- else error("Cannot write to file '%s' at position %ld.", filename, size);
- }
- else error("Cannot seek file '%s' to size %ld.", filename, size);
+ } else
+ error("Cannot write to file '%s' at position %zu.", filename, size);
+ } else
+ error("Cannot seek file '%s' to size %zu.", filename, size);
- close(fd);
- }
- else error("Cannot create/open file '%s'.", filename);
+ close(fd);
+ } else
+ error("Cannot create/open file '%s'.", filename);
- return mem;
+ return mem;
}
-int savememory(const char *filename, void *mem, unsigned long size)
-{
- char tmpfilename[FILENAME_MAX + 1];
+int savememory(const char *filename, void *mem, size_t size) {
+ char tmpfilename[FILENAME_MAX + 1];
- snprintfz(tmpfilename, FILENAME_MAX, "%s.%ld.tmp", filename, (long)getpid());
+ snprintfz(tmpfilename, FILENAME_MAX, "%s.%ld.tmp", filename, (long) getpid());
- int fd = open(tmpfilename, O_RDWR|O_CREAT|O_NOATIME, 0664);
- if(fd < 0) {
- error("Cannot create/open file '%s'.", filename);
- return -1;
- }
+ int fd = open(tmpfilename, O_RDWR | O_CREAT | O_NOATIME, 0664);
+ if (fd < 0) {
+ error("Cannot create/open file '%s'.", filename);
+ return -1;
+ }
- if(write(fd, mem, size) != (long)size) {
- error("Cannot write to file '%s' %ld bytes.", filename, (long)size);
- close(fd);
- return -1;
- }
+ if (write(fd, mem, size) != (ssize_t) size) {
+ error("Cannot write to file '%s' %ld bytes.", filename, (long) size);
+ close(fd);
+ return -1;
+ }
- close(fd);
+ close(fd);
- int ret = 0;
- if(rename(tmpfilename, filename)) {
- error("Cannot rename '%s' to '%s'", tmpfilename, filename);
- ret = -1;
- }
+ if (rename(tmpfilename, filename)) {
+ error("Cannot rename '%s' to '%s'", tmpfilename, filename);
+ return -1;
+ }
- return ret;
+ return 0;
}
int fd_is_valid(int fd) {
@@ -760,73 +867,69 @@ int fd_is_valid(int fd) {
*/
unsigned int hz;
-void get_HZ(void)
-{
- long ticks;
+void get_HZ(void) {
+ long ticks;
- if ((ticks = sysconf(_SC_CLK_TCK)) == -1) {
- perror("sysconf");
- }
+ if ((ticks = sysconf(_SC_CLK_TCK)) == -1) {
+ perror("sysconf");
+ }
- hz = (unsigned int) ticks;
+ hz = (unsigned int) ticks;
}
-pid_t gettid(void)
-{
- return syscall(SYS_gettid);
+pid_t gettid(void) {
+ return (pid_t)syscall(SYS_gettid);
}
char *fgets_trim_len(char *buf, size_t buf_size, FILE *fp, size_t *len) {
- char *s = fgets(buf, buf_size, fp);
- if(!s) return NULL;
+ char *s = fgets(buf, (int)buf_size, fp);
+ if (!s) return NULL;
- char *t = s;
- if(*t != '\0') {
- // find the string end
- while (*++t != '\0');
+ char *t = s;
+ if (*t != '\0') {
+ // find the string end
+ while (*++t != '\0');
- // trim trailing spaces/newlines/tabs
- while (--t > s && *t == '\n')
- *t = '\0';
- }
+ // trim trailing spaces/newlines/tabs
+ while (--t > s && *t == '\n')
+ *t = '\0';
+ }
- if(len)
- *len = t - s + 1;
+ if (len)
+ *len = t - s + 1;
- return s;
+ return s;
}
char *strncpyz(char *dst, const char *src, size_t n) {
- char *p = dst;
+ char *p = dst;
- while(*src && n--)
- *dst++ = *src++;
+ while (*src && n--)
+ *dst++ = *src++;
- *dst = '\0';
+ *dst = '\0';
- return p;
+ return p;
}
int vsnprintfz(char *dst, size_t n, const char *fmt, va_list args) {
- int size;
+ int size = vsnprintf(dst, n, fmt, args);
- size = vsnprintf(dst, n, fmt, args);
+ if (unlikely((size_t) size > n)) {
+ // truncated
+ size = (int)n;
+ }
- if(unlikely((size_t)size > n)) {
- // there is bug in vsnprintf() and it returns
- // a number higher to len, but it does not
- // overflow the buffer.
- size = n;
- }
-
- dst[size] = '\0';
- return size;
+ dst[size] = '\0';
+ return size;
}
int snprintfz(char *dst, size_t n, const char *fmt, ...) {
- va_list args;
+ va_list args;
+
+ va_start(args, fmt);
+ int ret = vsnprintfz(dst, n, fmt, args);
+ va_end(args);
- va_start(args, fmt);
- return vsnprintfz(dst, n, fmt, args);
- va_end(args);
+ return ret;
}
diff --git a/src/common.h b/src/common.h
index c94f1cde5..a6e85034b 100644
--- a/src/common.h
+++ b/src/common.h
@@ -1,36 +1,152 @@
+#ifndef NETDATA_COMMON_H
+#define NETDATA_COMMON_H 1
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pthread.h>
+#include <errno.h>
+
+#include <stdio.h>
+#include <stdlib.h>
#include <stdarg.h>
-#include <sys/time.h>
+#include <stddef.h>
+
+#include <ctype.h>
+#include <string.h>
+#include <strings.h>
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <grp.h>
+#include <pwd.h>
+#include <locale.h>
+#include <malloc.h>
+#include <netdb.h>
+#include <poll.h>
+#include <signal.h>
+#include <syslog.h>
+#include <sys/mman.h>
+#include <sys/prctl.h>
#include <sys/resource.h>
-#include <stdio.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/syscall.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+#include <uuid/uuid.h>
-#ifndef NETDATA_COMMON_H
-#define NETDATA_COMMON_H 1
+#ifdef STORAGE_WITH_MATH
+#include <math.h>
+#endif
#if defined(HAVE_INTTYPES_H)
#include <inttypes.h>
#elif defined(HAVE_STDINT_H)
#include <stdint.h>
#endif
-#include <sys/types.h>
-#include <unistd.h>
+#ifdef NETDATA_WITH_ZLIB
+#include <zlib.h>
+#endif
+
+#ifndef __ATOMIC_SEQ_CST
+#define NETDATA_NO_ATOMIC_INSTRUCTIONS 1
+#endif
+
+#ifdef __GNUC__
+#define GCC_VERSION (__GNUC__ * 10000 \
+ + __GNUC_MINOR__ * 100 \
+ + __GNUC_PATCHLEVEL__)
+
+#if __x86_64__ || __ppc64__
+#define ENVIRONMENT64
+#else
+#define ENVIRONMENT32
+#endif
+
+#else // !__GNUC__
+#define NETDATA_NO_ATOMIC_INSTRUCTIONS 1
+#define ENVIRONMENT32
+#endif // __GNUC__
+
+#include "avl.h"
+#include "log.h"
+#include "global_statistics.h"
+#include "storage_number.h"
+#include "web_buffer.h"
+#include "web_buffer_svg.h"
+#include "url.h"
+#include "popen.h"
+
+#include "procfile.h"
+#include "appconfig.h"
+#include "dictionary.h"
+#include "proc_self_mountinfo.h"
+#include "plugin_checks.h"
+#include "plugin_idlejitter.h"
+#include "plugin_nfacct.h"
+#include "plugin_proc.h"
+#include "plugin_tc.h"
+#include "plugins_d.h"
+
+#include "eval.h"
+#include "health.h"
+
+#include "rrd.h"
+#include "rrd2json.h"
+
+#include "web_client.h"
+#include "web_server.h"
+
+#include "registry.h"
+#include "daemon.h"
+#include "main.h"
+#include "unit_test.h"
+
+#ifdef abs
+#undef abs
+#endif
#define abs(x) ((x < 0)? -x : x)
-#define usecdiff(now, last) (((((now)->tv_sec * 1000000ULL) + (now)->tv_usec) - (((last)->tv_sec * 1000000ULL) + (last)->tv_usec)))
+
+extern unsigned long long usec_dt(struct timeval *now, struct timeval *old);
+extern unsigned long long timeval_usec(struct timeval *tv);
+
+// #define usec_dt(now, last) (((((now)->tv_sec * 1000000ULL) + (now)->tv_usec) - (((last)->tv_sec * 1000000ULL) + (last)->tv_usec)))
extern void netdata_fix_chart_id(char *s);
extern void netdata_fix_chart_name(char *s);
extern uint32_t simple_hash(const char *name);
+extern uint32_t simple_uhash(const char *name);
+
extern void strreverse(char* begin, char* end);
extern char *mystrsep(char **ptr, char *s);
extern char *trim(char *s);
extern char *strncpyz(char *dst, const char *src, size_t n);
extern int vsnprintfz(char *dst, size_t n, const char *fmt, va_list args);
-extern int snprintfz(char *dst, size_t n, const char *fmt, ...);
+extern int snprintfz(char *dst, size_t n, const char *fmt, ...) __attribute__ (( format (printf, 3, 4)));
+
+// memory allocation functions that handle failures
+extern char *strdupz(const char *s);
+extern void *callocz(size_t nmemb, size_t size);
+extern void *mallocz(size_t size);
+extern void freez(void *ptr);
+extern void *reallocz(void *ptr, size_t size);
extern void *mymmap(const char *filename, size_t size, int flags, int ksm);
-extern int savememory(const char *filename, void *mem, unsigned long size);
+extern int savememory(const char *filename, void *mem, size_t size);
extern int fd_is_valid(int fd);
@@ -43,7 +159,8 @@ extern void get_HZ(void);
extern pid_t gettid(void);
-extern unsigned long long timems(void);
+extern unsigned long long time_usec(void);
+extern int sleep_usec(unsigned long long usec);
extern char *fgets_trim_len(char *buf, size_t buf_size, FILE *fp, size_t *len);
diff --git a/src/daemon.c b/src/daemon.c
index 2a56ae0cc..bc4614f07 100644
--- a/src/daemon.c
+++ b/src/daemon.c
@@ -1,315 +1,230 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <signal.h>
-#include <string.h>
-#include <sys/types.h>
-#include <pwd.h>
-#include <grp.h>
-#include <pthread.h>
-#include <sys/wait.h>
-#include <sys/stat.h>
-
#include "common.h"
-#include "appconfig.h"
-#include "log.h"
-#include "web_client.h"
-#include "plugins_d.h"
-#include "rrd.h"
-#include "popen.h"
-#include "main.h"
-#include "daemon.h"
+#include <sched.h>
char pidfile[FILENAME_MAX + 1] = "";
-int pidfd = -1;
-void sig_handler(int signo)
+void sig_handler_exit(int signo)
+{
+ if(signo) {
+ error_log_limit_unlimited();
+ error("Received signal %d. Exiting...", signo);
+ netdata_exit = 1;
+ }
+}
+
+void sig_handler_logrotate(int signo)
{
- if(signo)
- netdata_exit = 1;
+ if(signo) {
+ error_log_limit_reset();
+ info("Received signal %d to re-open the log files", signo);
+ reopen_all_log_files();
+ }
}
-int become_user(const char *username)
+void sig_handler_save(int signo)
{
- struct passwd *pw = getpwnam(username);
- if(!pw) {
- error("User %s is not present.", username);
- return -1;
- }
-
- uid_t uid = pw->pw_uid;
- gid_t gid = pw->pw_gid;
-
- int ngroups = sysconf(_SC_NGROUPS_MAX);
- gid_t *supplementary_groups = NULL;
- if(ngroups) {
- supplementary_groups = malloc(sizeof(gid_t) * ngroups);
- if(supplementary_groups) {
- if(getgrouplist(username, gid, supplementary_groups, &ngroups) == -1) {
- error("Cannot get supplementary groups of user '%s'.", username);
- free(supplementary_groups);
- supplementary_groups = NULL;
- ngroups = 0;
- }
- }
- else fatal("Cannot allocate memory for %d supplementary groups", ngroups);
- }
-
- if(pidfile[0] && getuid() != uid) {
- // we are dropping privileges
- if(chown(pidfile, uid, gid) != 0)
- error("Cannot chown pidfile '%s' to user '%s'", pidfile, username);
-
- else if(pidfd != -1) {
- // not need to keep it open
- close(pidfd);
- pidfd = -1;
- }
- }
- else if(pidfd != -1) {
- // not need to keep it open
- close(pidfd);
- pidfd = -1;
- }
-
- if(supplementary_groups && ngroups) {
- if(setgroups(ngroups, supplementary_groups) == -1)
- error("Cannot set supplementary groups for user '%s'", username);
-
- free(supplementary_groups);
- supplementary_groups = NULL;
- ngroups = 0;
- }
-
- if(setresgid(gid, gid, gid) != 0) {
- error("Cannot switch to user's %s group (gid: %d).", username, gid);
- return -1;
- }
-
- if(setresuid(uid, uid, uid) != 0) {
- error("Cannot switch to user %s (uid: %d).", username, uid);
- return -1;
- }
-
- if(setgid(gid) != 0) {
- error("Cannot switch to user's %s group (gid: %d).", username, gid);
- return -1;
- }
- if(setegid(gid) != 0) {
- error("Cannot effectively switch to user's %s group (gid: %d).", username, gid);
- return -1;
- }
- if(setuid(uid) != 0) {
- error("Cannot switch to user %s (uid: %d).", username, uid);
- return -1;
- }
- if(seteuid(uid) != 0) {
- error("Cannot effectively switch to user %s (uid: %d).", username, uid);
- return -1;
- }
-
- return(0);
+ if(signo) {
+ error_log_limit_reset();
+ info("Received signal %d to save the database...", signo);
+ rrdset_save_all();
+ }
+}
+
+void sig_handler_reload_health(int signo)
+{
+ if(signo) {
+ error_log_limit_reset();
+ info("Received signal %d to reload health configuration...", signo);
+ health_reload();
+ }
+}
+
+static void chown_open_file(int fd, uid_t uid, gid_t gid) {
+ if(fd == -1) return;
+
+ struct stat buf;
+
+ if(fstat(fd, &buf) == -1) {
+ error("Cannot fstat() fd %d", fd);
+ return;
+ }
+
+ if((buf.st_uid != uid || buf.st_gid != gid) && S_ISREG(buf.st_mode)) {
+ if(fchown(fd, uid, gid) == -1)
+ error("Cannot fchown() fd %d.", fd);
+ }
+}
+
+int become_user(const char *username, int pid_fd)
+{
+ struct passwd *pw = getpwnam(username);
+ if(!pw) {
+ error("User %s is not present.", username);
+ return -1;
+ }
+
+ uid_t uid = pw->pw_uid;
+ gid_t gid = pw->pw_gid;
+
+ int ngroups = (int)sysconf(_SC_NGROUPS_MAX);
+ gid_t *supplementary_groups = NULL;
+ if(ngroups) {
+ supplementary_groups = mallocz(sizeof(gid_t) * ngroups);
+ if(getgrouplist(username, gid, supplementary_groups, &ngroups) == -1) {
+ error("Cannot get supplementary groups of user '%s'.", username);
+ freez(supplementary_groups);
+ supplementary_groups = NULL;
+ ngroups = 0;
+ }
+ }
+
+ chown_open_file(STDOUT_FILENO, uid, gid);
+ chown_open_file(STDERR_FILENO, uid, gid);
+ chown_open_file(stdaccess_fd, uid, gid);
+ chown_open_file(pid_fd, uid, gid);
+
+ if(supplementary_groups && ngroups) {
+ if(setgroups(ngroups, supplementary_groups) == -1)
+ error("Cannot set supplementary groups for user '%s'", username);
+
+ freez(supplementary_groups);
+ supplementary_groups = NULL;
+ ngroups = 0;
+ }
+
+ if(setresgid(gid, gid, gid) != 0) {
+ error("Cannot switch to user's %s group (gid: %u).", username, gid);
+ return -1;
+ }
+
+ if(setresuid(uid, uid, uid) != 0) {
+ error("Cannot switch to user %s (uid: %u).", username, uid);
+ return -1;
+ }
+
+ if(setgid(gid) != 0) {
+ error("Cannot switch to user's %s group (gid: %u).", username, gid);
+ return -1;
+ }
+ if(setegid(gid) != 0) {
+ error("Cannot effectively switch to user's %s group (gid: %u).", username, gid);
+ return -1;
+ }
+ if(setuid(uid) != 0) {
+ error("Cannot switch to user %s (uid: %u).", username, uid);
+ return -1;
+ }
+ if(seteuid(uid) != 0) {
+ error("Cannot effectively switch to user %s (uid: %u).", username, uid);
+ return -1;
+ }
+
+ return(0);
+}
+
+void oom_score_adj(int score) {
+ int done = 0;
+ int fd = open("/proc/self/oom_score_adj", O_WRONLY);
+ if(fd != -1) {
+ char buf[10 + 1];
+ ssize_t len = snprintfz(buf, 10, "%d", score);
+ if(write(fd, buf, len) == len) done = 1;
+ close(fd);
+ }
+
+ if(!done)
+ error("Cannot adjust my Out-Of-Memory score to %d.", score);
+ else
+ info("Adjusted my Out-Of-Memory score to %d.", score);
+}
+
+int sched_setscheduler_idle(void) {
+#ifdef SCHED_IDLE
+ const struct sched_param param = {
+ .sched_priority = 0
+ };
+
+ int i = sched_setscheduler(0, SCHED_IDLE, &param);
+ if(i != 0)
+ error("Cannot adjust my scheduling priority to IDLE.");
+ else
+ info("Adjusted my scheduling priority to IDLE.");
+
+ return i;
+#else
+ return -1;
+#endif
}
-int become_daemon(int dont_fork, int close_all_files, const char *user, const char *input, const char *output, const char *error, const char *access, int *access_fd, FILE **access_fp)
+int become_daemon(int dont_fork, const char *user)
{
- fflush(NULL);
-
- // open the files before forking
- int input_fd = -1, output_fd = -1, error_fd = -1, dev_null;
-
- if(input && *input) {
- if((input_fd = open(input, O_RDONLY, 0666)) == -1) {
- error("Cannot open input file '%s'.", input);
- return -1;
- }
- }
-
- if(output && *output) {
- if((output_fd = open(output, O_RDWR | O_APPEND | O_CREAT, 0666)) == -1) {
- error("Cannot open output log file '%s'", output);
- if(input_fd != -1) close(input_fd);
- return -1;
- }
- }
-
- if(error && *error) {
- if((error_fd = open(error, O_RDWR | O_APPEND | O_CREAT, 0666)) == -1) {
- error("Cannot open error log file '%s'.", error);
- if(input_fd != -1) close(input_fd);
- if(output_fd != -1) close(output_fd);
- return -1;
- }
- }
-
- if(access && *access && access_fd) {
- if((*access_fd = open(access, O_RDWR | O_APPEND | O_CREAT, 0666)) == -1) {
- error("Cannot open access log file '%s'", access);
- if(input_fd != -1) close(input_fd);
- if(output_fd != -1) close(output_fd);
- if(error_fd != -1) close(error_fd);
- return -1;
- }
-
- if(access_fp) {
- *access_fp = fdopen(*access_fd, "w");
- if(!*access_fp) {
- error("Cannot migrate file's '%s' fd %d.", access, *access_fd);
- if(input_fd != -1) close(input_fd);
- if(output_fd != -1) close(output_fd);
- if(error_fd != -1) close(error_fd);
- close(*access_fd);
- *access_fd = -1;
- return -1;
- }
- if(setvbuf(*access_fp, NULL, _IOLBF, 0) != 0)
- error("Cannot set line buffering on access.log");
- }
- }
-
- if((dev_null = open("/dev/null", O_RDWR, 0666)) == -1) {
- perror("Cannot open /dev/null");
- if(input_fd != -1) close(input_fd);
- if(output_fd != -1) close(output_fd);
- if(error_fd != -1) close(error_fd);
- if(access && access_fd && *access_fd != -1) {
- close(*access_fd);
- *access_fd = -1;
- if(access_fp) {
- fclose(*access_fp);
- *access_fp = NULL;
- }
- }
- return -1;
- }
-
- // all files opened
- // lets do it
-
- if(!dont_fork) {
- int i = fork();
- if(i == -1) {
- perror("cannot fork");
- exit(1);
- }
- if(i != 0) {
- exit(0); // the parent
- }
-
- // become session leader
- if (setsid() < 0) {
- perror("Cannot become session leader.");
- exit(2);
- }
- }
-
- // fork() again
- if(!dont_fork) {
- int i = fork();
- if(i == -1) {
- perror("cannot fork");
- exit(1);
- }
- if(i != 0) {
- exit(0); // the parent
- }
- }
-
- // Set new file permissions
- umask(0);
-
- // close all files
- if(close_all_files) {
- int i;
- for(i = (int) (sysconf(_SC_OPEN_MAX) - 1); i > 0; i--)
- if(
- ((access_fd && i != *access_fd) || !access_fd)
- && i != dev_null
- && i != input_fd
- && i != output_fd
- && i != error_fd
- && fd_is_valid(i)
- ) close(i);
- }
- else {
- close(STDIN_FILENO);
- close(STDOUT_FILENO);
- close(STDERR_FILENO);
- }
-
- // put the opened files
- // to our standard file descriptors
- if(input_fd != -1) {
- if(input_fd != STDIN_FILENO) {
- dup2(input_fd, STDIN_FILENO);
- close(input_fd);
- }
- input_fd = -1;
- }
- else dup2(dev_null, STDIN_FILENO);
-
- if(output_fd != -1) {
- if(output_fd != STDOUT_FILENO) {
- dup2(output_fd, STDOUT_FILENO);
- close(output_fd);
- }
-
- if(setvbuf(stdout, NULL, _IOLBF, 0) != 0)
- error("Cannot set line buffering on debug.log");
-
- output_fd = -1;
- }
- else dup2(dev_null, STDOUT_FILENO);
-
- if(error_fd != -1) {
- if(error_fd != STDERR_FILENO) {
- dup2(error_fd, STDERR_FILENO);
- close(error_fd);
- }
-
- if(setvbuf(stderr, NULL, _IOLBF, 0) != 0)
- error("Cannot set line buffering on error.log");
-
- error_fd = -1;
- }
- else dup2(dev_null, STDERR_FILENO);
-
- // close /dev/null
- if(dev_null != STDIN_FILENO && dev_null != STDOUT_FILENO && dev_null != STDERR_FILENO)
- close(dev_null);
-
- // generate our pid file
- if(pidfile[0]) {
- pidfd = open(pidfile, O_RDWR | O_CREAT, 0644);
- if(pidfd >= 0) {
- if(ftruncate(pidfd, 0) != 0)
- error("Cannot truncate pidfile '%s'.", pidfile);
-
- char b[100];
- sprintf(b, "%d\n", getpid());
- ssize_t i = write(pidfd, b, strlen(b));
- if(i <= 0)
- error("Cannot write pidfile '%s'.", pidfile);
-
- // don't close it, we might need it at exit
- // close(pidfd);
- }
- else error("Failed to open pidfile '%s'.", pidfile);
- }
-
- if(user && *user) {
- if(become_user(user) != 0) {
- error("Cannot become user '%s'. Continuing as we are.", user);
- }
- else info("Successfully became user '%s'.", user);
- }
- else if(pidfd != -1)
- close(pidfd);
-
- return(0);
+ if(!dont_fork) {
+ int i = fork();
+ if(i == -1) {
+ perror("cannot fork");
+ exit(1);
+ }
+ if(i != 0) {
+ exit(0); // the parent
+ }
+
+ // become session leader
+ if (setsid() < 0) {
+ perror("Cannot become session leader.");
+ exit(2);
+ }
+
+ // fork() again
+ i = fork();
+ if(i == -1) {
+ perror("cannot fork");
+ exit(1);
+ }
+ if(i != 0) {
+ exit(0); // the parent
+ }
+ }
+
+ // generate our pid file
+ int pidfd = -1;
+ if(pidfile[0]) {
+ pidfd = open(pidfile, O_WRONLY | O_CREAT, 0644);
+ if(pidfd >= 0) {
+ if(ftruncate(pidfd, 0) != 0)
+ error("Cannot truncate pidfile '%s'.", pidfile);
+
+ char b[100];
+ sprintf(b, "%d\n", getpid());
+ ssize_t i = write(pidfd, b, strlen(b));
+ if(i <= 0)
+ error("Cannot write pidfile '%s'.", pidfile);
+ }
+ else error("Failed to open pidfile '%s'.", pidfile);
+ }
+
+ // Set new file permissions
+ umask(0002);
+
+ // adjust my Out-Of-Memory score
+ oom_score_adj(1000);
+
+ // never become a problem
+ if(sched_setscheduler_idle() != 0) {
+ if(nice(19) == -1) error("Cannot lower my CPU priority.");
+ else info("Set my nice value to 19.");
+ }
+
+ if(user && *user) {
+ if(become_user(user, pidfd) != 0) {
+ error("Cannot become user '%s'. Continuing as we are.", user);
+ }
+ else info("Successfully became user '%s'.", user);
+ }
+
+ if(pidfd != -1) {
+ close(pidfd);
+ pidfd = -1;
+ }
+
+ return(0);
}
diff --git a/src/daemon.h b/src/daemon.h
index 0642be3c0..b193602d6 100644
--- a/src/daemon.h
+++ b/src/daemon.h
@@ -1,15 +1,17 @@
#ifndef NETDATA_DAEMON_H
#define NETDATA_DAEMON_H 1
-extern void sig_handler(int signo);
+extern void sig_handler_exit(int signo);
+extern void sig_handler_save(int signo);
+extern void sig_handler_logrotate(int signo);
+extern void sig_handler_reload_health(int signo);
-extern int become_user(const char *username);
+extern int become_user(const char *username, int pid_fd);
-extern int become_daemon(int dont_fork, int close_all_files, const char *user, const char *input, const char *output, const char *error, const char *access, int *access_fd, FILE **access_fp);
+extern int become_daemon(int dont_fork, const char *user);
extern void netdata_cleanup_and_exit(int i);
extern char pidfile[];
-extern int pidfd;
#endif /* NETDATA_DAEMON_H */
diff --git a/src/dictionary.c b/src/dictionary.c
index 1543f4d0e..91d3b45f1 100644
--- a/src/dictionary.c
+++ b/src/dictionary.c
@@ -1,39 +1,52 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
+#include "common.h"
-#include <pthread.h>
-#include <stdlib.h>
-#include <string.h>
+// ----------------------------------------------------------------------------
+// dictionary statistics
-#include "avl.h"
-#include "common.h"
-#include "log.h"
+static inline void NETDATA_DICTIONARY_STATS_INSERTS_PLUS1(DICTIONARY *dict) {
+ if(likely(dict->stats))
+ dict->stats->inserts++;
+}
+static inline void NETDATA_DICTIONARY_STATS_DELETES_PLUS1(DICTIONARY *dict) {
+ if(likely(dict->stats))
+ dict->stats->deletes++;
+}
+static inline void NETDATA_DICTIONARY_STATS_SEARCHES_PLUS1(DICTIONARY *dict) {
+ if(likely(dict->stats))
+ dict->stats->searches++;
+}
+static inline void NETDATA_DICTIONARY_STATS_ENTRIES_PLUS1(DICTIONARY *dict) {
+ if(likely(dict->stats))
+ dict->stats->entries++;
+}
+static inline void NETDATA_DICTIONARY_STATS_ENTRIES_MINUS1(DICTIONARY *dict) {
+ if(likely(dict->stats))
+ dict->stats->entries--;
+}
-#include "dictionary.h"
// ----------------------------------------------------------------------------
// dictionary locks
static inline void dictionary_read_lock(DICTIONARY *dict) {
- if(likely(!(dict->flags & DICTIONARY_FLAG_SINGLE_THREADED))) {
- // debug(D_DICTIONARY, "Dictionary READ lock");
- pthread_rwlock_rdlock(&dict->rwlock);
- }
+ if(likely(dict->rwlock)) {
+ // debug(D_DICTIONARY, "Dictionary READ lock");
+ pthread_rwlock_rdlock(dict->rwlock);
+ }
}
static inline void dictionary_write_lock(DICTIONARY *dict) {
- if(likely(!(dict->flags & DICTIONARY_FLAG_SINGLE_THREADED))) {
- // debug(D_DICTIONARY, "Dictionary WRITE lock");
- pthread_rwlock_wrlock(&dict->rwlock);
- }
+ if(likely(dict->rwlock)) {
+ // debug(D_DICTIONARY, "Dictionary WRITE lock");
+ pthread_rwlock_wrlock(dict->rwlock);
+ }
}
static inline void dictionary_unlock(DICTIONARY *dict) {
- if(likely(!(dict->flags & DICTIONARY_FLAG_SINGLE_THREADED))) {
- // debug(D_DICTIONARY, "Dictionary UNLOCK lock");
- pthread_rwlock_unlock(&dict->rwlock);
- }
+ if(likely(dict->rwlock)) {
+ // debug(D_DICTIONARY, "Dictionary UNLOCK lock");
+ pthread_rwlock_unlock(dict->rwlock);
+ }
}
@@ -41,192 +54,196 @@ static inline void dictionary_unlock(DICTIONARY *dict) {
// avl index
static int name_value_compare(void* a, void* b) {
- if(((NAME_VALUE *)a)->hash < ((NAME_VALUE *)b)->hash) return -1;
- else if(((NAME_VALUE *)a)->hash > ((NAME_VALUE *)b)->hash) return 1;
- else return strcmp(((NAME_VALUE *)a)->name, ((NAME_VALUE *)b)->name);
+ if(((NAME_VALUE *)a)->hash < ((NAME_VALUE *)b)->hash) return -1;
+ else if(((NAME_VALUE *)a)->hash > ((NAME_VALUE *)b)->hash) return 1;
+ else return strcmp(((NAME_VALUE *)a)->name, ((NAME_VALUE *)b)->name);
}
-#define dictionary_name_value_index_add_nolock(dict, nv) do { (dict)->inserts++; avl_insert(&((dict)->values_index), (avl *)(nv)); } while(0)
-#define dictionary_name_value_index_del_nolock(dict, nv) do { (dict)->deletes++; avl_remove(&(dict->values_index), (avl *)(nv)); } while(0)
+#define dictionary_name_value_index_add_nolock(dict, nv) do { NETDATA_DICTIONARY_STATS_INSERTS_PLUS1(dict); avl_insert(&((dict)->values_index), (avl *)(nv)); } while(0)
+#define dictionary_name_value_index_del_nolock(dict, nv) do { NETDATA_DICTIONARY_STATS_DELETES_PLUS1(dict); avl_remove(&(dict->values_index), (avl *)(nv)); } while(0)
static inline NAME_VALUE *dictionary_name_value_index_find_nolock(DICTIONARY *dict, const char *name, uint32_t hash) {
- NAME_VALUE tmp;
- tmp.hash = (hash)?hash:simple_hash(name);
- tmp.name = (char *)name;
+ NAME_VALUE tmp;
+ tmp.hash = (hash)?hash:simple_hash(name);
+ tmp.name = (char *)name;
- dict->searches++;
- return (NAME_VALUE *)avl_search(&(dict->values_index), (avl *) &tmp);
+ NETDATA_DICTIONARY_STATS_SEARCHES_PLUS1(dict);
+ return (NAME_VALUE *)avl_search(&(dict->values_index), (avl *) &tmp);
}
// ----------------------------------------------------------------------------
// internal methods
static NAME_VALUE *dictionary_name_value_create_nolock(DICTIONARY *dict, const char *name, void *value, size_t value_len, uint32_t hash) {
- debug(D_DICTIONARY, "Creating name value entry for name '%s'.", name);
-
- NAME_VALUE *nv = calloc(1, sizeof(NAME_VALUE));
- if(unlikely(!nv)) fatal("Cannot allocate name_value of size %z", sizeof(NAME_VALUE));
+ debug(D_DICTIONARY, "Creating name value entry for name '%s'.", name);
- if(dict->flags & DICTIONARY_FLAG_NAME_LINK_DONT_CLONE)
- nv->name = (char *)name;
- else {
- nv->name = strdup(name);
- if (unlikely(!nv->name))
- fatal("Cannot allocate name_value.name of size %z", strlen(name));
- }
+ NAME_VALUE *nv = callocz(1, sizeof(NAME_VALUE));
- nv->hash = (hash)?hash:simple_hash(nv->name);
+ if(dict->flags & DICTIONARY_FLAG_NAME_LINK_DONT_CLONE)
+ nv->name = (char *)name;
+ else {
+ nv->name = strdupz(name);
+ }
- if(dict->flags & DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE)
- nv->value = value;
- else {
- nv->value = malloc(value_len);
- if (unlikely(!nv->value))
- fatal("Cannot allocate name_value.value of size %z", value_len);
+ nv->hash = (hash)?hash:simple_hash(nv->name);
- memcpy(nv->value, value, value_len);
- }
+ if(dict->flags & DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE)
+ nv->value = value;
+ else {
+ nv->value = mallocz(value_len);
+ memcpy(nv->value, value, value_len);
+ }
- // index it
- dictionary_name_value_index_add_nolock(dict, nv);
- dict->entries++;
+ // index it
+ dictionary_name_value_index_add_nolock(dict, nv);
+ NETDATA_DICTIONARY_STATS_ENTRIES_PLUS1(dict);
- return nv;
+ return nv;
}
static void dictionary_name_value_destroy_nolock(DICTIONARY *dict, NAME_VALUE *nv) {
- debug(D_DICTIONARY, "Destroying name value entry for name '%s'.", nv->name);
+ debug(D_DICTIONARY, "Destroying name value entry for name '%s'.", nv->name);
- dictionary_name_value_index_del_nolock(dict, nv);
+ dictionary_name_value_index_del_nolock(dict, nv);
- dict->entries--;
+ NETDATA_DICTIONARY_STATS_ENTRIES_MINUS1(dict);
- if(!(dict->flags & DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE)) {
- debug(D_REGISTRY, "Dictionary freeing value of '%s'", nv->name);
- free(nv->value);
- }
+ if(!(dict->flags & DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE)) {
+ debug(D_REGISTRY, "Dictionary freeing value of '%s'", nv->name);
+ freez(nv->value);
+ }
- if(!(dict->flags & DICTIONARY_FLAG_NAME_LINK_DONT_CLONE)) {
- debug(D_REGISTRY, "Dictionary freeing name '%s'", nv->name);
- free(nv->name);
- }
+ if(!(dict->flags & DICTIONARY_FLAG_NAME_LINK_DONT_CLONE)) {
+ debug(D_REGISTRY, "Dictionary freeing name '%s'", nv->name);
+ freez(nv->name);
+ }
- free(nv);
+ freez(nv);
}
// ----------------------------------------------------------------------------
// API - basic methods
-DICTIONARY *dictionary_create(uint32_t flags) {
- debug(D_DICTIONARY, "Creating dictionary.");
+DICTIONARY *dictionary_create(uint8_t flags) {
+ debug(D_DICTIONARY, "Creating dictionary.");
+
+ DICTIONARY *dict = callocz(1, sizeof(DICTIONARY));
- DICTIONARY *dict = calloc(1, sizeof(DICTIONARY));
- if(unlikely(!dict)) fatal("Cannot allocate DICTIONARY");
+ if(flags & DICTIONARY_FLAG_WITH_STATISTICS)
+ dict->stats = callocz(1, sizeof(struct dictionary_stats));
- avl_init(&dict->values_index, name_value_compare);
- pthread_rwlock_init(&dict->rwlock, NULL);
+ if(!(flags & DICTIONARY_FLAG_SINGLE_THREADED)) {
+ dict->rwlock = callocz(1, sizeof(pthread_rwlock_t));
+ pthread_rwlock_init(dict->rwlock, NULL);
+ }
- dict->flags = flags;
+ avl_init(&dict->values_index, name_value_compare);
+ dict->flags = flags;
- return dict;
+ return dict;
}
void dictionary_destroy(DICTIONARY *dict) {
- debug(D_DICTIONARY, "Destroying dictionary.");
+ debug(D_DICTIONARY, "Destroying dictionary.");
- dictionary_write_lock(dict);
+ dictionary_write_lock(dict);
- while(dict->values_index.root)
- dictionary_name_value_destroy_nolock(dict, (NAME_VALUE *)dict->values_index.root);
+ while(dict->values_index.root)
+ dictionary_name_value_destroy_nolock(dict, (NAME_VALUE *)dict->values_index.root);
- dictionary_unlock(dict);
+ dictionary_unlock(dict);
- free(dict);
+ if(dict->stats)
+ freez(dict->stats);
+
+ if(dict->rwlock)
+ freez(dict->rwlock);
+
+ freez(dict);
}
// ----------------------------------------------------------------------------
void *dictionary_set(DICTIONARY *dict, const char *name, void *value, size_t value_len) {
- debug(D_DICTIONARY, "SET dictionary entry with name '%s'.", name);
-
- uint32_t hash = simple_hash(name);
+ debug(D_DICTIONARY, "SET dictionary entry with name '%s'.", name);
- dictionary_write_lock(dict);
+ uint32_t hash = simple_hash(name);
- NAME_VALUE *nv = dictionary_name_value_index_find_nolock(dict, name, hash);
- if(unlikely(!nv)) {
- debug(D_DICTIONARY, "Dictionary entry with name '%s' not found. Creating a new one.", name);
+ dictionary_write_lock(dict);
- nv = dictionary_name_value_create_nolock(dict, name, value, value_len, hash);
- if(unlikely(!nv))
- fatal("Cannot create name_value.");
- }
- else {
- debug(D_DICTIONARY, "Dictionary entry with name '%s' found. Changing its value.", name);
+ NAME_VALUE *nv = dictionary_name_value_index_find_nolock(dict, name, hash);
+ if(unlikely(!nv)) {
+ debug(D_DICTIONARY, "Dictionary entry with name '%s' not found. Creating a new one.", name);
- if(dict->flags & DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE) {
- debug(D_REGISTRY, "Dictionary: linking value to '%s'", name);
- nv->value = value;
- }
- else {
- debug(D_REGISTRY, "Dictionary: cloning value to '%s'", name);
+ nv = dictionary_name_value_create_nolock(dict, name, value, value_len, hash);
+ if(unlikely(!nv))
+ fatal("Cannot create name_value.");
+ }
+ else {
+ debug(D_DICTIONARY, "Dictionary entry with name '%s' found. Changing its value.", name);
- void *value = malloc(value_len),
- *old = nv->value;
+ if(dict->flags & DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE) {
+ debug(D_REGISTRY, "Dictionary: linking value to '%s'", name);
+ nv->value = value;
+ }
+ else {
+ debug(D_REGISTRY, "Dictionary: cloning value to '%s'", name);
- if(unlikely(!nv->value))
- fatal("Cannot allocate value of size %z", value_len);
+ // copy the new value without breaking
+ // any other thread accessing the same entry
+ void *new = mallocz(value_len),
+ *old = nv->value;
- memcpy(value, value, value_len);
- nv->value = value;
+ memcpy(new, value, value_len);
+ nv->value = new;
- debug(D_REGISTRY, "Dictionary: freeing old value of '%s'", name);
- free(old);
- }
- }
+ debug(D_REGISTRY, "Dictionary: freeing old value of '%s'", name);
+ freez(old);
+ }
+ }
- dictionary_unlock(dict);
+ dictionary_unlock(dict);
- return nv->value;
+ return nv->value;
}
void *dictionary_get(DICTIONARY *dict, const char *name) {
- debug(D_DICTIONARY, "GET dictionary entry with name '%s'.", name);
+ debug(D_DICTIONARY, "GET dictionary entry with name '%s'.", name);
- dictionary_read_lock(dict);
- NAME_VALUE *nv = dictionary_name_value_index_find_nolock(dict, name, 0);
- dictionary_unlock(dict);
+ dictionary_read_lock(dict);
+ NAME_VALUE *nv = dictionary_name_value_index_find_nolock(dict, name, 0);
+ dictionary_unlock(dict);
- if(unlikely(!nv)) {
- debug(D_DICTIONARY, "Not found dictionary entry with name '%s'.", name);
- return NULL;
- }
+ if(unlikely(!nv)) {
+ debug(D_DICTIONARY, "Not found dictionary entry with name '%s'.", name);
+ return NULL;
+ }
- debug(D_DICTIONARY, "Found dictionary entry with name '%s'.", name);
- return nv->value;
+ debug(D_DICTIONARY, "Found dictionary entry with name '%s'.", name);
+ return nv->value;
}
int dictionary_del(DICTIONARY *dict, const char *name) {
- int ret;
+ int ret;
- debug(D_DICTIONARY, "DEL dictionary entry with name '%s'.", name);
+ debug(D_DICTIONARY, "DEL dictionary entry with name '%s'.", name);
- dictionary_write_lock(dict);
+ dictionary_write_lock(dict);
- NAME_VALUE *nv = dictionary_name_value_index_find_nolock(dict, name, 0);
- if(unlikely(!nv)) {
- debug(D_DICTIONARY, "Not found dictionary entry with name '%s'.", name);
- ret = -1;
- }
- else {
- debug(D_DICTIONARY, "Found dictionary entry with name '%s'.", name);
- dictionary_name_value_destroy_nolock(dict, nv);
- ret = 0;
- }
+ NAME_VALUE *nv = dictionary_name_value_index_find_nolock(dict, name, 0);
+ if(unlikely(!nv)) {
+ debug(D_DICTIONARY, "Not found dictionary entry with name '%s'.", name);
+ ret = -1;
+ }
+ else {
+ debug(D_DICTIONARY, "Found dictionary entry with name '%s'.", name);
+ dictionary_name_value_destroy_nolock(dict, nv);
+ ret = 0;
+ }
- dictionary_unlock(dict);
+ dictionary_unlock(dict);
- return ret;
+ return ret;
}
@@ -236,36 +253,36 @@ int dictionary_del(DICTIONARY *dict, const char *name) {
// do not user other dictionary calls while walking the dictionary - deadlock!
static int dictionary_walker(avl *a, int (*callback)(void *entry, void *data), void *data) {
- int total = 0, ret = 0;
+ int total = 0, ret = 0;
- if(a->right) {
- ret = dictionary_walker(a->right, callback, data);
- if(ret < 0) return ret;
- total += ret;
- }
+ if(a->avl_link[0]) {
+ ret = dictionary_walker(a->avl_link[0], callback, data);
+ if(ret < 0) return ret;
+ total += ret;
+ }
- ret = callback(((NAME_VALUE *)a)->value, data);
- if(ret < 0) return ret;
- total += ret;
+ ret = callback(((NAME_VALUE *)a)->value, data);
+ if(ret < 0) return ret;
+ total += ret;
- if(a->left) {
- dictionary_walker(a->left, callback, data);
- if (ret < 0) return ret;
- total += ret;
- }
+ if(a->avl_link[1]) {
+ ret = dictionary_walker(a->avl_link[1], callback, data);
+ if (ret < 0) return ret;
+ total += ret;
+ }
- return total;
+ return total;
}
int dictionary_get_all(DICTIONARY *dict, int (*callback)(void *entry, void *data), void *data) {
- int ret = 0;
+ int ret = 0;
- dictionary_read_lock(dict);
+ dictionary_read_lock(dict);
- if(likely(dict->values_index.root))
- ret = dictionary_walker(dict->values_index.root, callback, data);
+ if(likely(dict->values_index.root))
+ ret = dictionary_walker(dict->values_index.root, callback, data);
- dictionary_unlock(dict);
+ dictionary_unlock(dict);
- return ret;
+ return ret;
}
diff --git a/src/dictionary.h b/src/dictionary.h
index 575f28271..6bebbfa85 100644
--- a/src/dictionary.h
+++ b/src/dictionary.h
@@ -1,45 +1,44 @@
-#include <pthread.h>
-
-#include "web_buffer.h"
-#include "avl.h"
-
#ifndef NETDATA_DICTIONARY_H
#define NETDATA_DICTIONARY_H 1
+struct dictionary_stats {
+ unsigned long long inserts;
+ unsigned long long deletes;
+ unsigned long long searches;
+ unsigned long long entries;
+};
+
typedef struct name_value {
- avl avl; // the index - this has to be first!
+ avl avl; // the index - this has to be first!
- uint32_t hash; // a simple hash to speed up searching
- // we first compare hashes, and only if the hashes are equal we do string comparisons
+ uint32_t hash; // a simple hash to speed up searching
+ // we first compare hashes, and only if the hashes are equal we do string comparisons
- char *name;
- void *value;
+ char *name;
+ void *value;
} NAME_VALUE;
typedef struct dictionary {
- avl_tree values_index;
-
- uint8_t flags;
+ avl_tree values_index;
- unsigned long long inserts;
- unsigned long long deletes;
- unsigned long long searches;
- unsigned long long entries;
+ uint8_t flags;
- pthread_rwlock_t rwlock;
+ struct dictionary_stats *stats;
+ pthread_rwlock_t *rwlock;
} DICTIONARY;
-#define DICTIONARY_FLAG_DEFAULT 0x00000000
-#define DICTIONARY_FLAG_SINGLE_THREADED 0x00000001
-#define DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE 0x00000002
-#define DICTIONARY_FLAG_NAME_LINK_DONT_CLONE 0x00000004
+#define DICTIONARY_FLAG_DEFAULT 0x00000000
+#define DICTIONARY_FLAG_SINGLE_THREADED 0x00000001
+#define DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE 0x00000002
+#define DICTIONARY_FLAG_NAME_LINK_DONT_CLONE 0x00000004
+#define DICTIONARY_FLAG_WITH_STATISTICS 0x00000008
-extern DICTIONARY *dictionary_create(uint32_t flags);
+extern DICTIONARY *dictionary_create(uint8_t flags);
extern void dictionary_destroy(DICTIONARY *dict);
extern void *dictionary_set(DICTIONARY *dict, const char *name, void *value, size_t value_len);
extern void *dictionary_get(DICTIONARY *dict, const char *name);
extern int dictionary_del(DICTIONARY *dict, const char *name);
-extern int dictionary_get_all(DICTIONARY *dict, int (*callback)(void *entry, void *data), void *data);
+extern int dictionary_get_all(DICTIONARY *dict, int (*callback)(void *entry, void *d), void *data);
#endif /* NETDATA_DICTIONARY_H */
diff --git a/src/eval.c b/src/eval.c
new file mode 100644
index 000000000..8866ee95d
--- /dev/null
+++ b/src/eval.c
@@ -0,0 +1,1119 @@
+#include "common.h"
+
+// ----------------------------------------------------------------------------
+// data structures for storing the parsed expression in memory
+
+typedef struct eval_value {
+ int type;
+
+ union {
+ calculated_number number;
+ EVAL_VARIABLE *variable;
+ struct eval_node *expression;
+ };
+} EVAL_VALUE;
+
+typedef struct eval_node {
+ int id;
+ unsigned char operator;
+ int precedence;
+
+ int count;
+ EVAL_VALUE ops[];
+} EVAL_NODE;
+
+// these are used for EVAL_NODE.operator
+// they are used as internal IDs to identify an operator
+// THEY ARE NOT USED FOR PARSING OPERATORS LIKE THAT
+#define EVAL_OPERATOR_NOP '\0'
+#define EVAL_OPERATOR_EXPRESSION_OPEN '('
+#define EVAL_OPERATOR_EXPRESSION_CLOSE ')'
+#define EVAL_OPERATOR_NOT '!'
+#define EVAL_OPERATOR_PLUS '+'
+#define EVAL_OPERATOR_MINUS '-'
+#define EVAL_OPERATOR_AND '&'
+#define EVAL_OPERATOR_OR '|'
+#define EVAL_OPERATOR_GREATER_THAN_OR_EQUAL 'G'
+#define EVAL_OPERATOR_LESS_THAN_OR_EQUAL 'L'
+#define EVAL_OPERATOR_NOT_EQUAL '~'
+#define EVAL_OPERATOR_EQUAL '='
+#define EVAL_OPERATOR_LESS '<'
+#define EVAL_OPERATOR_GREATER '>'
+#define EVAL_OPERATOR_MULTIPLY '*'
+#define EVAL_OPERATOR_DIVIDE '/'
+#define EVAL_OPERATOR_SIGN_PLUS 'P'
+#define EVAL_OPERATOR_SIGN_MINUS 'M'
+#define EVAL_OPERATOR_ABS 'A'
+#define EVAL_OPERATOR_IF_THEN_ELSE '?'
+
+// ----------------------------------------------------------------------------
+// forward function definitions
+
+static inline void eval_node_free(EVAL_NODE *op);
+static inline EVAL_NODE *parse_full_expression(const char **string, int *error);
+static inline EVAL_NODE *parse_one_full_operand(const char **string, int *error);
+static inline calculated_number eval_node(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error);
+static inline void print_parsed_as_node(BUFFER *out, EVAL_NODE *op, int *error);
+static inline void print_parsed_as_constant(BUFFER *out, calculated_number n);
+
+// ----------------------------------------------------------------------------
+// evaluation of expressions
+
+static inline calculated_number eval_check_number(calculated_number n, int *error) {
+ if(unlikely(isnan(n))) {
+ *error = EVAL_ERROR_VALUE_IS_NAN;
+ return 0;
+ }
+
+ if(unlikely(isinf(n))) {
+ *error = EVAL_ERROR_VALUE_IS_INFINITE;
+ return 0;
+ }
+
+ return n;
+}
+
+static inline calculated_number eval_variable(EVAL_EXPRESSION *exp, EVAL_VARIABLE *v, int *error) {
+ static uint32_t this_hash = 0, now_hash = 0, after_hash = 0, before_hash = 0;
+ calculated_number n;
+
+ if(unlikely(this_hash == 0)) {
+ this_hash = simple_hash("this");
+ now_hash = simple_hash("now");
+ after_hash = simple_hash("after");
+ before_hash = simple_hash("before");
+ }
+
+ if(v->hash == this_hash && !strcmp(v->name, "this")) {
+ n = (exp->this)?*exp->this:NAN;
+ buffer_strcat(exp->error_msg, "[ $this = ");
+ print_parsed_as_constant(exp->error_msg, n);
+ buffer_strcat(exp->error_msg, " ] ");
+ return n;
+ }
+
+ if(v->hash == after_hash && !strcmp(v->name, "after")) {
+ n = (exp->after && *exp->after)?*exp->after:NAN;
+ buffer_strcat(exp->error_msg, "[ $after = ");
+ print_parsed_as_constant(exp->error_msg, n);
+ buffer_strcat(exp->error_msg, " ] ");
+ return n;
+ }
+
+ if(v->hash == before_hash && !strcmp(v->name, "before")) {
+ n = (exp->before && *exp->before)?*exp->before:NAN;
+ buffer_strcat(exp->error_msg, "[ $before = ");
+ print_parsed_as_constant(exp->error_msg, n);
+ buffer_strcat(exp->error_msg, " ] ");
+ return n;
+ }
+
+ if(v->hash == now_hash && !strcmp(v->name, "now")) {
+ n = time(NULL);
+ buffer_strcat(exp->error_msg, "[ $now = ");
+ print_parsed_as_constant(exp->error_msg, n);
+ buffer_strcat(exp->error_msg, " ] ");
+ return n;
+ }
+
+ if(exp->rrdcalc && health_variable_lookup(v->name, v->hash, exp->rrdcalc, &n)) {
+ buffer_sprintf(exp->error_msg, "[ $%s = ", v->name);
+ print_parsed_as_constant(exp->error_msg, n);
+ buffer_strcat(exp->error_msg, " ] ");
+ return n;
+ }
+
+ *error = EVAL_ERROR_UNKNOWN_VARIABLE;
+ buffer_sprintf(exp->error_msg, "unknown variable '%s'", v->name);
+ return 0;
+}
+
+static inline calculated_number eval_value(EVAL_EXPRESSION *exp, EVAL_VALUE *v, int *error) {
+ calculated_number n;
+
+ switch(v->type) {
+ case EVAL_VALUE_EXPRESSION:
+ n = eval_node(exp, v->expression, error);
+ break;
+
+ case EVAL_VALUE_NUMBER:
+ n = v->number;
+ break;
+
+ case EVAL_VALUE_VARIABLE:
+ n = eval_variable(exp, v->variable, error);
+ break;
+
+ default:
+ *error = EVAL_ERROR_INVALID_VALUE;
+ n = 0;
+ break;
+ }
+
+ // return eval_check_number(n, error);
+ return n;
+}
+
+static inline int is_true(calculated_number n) {
+ if(isnan(n)) return 0;
+ if(isinf(n)) return 1;
+ if(n == 0) return 0;
+ return 1;
+}
+
+calculated_number eval_and(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
+ return is_true(eval_value(exp, &op->ops[0], error)) && is_true(eval_value(exp, &op->ops[1], error));
+}
+calculated_number eval_or(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
+ return is_true(eval_value(exp, &op->ops[0], error)) || is_true(eval_value(exp, &op->ops[1], error));
+}
+calculated_number eval_greater_than_or_equal(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
+ calculated_number n1 = eval_value(exp, &op->ops[0], error);
+ calculated_number n2 = eval_value(exp, &op->ops[1], error);
+ return isgreaterequal(n1, n2);
+}
+calculated_number eval_less_than_or_equal(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
+ calculated_number n1 = eval_value(exp, &op->ops[0], error);
+ calculated_number n2 = eval_value(exp, &op->ops[1], error);
+ return islessequal(n1, n2);
+}
+calculated_number eval_equal(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
+ calculated_number n1 = eval_value(exp, &op->ops[0], error);
+ calculated_number n2 = eval_value(exp, &op->ops[1], error);
+ if(isnan(n1) && isnan(n2)) return 1;
+ if(isinf(n1) && isinf(n2)) return 1;
+ if(isnan(n1) || isnan(n2)) return 0;
+ if(isinf(n1) || isinf(n2)) return 0;
+ return n1 == n2;
+}
+calculated_number eval_not_equal(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
+ return !eval_equal(exp, op, error);
+}
+calculated_number eval_less(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
+ calculated_number n1 = eval_value(exp, &op->ops[0], error);
+ calculated_number n2 = eval_value(exp, &op->ops[1], error);
+ return isless(n1, n2);
+}
+calculated_number eval_greater(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
+ calculated_number n1 = eval_value(exp, &op->ops[0], error);
+ calculated_number n2 = eval_value(exp, &op->ops[1], error);
+ return isgreater(n1, n2);
+}
+calculated_number eval_plus(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
+ calculated_number n1 = eval_value(exp, &op->ops[0], error);
+ calculated_number n2 = eval_value(exp, &op->ops[1], error);
+ if(isnan(n1) || isnan(n2)) return NAN;
+ if(isinf(n1) || isinf(n2)) return INFINITY;
+ return n1 + n2;
+}
+calculated_number eval_minus(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
+ calculated_number n1 = eval_value(exp, &op->ops[0], error);
+ calculated_number n2 = eval_value(exp, &op->ops[1], error);
+ if(isnan(n1) || isnan(n2)) return NAN;
+ if(isinf(n1) || isinf(n2)) return INFINITY;
+ return n1 - n2;
+}
+calculated_number eval_multiply(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
+ calculated_number n1 = eval_value(exp, &op->ops[0], error);
+ calculated_number n2 = eval_value(exp, &op->ops[1], error);
+ if(isnan(n1) || isnan(n2)) return NAN;
+ if(isinf(n1) || isinf(n2)) return INFINITY;
+ return n1 * n2;
+}
+calculated_number eval_divide(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
+ calculated_number n1 = eval_value(exp, &op->ops[0], error);
+ calculated_number n2 = eval_value(exp, &op->ops[1], error);
+ if(isnan(n1) || isnan(n2)) return NAN;
+ if(isinf(n1) || isinf(n2)) return INFINITY;
+ return n1 / n2;
+}
+calculated_number eval_nop(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
+ return eval_value(exp, &op->ops[0], error);
+}
+calculated_number eval_not(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
+ return !is_true(eval_value(exp, &op->ops[0], error));
+}
+calculated_number eval_sign_plus(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
+ return eval_value(exp, &op->ops[0], error);
+}
+calculated_number eval_sign_minus(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
+ calculated_number n1 = eval_value(exp, &op->ops[0], error);
+ if(isnan(n1)) return NAN;
+ if(isinf(n1)) return INFINITY;
+ return -n1;
+}
+calculated_number eval_abs(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
+ calculated_number n1 = eval_value(exp, &op->ops[0], error);
+ if(isnan(n1)) return NAN;
+ if(isinf(n1)) return INFINITY;
+ return abs(n1);
+}
+calculated_number eval_if_then_else(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
+ if(is_true(eval_value(exp, &op->ops[0], error)))
+ return eval_value(exp, &op->ops[1], error);
+ else
+ return eval_value(exp, &op->ops[2], error);
+}
+
+static struct operator {
+ const char *print_as;
+ char precedence;
+ char parameters;
+ char isfunction;
+ calculated_number (*eval)(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error);
+} operators[256] = {
+ // this is a random access array
+ // we always access it with a known EVAL_OPERATOR_X
+
+ [EVAL_OPERATOR_IF_THEN_ELSE] = { "?", 1, 3, 0, eval_if_then_else },
+ [EVAL_OPERATOR_AND] = { "&&", 2, 2, 0, eval_and },
+ [EVAL_OPERATOR_OR] = { "||", 2, 2, 0, eval_or },
+ [EVAL_OPERATOR_GREATER_THAN_OR_EQUAL] = { ">=", 3, 2, 0, eval_greater_than_or_equal },
+ [EVAL_OPERATOR_LESS_THAN_OR_EQUAL] = { "<=", 3, 2, 0, eval_less_than_or_equal },
+ [EVAL_OPERATOR_NOT_EQUAL] = { "!=", 3, 2, 0, eval_not_equal },
+ [EVAL_OPERATOR_EQUAL] = { "==", 3, 2, 0, eval_equal },
+ [EVAL_OPERATOR_LESS] = { "<", 3, 2, 0, eval_less },
+ [EVAL_OPERATOR_GREATER] = { ">", 3, 2, 0, eval_greater },
+ [EVAL_OPERATOR_PLUS] = { "+", 4, 2, 0, eval_plus },
+ [EVAL_OPERATOR_MINUS] = { "-", 4, 2, 0, eval_minus },
+ [EVAL_OPERATOR_MULTIPLY] = { "*", 5, 2, 0, eval_multiply },
+ [EVAL_OPERATOR_DIVIDE] = { "/", 5, 2, 0, eval_divide },
+ [EVAL_OPERATOR_NOT] = { "!", 6, 1, 0, eval_not },
+ [EVAL_OPERATOR_SIGN_PLUS] = { "+", 6, 1, 0, eval_sign_plus },
+ [EVAL_OPERATOR_SIGN_MINUS] = { "-", 6, 1, 0, eval_sign_minus },
+ [EVAL_OPERATOR_ABS] = { "abs(",6,1, 1, eval_abs },
+ [EVAL_OPERATOR_NOP] = { NULL, 7, 1, 0, eval_nop },
+ [EVAL_OPERATOR_EXPRESSION_OPEN] = { NULL, 7, 1, 0, eval_nop },
+
+ // this should exist in our evaluation list
+ [EVAL_OPERATOR_EXPRESSION_CLOSE] = { NULL, 99, 1, 0, eval_nop }
+};
+
+#define eval_precedence(operator) (operators[(unsigned char)(operator)].precedence)
+
+static inline calculated_number eval_node(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
+ if(unlikely(op->count != operators[op->operator].parameters)) {
+ *error = EVAL_ERROR_INVALID_NUMBER_OF_OPERANDS;
+ return 0;
+ }
+
+ calculated_number n = operators[op->operator].eval(exp, op, error);
+
+ // return eval_check_number(n, error);
+ return n;
+}
+
+// ----------------------------------------------------------------------------
+// parsed-as generation
+
+static inline void print_parsed_as_variable(BUFFER *out, EVAL_VARIABLE *v, int *error) {
+ (void)error;
+ buffer_sprintf(out, "$%s", v->name);
+}
+
+static inline void print_parsed_as_constant(BUFFER *out, calculated_number n) {
+ if(unlikely(isnan(n))) {
+ buffer_strcat(out, "nan");
+ return;
+ }
+
+ if(unlikely(isinf(n))) {
+ buffer_strcat(out, "inf");
+ return;
+ }
+
+ char b[100+1], *s;
+ snprintfz(b, 100, CALCULATED_NUMBER_FORMAT, n);
+
+ s = &b[strlen(b) - 1];
+ while(s > b && *s == '0') {
+ *s ='\0';
+ s--;
+ }
+
+ if(s > b && *s == '.')
+ *s = '\0';
+
+ buffer_strcat(out, b);
+}
+
+static inline void print_parsed_as_value(BUFFER *out, EVAL_VALUE *v, int *error) {
+ switch(v->type) {
+ case EVAL_VALUE_EXPRESSION:
+ print_parsed_as_node(out, v->expression, error);
+ break;
+
+ case EVAL_VALUE_NUMBER:
+ print_parsed_as_constant(out, v->number);
+ break;
+
+ case EVAL_VALUE_VARIABLE:
+ print_parsed_as_variable(out, v->variable, error);
+ break;
+
+ default:
+ *error = EVAL_ERROR_INVALID_VALUE;
+ break;
+ }
+}
+
+static inline void print_parsed_as_node(BUFFER *out, EVAL_NODE *op, int *error) {
+ if(unlikely(op->count != operators[op->operator].parameters)) {
+ *error = EVAL_ERROR_INVALID_NUMBER_OF_OPERANDS;
+ return;
+ }
+
+ if(operators[op->operator].parameters == 1) {
+
+ if(operators[op->operator].print_as)
+ buffer_sprintf(out, "%s", operators[op->operator].print_as);
+
+ //if(op->operator == EVAL_OPERATOR_EXPRESSION_OPEN)
+ // buffer_strcat(out, "(");
+
+ print_parsed_as_value(out, &op->ops[0], error);
+
+ //if(op->operator == EVAL_OPERATOR_EXPRESSION_OPEN)
+ // buffer_strcat(out, ")");
+ }
+
+ else if(operators[op->operator].parameters == 2) {
+ buffer_strcat(out, "(");
+ print_parsed_as_value(out, &op->ops[0], error);
+
+ if(operators[op->operator].print_as)
+ buffer_sprintf(out, " %s ", operators[op->operator].print_as);
+
+ print_parsed_as_value(out, &op->ops[1], error);
+ buffer_strcat(out, ")");
+ }
+ else if(op->operator == EVAL_OPERATOR_IF_THEN_ELSE && operators[op->operator].parameters == 3) {
+ buffer_strcat(out, "(");
+ print_parsed_as_value(out, &op->ops[0], error);
+
+ if(operators[op->operator].print_as)
+ buffer_sprintf(out, " %s ", operators[op->operator].print_as);
+
+ print_parsed_as_value(out, &op->ops[1], error);
+ buffer_strcat(out, " : ");
+ print_parsed_as_value(out, &op->ops[2], error);
+ buffer_strcat(out, ")");
+ }
+
+ if(operators[op->operator].isfunction)
+ buffer_strcat(out, ")");
+}
+
+// ----------------------------------------------------------------------------
+// parsing expressions
+
+// skip spaces
+static inline void skip_spaces(const char **string) {
+ const char *s = *string;
+ while(isspace(*s)) s++;
+ *string = s;
+}
+
+// what character can appear just after an operator keyword
+// like NOT AND OR ?
+static inline int isoperatorterm_word(const char s) {
+ if(isspace(s) || s == '(' || s == '$' || s == '!' || s == '-' || s == '+' || isdigit(s) || !s)
+ return 1;
+
+ return 0;
+}
+
+// what character can appear just after an operator symbol?
+static inline int isoperatorterm_symbol(const char s) {
+ if(isoperatorterm_word(s) || isalpha(s))
+ return 1;
+
+ return 0;
+}
+
+// return 1 if the character should never appear in a variable
+static inline int isvariableterm(const char s) {
+ if(isalnum(s) || s == '.' || s == '_')
+ return 0;
+
+ return 1;
+}
+
+// ----------------------------------------------------------------------------
+// parse operators
+
+static inline int parse_and(const char **string) {
+ const char *s = *string;
+
+ // AND
+ if((s[0] == 'A' || s[0] == 'a') && (s[1] == 'N' || s[1] == 'n') && (s[2] == 'D' || s[2] == 'd') && isoperatorterm_word(s[3])) {
+ *string = &s[4];
+ return 1;
+ }
+
+ // &&
+ if(s[0] == '&' && s[1] == '&' && isoperatorterm_symbol(s[2])) {
+ *string = &s[2];
+ return 1;
+ }
+
+ return 0;
+}
+
+static inline int parse_or(const char **string) {
+ const char *s = *string;
+
+ // OR
+ if((s[0] == 'O' || s[0] == 'o') && (s[1] == 'R' || s[1] == 'r') && isoperatorterm_word(s[2])) {
+ *string = &s[3];
+ return 1;
+ }
+
+ // ||
+ if(s[0] == '|' && s[1] == '|' && isoperatorterm_symbol(s[2])) {
+ *string = &s[2];
+ return 1;
+ }
+
+ return 0;
+}
+
+static inline int parse_greater_than_or_equal(const char **string) {
+ const char *s = *string;
+
+ // >=
+ if(s[0] == '>' && s[1] == '=' && isoperatorterm_symbol(s[2])) {
+ *string = &s[2];
+ return 1;
+ }
+
+ return 0;
+}
+
+static inline int parse_less_than_or_equal(const char **string) {
+ const char *s = *string;
+
+ // <=
+ if (s[0] == '<' && s[1] == '=' && isoperatorterm_symbol(s[2])) {
+ *string = &s[2];
+ return 1;
+ }
+
+ return 0;
+}
+
+static inline int parse_greater(const char **string) {
+ const char *s = *string;
+
+ // >
+ if(s[0] == '>' && isoperatorterm_symbol(s[1])) {
+ *string = &s[1];
+ return 1;
+ }
+
+ return 0;
+}
+
+static inline int parse_less(const char **string) {
+ const char *s = *string;
+
+ // <
+ if(s[0] == '<' && isoperatorterm_symbol(s[1])) {
+ *string = &s[1];
+ return 1;
+ }
+
+ return 0;
+}
+
+static inline int parse_equal(const char **string) {
+ const char *s = *string;
+
+ // ==
+ if(s[0] == '=' && s[1] == '=' && isoperatorterm_symbol(s[2])) {
+ *string = &s[2];
+ return 1;
+ }
+
+ // =
+ if(s[0] == '=' && isoperatorterm_symbol(s[1])) {
+ *string = &s[1];
+ return 1;
+ }
+
+ return 0;
+}
+
+static inline int parse_not_equal(const char **string) {
+ const char *s = *string;
+
+ // !=
+ if(s[0] == '!' && s[1] == '=' && isoperatorterm_symbol(s[2])) {
+ *string = &s[2];
+ return 1;
+ }
+
+ // <>
+ if(s[0] == '<' && s[1] == '>' && isoperatorterm_symbol(s[2])) {
+ *string = &s[2];
+ }
+
+ return 0;
+}
+
+static inline int parse_not(const char **string) {
+ const char *s = *string;
+
+ // NOT
+ if((s[0] == 'N' || s[0] == 'n') && (s[1] == 'O' || s[1] == 'o') && (s[2] == 'T' || s[2] == 't') && isoperatorterm_word(s[3])) {
+ *string = &s[3];
+ return 1;
+ }
+
+ if(s[0] == '!') {
+ *string = &s[1];
+ return 1;
+ }
+
+ return 0;
+}
+
+static inline int parse_multiply(const char **string) {
+ const char *s = *string;
+
+ // *
+ if(s[0] == '*' && isoperatorterm_symbol(s[1])) {
+ *string = &s[1];
+ return 1;
+ }
+
+ return 0;
+}
+
+static inline int parse_divide(const char **string) {
+ const char *s = *string;
+
+ // /
+ if(s[0] == '/' && isoperatorterm_symbol(s[1])) {
+ *string = &s[1];
+ return 1;
+ }
+
+ return 0;
+}
+
+static inline int parse_minus(const char **string) {
+ const char *s = *string;
+
+ // -
+ if(s[0] == '-' && isoperatorterm_symbol(s[1])) {
+ *string = &s[1];
+ return 1;
+ }
+
+ return 0;
+}
+
+static inline int parse_plus(const char **string) {
+ const char *s = *string;
+
+ // +
+ if(s[0] == '+' && isoperatorterm_symbol(s[1])) {
+ *string = &s[1];
+ return 1;
+ }
+
+ return 0;
+}
+
+static inline int parse_open_subexpression(const char **string) {
+ const char *s = *string;
+
+ // (
+ if(s[0] == '(') {
+ *string = &s[1];
+ return 1;
+ }
+
+ return 0;
+}
+
+#define parse_close_function(x) parse_close_subexpression(x)
+
+static inline int parse_close_subexpression(const char **string) {
+ const char *s = *string;
+
+ // )
+ if(s[0] == ')') {
+ *string = &s[1];
+ return 1;
+ }
+
+ return 0;
+}
+
+static inline int parse_variable(const char **string, char *buffer, size_t len) {
+ const char *s = *string;
+
+ // $
+ if(s[0] == '$') {
+ size_t i = 0;
+ s++;
+
+ while(*s && !isvariableterm(*s) && i < len)
+ buffer[i++] = *s++;
+
+ buffer[i] = '\0';
+
+ if(buffer[0]) {
+ *string = &s[0];
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static inline int parse_constant(const char **string, calculated_number *number) {
+ char *end = NULL;
+ calculated_number n = strtold(*string, &end);
+ if(unlikely(!end || *string == end)) {
+ *number = 0;
+ return 0;
+ }
+ *number = n;
+ *string = end;
+ return 1;
+}
+
+static inline int parse_abs(const char **string) {
+ const char *s = *string;
+
+ // ABS
+ if((s[0] == 'A' || s[0] == 'a') && (s[1] == 'B' || s[1] == 'b') && (s[2] == 'S' || s[2] == 's') && s[3] == '(') {
+ *string = &s[3];
+ return 1;
+ }
+
+ return 0;
+}
+
+static inline int parse_if_then_else(const char **string) {
+ const char *s = *string;
+
+ // ?
+ if(s[0] == '?') {
+ *string = &s[1];
+ return 1;
+ }
+
+ return 0;
+}
+
+static struct operator_parser {
+ unsigned char id;
+ int (*parse)(const char **);
+} operator_parsers[] = {
+ // the order in this list is important!
+ // the first matching will be used
+ // so place the longer of overlapping ones
+ // at the top
+
+ { EVAL_OPERATOR_AND, parse_and },
+ { EVAL_OPERATOR_OR, parse_or },
+ { EVAL_OPERATOR_GREATER_THAN_OR_EQUAL, parse_greater_than_or_equal },
+ { EVAL_OPERATOR_LESS_THAN_OR_EQUAL, parse_less_than_or_equal },
+ { EVAL_OPERATOR_NOT_EQUAL, parse_not_equal },
+ { EVAL_OPERATOR_EQUAL, parse_equal },
+ { EVAL_OPERATOR_LESS, parse_less },
+ { EVAL_OPERATOR_GREATER, parse_greater },
+ { EVAL_OPERATOR_PLUS, parse_plus },
+ { EVAL_OPERATOR_MINUS, parse_minus },
+ { EVAL_OPERATOR_MULTIPLY, parse_multiply },
+ { EVAL_OPERATOR_DIVIDE, parse_divide },
+ { EVAL_OPERATOR_IF_THEN_ELSE, parse_if_then_else },
+
+ /* we should not put in this list the following:
+ *
+ * - NOT
+ * - (
+ * - )
+ *
+ * these are handled in code
+ */
+
+ // termination
+ { EVAL_OPERATOR_NOP, NULL }
+};
+
+static inline unsigned char parse_operator(const char **string, int *precedence) {
+ skip_spaces(string);
+
+ int i;
+ for(i = 0 ; operator_parsers[i].parse != NULL ; i++)
+ if(operator_parsers[i].parse(string)) {
+ if(precedence) *precedence = eval_precedence(operator_parsers[i].id);
+ return operator_parsers[i].id;
+ }
+
+ return EVAL_OPERATOR_NOP;
+}
+
+// ----------------------------------------------------------------------------
+// memory management
+
+static inline EVAL_NODE *eval_node_alloc(int count) {
+ static int id = 1;
+
+ EVAL_NODE *op = callocz(1, sizeof(EVAL_NODE) + (sizeof(EVAL_VALUE) * count));
+
+ op->id = id++;
+ op->operator = EVAL_OPERATOR_NOP;
+ op->precedence = eval_precedence(EVAL_OPERATOR_NOP);
+ op->count = count;
+ return op;
+}
+
+static inline void eval_node_set_value_to_node(EVAL_NODE *op, int pos, EVAL_NODE *value) {
+ if(pos >= op->count)
+ fatal("Invalid request to set position %d of OPERAND that has only %d values", pos + 1, op->count + 1);
+
+ op->ops[pos].type = EVAL_VALUE_EXPRESSION;
+ op->ops[pos].expression = value;
+}
+
+static inline void eval_node_set_value_to_constant(EVAL_NODE *op, int pos, calculated_number value) {
+ if(pos >= op->count)
+ fatal("Invalid request to set position %d of OPERAND that has only %d values", pos + 1, op->count + 1);
+
+ op->ops[pos].type = EVAL_VALUE_NUMBER;
+ op->ops[pos].number = value;
+}
+
+static inline void eval_node_set_value_to_variable(EVAL_NODE *op, int pos, const char *variable) {
+ if(pos >= op->count)
+ fatal("Invalid request to set position %d of OPERAND that has only %d values", pos + 1, op->count + 1);
+
+ op->ops[pos].type = EVAL_VALUE_VARIABLE;
+ op->ops[pos].variable = callocz(1, sizeof(EVAL_VARIABLE));
+ op->ops[pos].variable->name = strdupz(variable);
+ op->ops[pos].variable->hash = simple_hash(op->ops[pos].variable->name);
+}
+
+static inline void eval_variable_free(EVAL_VARIABLE *v) {
+ freez(v->name);
+ freez(v);
+}
+
+static inline void eval_value_free(EVAL_VALUE *v) {
+ switch(v->type) {
+ case EVAL_VALUE_EXPRESSION:
+ eval_node_free(v->expression);
+ break;
+
+ case EVAL_VALUE_VARIABLE:
+ eval_variable_free(v->variable);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static inline void eval_node_free(EVAL_NODE *op) {
+ if(op->count) {
+ int i;
+ for(i = op->count - 1; i >= 0 ;i--)
+ eval_value_free(&op->ops[i]);
+ }
+
+ freez(op);
+}
+
+// ----------------------------------------------------------------------------
+// the parsing logic
+
+// helper function to avoid allocations all over the place
+static inline EVAL_NODE *parse_next_operand_given_its_operator(const char **string, unsigned char operator_type, int *error) {
+ EVAL_NODE *sub = parse_one_full_operand(string, error);
+ if(!sub) return NULL;
+
+ EVAL_NODE *op = eval_node_alloc(1);
+ op->operator = operator_type;
+ eval_node_set_value_to_node(op, 0, sub);
+ return op;
+}
+
+// parse a full operand, including its sign or other associative operator (e.g. NOT)
+static inline EVAL_NODE *parse_one_full_operand(const char **string, int *error) {
+ char variable_buffer[EVAL_MAX_VARIABLE_NAME_LENGTH + 1];
+ EVAL_NODE *op1 = NULL;
+ calculated_number number;
+
+ *error = EVAL_ERROR_OK;
+
+ skip_spaces(string);
+ if(!(**string)) {
+ *error = EVAL_ERROR_MISSING_OPERAND;
+ return NULL;
+ }
+
+ if(parse_not(string)) {
+ op1 = parse_next_operand_given_its_operator(string, EVAL_OPERATOR_NOT, error);
+ op1->precedence = eval_precedence(EVAL_OPERATOR_NOT);
+ }
+ else if(parse_plus(string)) {
+ op1 = parse_next_operand_given_its_operator(string, EVAL_OPERATOR_SIGN_PLUS, error);
+ op1->precedence = eval_precedence(EVAL_OPERATOR_SIGN_PLUS);
+ }
+ else if(parse_minus(string)) {
+ op1 = parse_next_operand_given_its_operator(string, EVAL_OPERATOR_SIGN_MINUS, error);
+ op1->precedence = eval_precedence(EVAL_OPERATOR_SIGN_MINUS);
+ }
+ else if(parse_abs(string)) {
+ op1 = parse_next_operand_given_its_operator(string, EVAL_OPERATOR_ABS, error);
+ op1->precedence = eval_precedence(EVAL_OPERATOR_ABS);
+ }
+ else if(parse_open_subexpression(string)) {
+ EVAL_NODE *sub = parse_full_expression(string, error);
+ if(sub) {
+ op1 = eval_node_alloc(1);
+ op1->operator = EVAL_OPERATOR_EXPRESSION_OPEN;
+ op1->precedence = eval_precedence(EVAL_OPERATOR_EXPRESSION_OPEN);
+ eval_node_set_value_to_node(op1, 0, sub);
+ if(!parse_close_subexpression(string)) {
+ *error = EVAL_ERROR_MISSING_CLOSE_SUBEXPRESSION;
+ eval_node_free(op1);
+ return NULL;
+ }
+ }
+ }
+ else if(parse_variable(string, variable_buffer, EVAL_MAX_VARIABLE_NAME_LENGTH)) {
+ op1 = eval_node_alloc(1);
+ op1->operator = EVAL_OPERATOR_NOP;
+ eval_node_set_value_to_variable(op1, 0, variable_buffer);
+ }
+ else if(parse_constant(string, &number)) {
+ op1 = eval_node_alloc(1);
+ op1->operator = EVAL_OPERATOR_NOP;
+ eval_node_set_value_to_constant(op1, 0, number);
+ }
+ else if(**string)
+ *error = EVAL_ERROR_UNKNOWN_OPERAND;
+ else
+ *error = EVAL_ERROR_MISSING_OPERAND;
+
+ return op1;
+}
+
+// parse an operator and the rest of the expression
+// precedence processing is handled here
+static inline EVAL_NODE *parse_rest_of_expression(const char **string, int *error, EVAL_NODE *op1) {
+ EVAL_NODE *op2 = NULL;
+ unsigned char operator;
+ int precedence;
+
+ operator = parse_operator(string, &precedence);
+ skip_spaces(string);
+
+ if(operator != EVAL_OPERATOR_NOP) {
+ op2 = parse_one_full_operand(string, error);
+ if(!op2) {
+ // error is already reported
+ eval_node_free(op1);
+ return NULL;
+ }
+
+ EVAL_NODE *op = eval_node_alloc(operators[operator].parameters);
+ op->operator = operator;
+ op->precedence = precedence;
+
+ if(operator == EVAL_OPERATOR_IF_THEN_ELSE && op->count == 3) {
+ skip_spaces(string);
+
+ if(**string != ':') {
+ eval_node_free(op);
+ eval_node_free(op1);
+ eval_node_free(op2);
+ *error = EVAL_ERROR_IF_THEN_ELSE_MISSING_ELSE;
+ return NULL;
+ }
+ (*string)++;
+
+ skip_spaces(string);
+
+ EVAL_NODE *op3 = parse_one_full_operand(string, error);
+ if(!op3) {
+ eval_node_free(op);
+ eval_node_free(op1);
+ eval_node_free(op2);
+ // error is already reported
+ return NULL;
+ }
+
+ eval_node_set_value_to_node(op, 2, op3);
+ }
+
+ eval_node_set_value_to_node(op, 1, op2);
+
+ // precedence processing
+ // if this operator has a higher precedence compared to its next
+ // put the next operator on top of us (top = evaluated later)
+ // function recursion does the rest...
+ if(op->precedence > op1->precedence && op1->count == 2 && op1->operator != '(' && op1->ops[1].type == EVAL_VALUE_EXPRESSION) {
+ eval_node_set_value_to_node(op, 0, op1->ops[1].expression);
+ op1->ops[1].expression = op;
+ op = op1;
+ }
+ else
+ eval_node_set_value_to_node(op, 0, op1);
+
+ return parse_rest_of_expression(string, error, op);
+ }
+ else if(**string == ')') {
+ ;
+ }
+ else if(**string) {
+ eval_node_free(op1);
+ op1 = NULL;
+ *error = EVAL_ERROR_MISSING_OPERATOR;
+ }
+
+ return op1;
+}
+
+// high level function to parse an expression or a sub-expression
+static inline EVAL_NODE *parse_full_expression(const char **string, int *error) {
+ EVAL_NODE *op1 = NULL;
+
+ op1 = parse_one_full_operand(string, error);
+ if(!op1) {
+ *error = EVAL_ERROR_MISSING_OPERAND;
+ return NULL;
+ }
+
+ return parse_rest_of_expression(string, error, op1);
+}
+
+// ----------------------------------------------------------------------------
+// public API
+
+int expression_evaluate(EVAL_EXPRESSION *exp) {
+ exp->error = EVAL_ERROR_OK;
+
+ buffer_reset(exp->error_msg);
+ exp->result = eval_node(exp, (EVAL_NODE *)exp->nodes, &exp->error);
+
+ if(exp->error == EVAL_ERROR_OK)
+ exp->result = eval_check_number(exp->result, &exp->error);
+
+ if(exp->error != EVAL_ERROR_OK) {
+ exp->result = NAN;
+
+ if(buffer_strlen(exp->error_msg))
+ buffer_strcat(exp->error_msg, "; ");
+
+ buffer_sprintf(exp->error_msg, "failed to evaluate expression with error %d (%s)", exp->error, expression_strerror(exp->error));
+ return 0;
+ }
+
+ return 1;
+}
+
+EVAL_EXPRESSION *expression_parse(const char *string, const char **failed_at, int *error) {
+ const char *s = string;
+ int err = EVAL_ERROR_OK;
+ unsigned long pos = 0;
+
+ EVAL_NODE *op = parse_full_expression(&s, &err);
+
+ if(*s) {
+ if(op) {
+ eval_node_free(op);
+ op = NULL;
+ }
+ err = EVAL_ERROR_REMAINING_GARBAGE;
+ }
+
+ if (failed_at) *failed_at = s;
+ if (error) *error = err;
+
+ if(!op) {
+ pos = s - string + 1;
+ error("failed to parse expression '%s': %s at character %lu (i.e.: '%s').", string, expression_strerror(err), pos, s);
+ return NULL;
+ }
+
+ BUFFER *out = buffer_create(1024);
+ print_parsed_as_node(out, op, &err);
+ if(err != EVAL_ERROR_OK) {
+ error("failed to re-generate expression '%s' with reason: %s", string, expression_strerror(err));
+ eval_node_free(op);
+ buffer_free(out);
+ return NULL;
+ }
+
+ EVAL_EXPRESSION *exp = callocz(1, sizeof(EVAL_EXPRESSION));
+
+ exp->source = strdupz(string);
+ exp->parsed_as = strdupz(buffer_tostring(out));
+ buffer_free(out);
+
+ exp->error_msg = buffer_create(100);
+ exp->nodes = (void *)op;
+
+ return exp;
+}
+
+void expression_free(EVAL_EXPRESSION *exp) {
+ if(!exp) return;
+
+ if(exp->nodes) eval_node_free((EVAL_NODE *)exp->nodes);
+ freez((void *)exp->source);
+ freez((void *)exp->parsed_as);
+ buffer_free(exp->error_msg);
+ freez(exp);
+}
+
+const char *expression_strerror(int error) {
+ switch(error) {
+ case EVAL_ERROR_OK:
+ return "success";
+
+ case EVAL_ERROR_MISSING_CLOSE_SUBEXPRESSION:
+ return "missing closing parenthesis";
+
+ case EVAL_ERROR_UNKNOWN_OPERAND:
+ return "unknown operand";
+
+ case EVAL_ERROR_MISSING_OPERAND:
+ return "expected operand";
+
+ case EVAL_ERROR_MISSING_OPERATOR:
+ return "expected operator";
+
+ case EVAL_ERROR_REMAINING_GARBAGE:
+ return "remaining characters after expression";
+
+ case EVAL_ERROR_INVALID_VALUE:
+ return "invalid value structure - internal error";
+
+ case EVAL_ERROR_INVALID_NUMBER_OF_OPERANDS:
+ return "wrong number of operands for operation - internal error";
+
+ case EVAL_ERROR_VALUE_IS_NAN:
+ return "value is unset";
+
+ case EVAL_ERROR_VALUE_IS_INFINITE:
+ return "computed value is infinite";
+
+ case EVAL_ERROR_UNKNOWN_VARIABLE:
+ return "undefined variable";
+
+ case EVAL_ERROR_IF_THEN_ELSE_MISSING_ELSE:
+ return "missing second sub-expression of inline conditional";
+
+ default:
+ return "unknown error";
+ }
+}
diff --git a/src/eval.h b/src/eval.h
new file mode 100644
index 000000000..b509a9fa8
--- /dev/null
+++ b/src/eval.h
@@ -0,0 +1,72 @@
+#ifndef NETDATA_EVAL_H
+#define NETDATA_EVAL_H
+
+#define EVAL_MAX_VARIABLE_NAME_LENGTH 300
+
+typedef struct eval_variable {
+ char *name;
+ uint32_t hash;
+ struct rrdvar *rrdvar;
+ struct eval_variable *next;
+} EVAL_VARIABLE;
+
+typedef struct eval_expression {
+ const char *source;
+ const char *parsed_as;
+
+ calculated_number *this;
+ time_t *after;
+ time_t *before;
+
+ calculated_number result;
+
+ int error;
+ BUFFER *error_msg;
+
+ // hidden EVAL_NODE *
+ void *nodes;
+
+ // custom data to be used for looking up variables
+ struct rrdcalc *rrdcalc;
+} EVAL_EXPRESSION;
+
+#define EVAL_VALUE_INVALID 0
+#define EVAL_VALUE_NUMBER 1
+#define EVAL_VALUE_VARIABLE 2
+#define EVAL_VALUE_EXPRESSION 3
+
+// parsing and evaluation
+#define EVAL_ERROR_OK 0
+
+// parsing errors
+#define EVAL_ERROR_MISSING_CLOSE_SUBEXPRESSION 1
+#define EVAL_ERROR_UNKNOWN_OPERAND 2
+#define EVAL_ERROR_MISSING_OPERAND 3
+#define EVAL_ERROR_MISSING_OPERATOR 4
+#define EVAL_ERROR_REMAINING_GARBAGE 5
+#define EVAL_ERROR_IF_THEN_ELSE_MISSING_ELSE 6
+
+// evaluation errors
+#define EVAL_ERROR_INVALID_VALUE 101
+#define EVAL_ERROR_INVALID_NUMBER_OF_OPERANDS 102
+#define EVAL_ERROR_VALUE_IS_NAN 103
+#define EVAL_ERROR_VALUE_IS_INFINITE 104
+#define EVAL_ERROR_UNKNOWN_VARIABLE 105
+
+// parse the given string as an expression and return:
+// a pointer to an expression if it parsed OK
+// NULL in which case the pointer to error has the error code
+extern EVAL_EXPRESSION *expression_parse(const char *string, const char **failed_at, int *error);
+
+// free all resources allocated for an expression
+extern void expression_free(EVAL_EXPRESSION *op);
+
+// convert an error code to a message
+extern const char *expression_strerror(int error);
+
+// evaluate an expression and return
+// 1 = OK, the result is in: expression->result
+// 2 = FAILED, the error message is in: buffer_tostring(expression->error_msg)
+extern int expression_evaluate(EVAL_EXPRESSION *expression);
+
+#endif //NETDATA_EVAL_H
diff --git a/src/global_statistics.c b/src/global_statistics.c
index d4a04efd2..f39a4cf25 100644
--- a/src/global_statistics.c
+++ b/src/global_statistics.c
@@ -1,18 +1,262 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
+#include "common.h"
+
+volatile struct global_statistics global_statistics = {
+ .connected_clients = 0,
+ .web_requests = 0,
+ .web_usec = 0,
+ .bytes_received = 0,
+ .bytes_sent = 0,
+ .content_size = 0,
+ .compressed_content_size = 0
+};
+
+pthread_mutex_t global_statistics_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+inline void global_statistics_lock(void) {
+ pthread_mutex_lock(&global_statistics_mutex);
+}
+
+inline void global_statistics_unlock(void) {
+ pthread_mutex_unlock(&global_statistics_mutex);
+}
+
+void finished_web_request_statistics(uint64_t dt,
+ uint64_t bytes_received,
+ uint64_t bytes_sent,
+ uint64_t content_size,
+ uint64_t compressed_content_size) {
+#ifndef NETDATA_NO_ATOMIC_INSTRUCTIONS
+ uint64_t old_web_usec_max = global_statistics.web_usec_max;
+ while(dt > old_web_usec_max)
+ __atomic_compare_exchange(&global_statistics.web_usec_max, &old_web_usec_max, &dt, 1, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
+
+ __atomic_fetch_add(&global_statistics.web_requests, 1, __ATOMIC_SEQ_CST);
+ __atomic_fetch_add(&global_statistics.web_usec, dt, __ATOMIC_SEQ_CST);
+ __atomic_fetch_add(&global_statistics.bytes_received, bytes_received, __ATOMIC_SEQ_CST);
+ __atomic_fetch_add(&global_statistics.bytes_sent, bytes_sent, __ATOMIC_SEQ_CST);
+ __atomic_fetch_add(&global_statistics.content_size, content_size, __ATOMIC_SEQ_CST);
+ __atomic_fetch_add(&global_statistics.compressed_content_size, compressed_content_size, __ATOMIC_SEQ_CST);
+#else
+#warning NOT using atomic operations - using locks for global statistics
+ if (web_server_mode == WEB_SERVER_MODE_MULTI_THREADED)
+ global_statistics_lock();
+
+ if (dt > global_statistics.web_usec_max)
+ global_statistics.web_usec_max = dt;
+
+ global_statistics.web_requests++;
+ global_statistics.web_usec += dt;
+ global_statistics.bytes_received += bytes_received;
+ global_statistics.bytes_sent += bytes_sent;
+ global_statistics.content_size += content_size;
+ global_statistics.compressed_content_size += compressed_content_size;
+
+ if (web_server_mode == WEB_SERVER_MODE_MULTI_THREADED)
+ global_statistics_unlock();
#endif
-#include <pthread.h>
+}
-#include "global_statistics.h"
+void web_client_connected(void) {
+#ifndef NETDATA_NO_ATOMIC_INSTRUCTIONS
+ __atomic_fetch_add(&global_statistics.connected_clients, 1, __ATOMIC_SEQ_CST);
+#else
+ if (web_server_mode == WEB_SERVER_MODE_MULTI_THREADED)
+ global_statistics_lock();
-struct global_statistics global_statistics = { 0ULL, 0ULL, 0ULL, 0ULL };
+ global_statistics.connected_clients++;
-pthread_mutex_t global_statistics_mutex = PTHREAD_MUTEX_INITIALIZER;
+ if (web_server_mode == WEB_SERVER_MODE_MULTI_THREADED)
+ global_statistics_unlock();
+#endif
+}
+
+void web_client_disconnected(void) {
+#ifndef NETDATA_NO_ATOMIC_INSTRUCTIONS
+ __atomic_fetch_sub(&global_statistics.connected_clients, 1, __ATOMIC_SEQ_CST);
+#else
+ if (web_server_mode == WEB_SERVER_MODE_MULTI_THREADED)
+ global_statistics_lock();
-void global_statistics_lock(void) {
- pthread_mutex_lock(&global_statistics_mutex);
+ global_statistics.connected_clients--;
+
+ if (web_server_mode == WEB_SERVER_MODE_MULTI_THREADED)
+ global_statistics_unlock();
+#endif
}
-void global_statistics_unlock(void) {
- pthread_mutex_unlock(&global_statistics_mutex);
+
+inline void global_statistics_copy(struct global_statistics *gs, uint8_t options) {
+#ifndef NETDATA_NO_ATOMIC_INSTRUCTIONS
+ gs->connected_clients = __atomic_fetch_add(&global_statistics.connected_clients, 0, __ATOMIC_SEQ_CST);
+ gs->web_requests = __atomic_fetch_add(&global_statistics.web_requests, 0, __ATOMIC_SEQ_CST);
+ gs->web_usec = __atomic_fetch_add(&global_statistics.web_usec, 0, __ATOMIC_SEQ_CST);
+ gs->web_usec_max = __atomic_fetch_add(&global_statistics.web_usec_max, 0, __ATOMIC_SEQ_CST);
+ gs->bytes_received = __atomic_fetch_add(&global_statistics.bytes_received, 0, __ATOMIC_SEQ_CST);
+ gs->bytes_sent = __atomic_fetch_add(&global_statistics.bytes_sent, 0, __ATOMIC_SEQ_CST);
+ gs->content_size = __atomic_fetch_add(&global_statistics.content_size, 0, __ATOMIC_SEQ_CST);
+ gs->compressed_content_size = __atomic_fetch_add(&global_statistics.compressed_content_size, 0, __ATOMIC_SEQ_CST);
+
+ if(options & GLOBAL_STATS_RESET_WEB_USEC_MAX) {
+ uint64_t n = 0;
+ __atomic_compare_exchange(&global_statistics.web_usec_max, &gs->web_usec_max, &n, 1, __ATOMIC_SEQ_CST,
+ __ATOMIC_SEQ_CST);
+ }
+#else
+ global_statistics_lock();
+
+ memcpy(gs, (const void *)&global_statistics, sizeof(struct global_statistics));
+
+ if (options & GLOBAL_STATS_RESET_WEB_USEC_MAX)
+ global_statistics.web_usec_max = 0;
+
+ global_statistics_unlock();
+#endif
}
+
+void global_statistics_charts(void) {
+ static unsigned long long old_web_requests = 0, old_web_usec = 0,
+ old_content_size = 0, old_compressed_content_size = 0;
+
+ static collected_number compression_ratio = -1, average_response_time = -1;
+
+ static RRDSET *stcpu = NULL, *stcpu_thread = NULL, *stclients = NULL, *streqs = NULL, *stbytes = NULL, *stduration = NULL,
+ *stcompression = NULL;
+
+ struct global_statistics gs;
+ struct rusage me, thread;
+
+ global_statistics_copy(&gs, GLOBAL_STATS_RESET_WEB_USEC_MAX);
+ getrusage(RUSAGE_THREAD, &thread);
+ getrusage(RUSAGE_SELF, &me);
+
+ if (!stcpu_thread) stcpu_thread = rrdset_find("netdata.plugin_proc_cpu");
+ if (!stcpu_thread) {
+ stcpu_thread = rrdset_create("netdata", "plugin_proc_cpu", NULL, "proc.internal", NULL,
+ "NetData Proc Plugin CPU usage", "milliseconds/s", 132000, rrd_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);
+ } else rrdset_next(stcpu_thread);
+
+ rrddim_set(stcpu_thread, "user", thread.ru_utime.tv_sec * 1000000ULL + thread.ru_utime.tv_usec);
+ rrddim_set(stcpu_thread, "system", thread.ru_stime.tv_sec * 1000000ULL + thread.ru_stime.tv_usec);
+ rrdset_done(stcpu_thread);
+
+ // ----------------------------------------------------------------
+
+ if (!stcpu) stcpu = rrdset_find("netdata.server_cpu");
+ if (!stcpu) {
+ stcpu = rrdset_create("netdata", "server_cpu", NULL, "netdata", NULL, "NetData CPU usage", "milliseconds/s",
+ 130000, rrd_update_every, RRDSET_TYPE_STACKED);
+
+ rrddim_add(stcpu, "user", NULL, 1, 1000, RRDDIM_INCREMENTAL);
+ rrddim_add(stcpu, "system", NULL, 1, 1000, RRDDIM_INCREMENTAL);
+ } else rrdset_next(stcpu);
+
+ rrddim_set(stcpu, "user", me.ru_utime.tv_sec * 1000000ULL + me.ru_utime.tv_usec);
+ rrddim_set(stcpu, "system", me.ru_stime.tv_sec * 1000000ULL + me.ru_stime.tv_usec);
+ rrdset_done(stcpu);
+
+ // ----------------------------------------------------------------
+
+ if (!stclients) stclients = rrdset_find("netdata.clients");
+ if (!stclients) {
+ stclients = rrdset_create("netdata", "clients", NULL, "netdata", NULL, "NetData Web Clients",
+ "connected clients", 130200, rrd_update_every, RRDSET_TYPE_LINE);
+
+ rrddim_add(stclients, "clients", NULL, 1, 1, RRDDIM_ABSOLUTE);
+ } else rrdset_next(stclients);
+
+ rrddim_set(stclients, "clients", gs.connected_clients);
+ rrdset_done(stclients);
+
+ // ----------------------------------------------------------------
+
+ if (!streqs) streqs = rrdset_find("netdata.requests");
+ if (!streqs) {
+ streqs = rrdset_create("netdata", "requests", NULL, "netdata", NULL, "NetData Web Requests", "requests/s",
+ 130300, rrd_update_every, RRDSET_TYPE_LINE);
+
+ rrddim_add(streqs, "requests", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ } else rrdset_next(streqs);
+
+ rrddim_set(streqs, "requests", (collected_number) gs.web_requests);
+ rrdset_done(streqs);
+
+ // ----------------------------------------------------------------
+
+ if (!stbytes) stbytes = rrdset_find("netdata.net");
+ if (!stbytes) {
+ stbytes = rrdset_create("netdata", "net", NULL, "netdata", NULL, "NetData Network Traffic", "kilobits/s",
+ 130000, rrd_update_every, RRDSET_TYPE_AREA);
+
+ rrddim_add(stbytes, "in", NULL, 8, 1024, RRDDIM_INCREMENTAL);
+ rrddim_add(stbytes, "out", NULL, -8, 1024, RRDDIM_INCREMENTAL);
+ } else rrdset_next(stbytes);
+
+ rrddim_set(stbytes, "in", (collected_number) gs.bytes_received);
+ rrddim_set(stbytes, "out", (collected_number) gs.bytes_sent);
+ rrdset_done(stbytes);
+
+ // ----------------------------------------------------------------
+
+ if (!stduration) stduration = rrdset_find("netdata.response_time");
+ if (!stduration) {
+ stduration = rrdset_create("netdata", "response_time", NULL, "netdata", NULL, "NetData API Response Time",
+ "ms/request", 130400, rrd_update_every, RRDSET_TYPE_LINE);
+
+ rrddim_add(stduration, "average", NULL, 1, 1000, RRDDIM_ABSOLUTE);
+ rrddim_add(stduration, "max", NULL, 1, 1000, RRDDIM_ABSOLUTE);
+ } else rrdset_next(stduration);
+
+ uint64_t gweb_usec = gs.web_usec;
+ uint64_t gweb_requests = gs.web_requests;
+
+ uint64_t web_usec = (gweb_usec >= old_web_usec) ? gweb_usec - old_web_usec : 0;
+ uint64_t web_requests = (gweb_requests >= old_web_requests) ? gweb_requests - old_web_requests : 0;
+
+ old_web_usec = gweb_usec;
+ old_web_requests = gweb_requests;
+
+ if (web_requests)
+ average_response_time = (collected_number) (web_usec / web_requests);
+
+ if (unlikely(average_response_time != -1))
+ rrddim_set(stduration, "average", average_response_time);
+ else
+ rrddim_set(stduration, "average", 0);
+
+ rrddim_set(stduration, "max", ((gs.web_usec_max)?(collected_number)gs.web_usec_max:average_response_time));
+ rrdset_done(stduration);
+
+ // ----------------------------------------------------------------
+
+ if (!stcompression) stcompression = rrdset_find("netdata.compression_ratio");
+ if (!stcompression) {
+ stcompression = rrdset_create("netdata", "compression_ratio", NULL, "netdata", NULL,
+ "NetData API Responses Compression Savings Ratio", "percentage", 130500,
+ rrd_update_every, RRDSET_TYPE_LINE);
+
+ rrddim_add(stcompression, "savings", NULL, 1, 1000, RRDDIM_ABSOLUTE);
+ } else rrdset_next(stcompression);
+
+ // since we don't lock here to read the global statistics
+ // read the smaller value first
+ unsigned long long gcompressed_content_size = gs.compressed_content_size;
+ unsigned long long gcontent_size = gs.content_size;
+
+ unsigned long long compressed_content_size = gcompressed_content_size - old_compressed_content_size;
+ unsigned long long content_size = gcontent_size - old_content_size;
+
+ old_compressed_content_size = gcompressed_content_size;
+ old_content_size = gcontent_size;
+
+ if (content_size && content_size >= compressed_content_size)
+ compression_ratio = ((content_size - compressed_content_size) * 100 * 1000) / content_size;
+
+ if (compression_ratio != -1)
+ rrddim_set(stcompression, "savings", compression_ratio);
+
+ rrdset_done(stcompression);
+} \ No newline at end of file
diff --git a/src/global_statistics.h b/src/global_statistics.h
index ce3c3490e..d28aa4401 100644
--- a/src/global_statistics.h
+++ b/src/global_statistics.h
@@ -5,16 +5,32 @@
// global statistics
struct global_statistics {
- unsigned long long connected_clients;
- unsigned long long web_requests;
- unsigned long long bytes_received;
- unsigned long long bytes_sent;
+ volatile uint16_t connected_clients;
+ volatile uint64_t web_requests;
+ volatile uint64_t web_usec;
+ volatile uint64_t web_usec_max;
+ volatile uint64_t bytes_received;
+ volatile uint64_t bytes_sent;
+ volatile uint64_t content_size;
+ volatile uint64_t compressed_content_size;
};
-extern struct global_statistics global_statistics;
+extern volatile struct global_statistics global_statistics;
extern void global_statistics_lock(void);
extern void global_statistics_unlock(void);
+extern void finished_web_request_statistics(uint64_t dt,
+ uint64_t bytes_received,
+ uint64_t bytes_sent,
+ uint64_t content_size,
+ uint64_t compressed_content_size);
+
+extern void web_client_connected(void);
+extern void web_client_disconnected(void);
+
+#define GLOBAL_STATS_RESET_WEB_USEC_MAX 0x01
+extern void global_statistics_copy(struct global_statistics *gs, uint8_t options);
+extern void global_statistics_charts(void);
#endif /* NETDATA_GLOBAL_STATISTICS_H */
diff --git a/src/health.c b/src/health.c
new file mode 100644
index 000000000..3156cd080
--- /dev/null
+++ b/src/health.c
@@ -0,0 +1,2194 @@
+#include "common.h"
+
+#define RRDVAR_MAX_LENGTH 1024
+
+static const char *health_default_exec = PLUGINS_DIR "/alarm-email.sh";
+int health_enabled = 1;
+
+// ----------------------------------------------------------------------------
+// RRDVAR management
+
+static inline int rrdvar_fix_name(char *variable) {
+ int fixed = 0;
+ while(*variable) {
+ if (!isalnum(*variable) && *variable != '.' && *variable != '_') {
+ *variable++ = '_';
+ fixed++;
+ }
+ else
+ variable++;
+ }
+
+ return fixed;
+}
+
+int rrdvar_compare(void* a, void* b) {
+ if(((RRDVAR *)a)->hash < ((RRDVAR *)b)->hash) return -1;
+ else if(((RRDVAR *)a)->hash > ((RRDVAR *)b)->hash) return 1;
+ else return strcmp(((RRDVAR *)a)->name, ((RRDVAR *)b)->name);
+}
+
+static inline RRDVAR *rrdvar_index_add(avl_tree_lock *tree, RRDVAR *rv) {
+ RRDVAR *ret = (RRDVAR *)avl_insert_lock(tree, (avl *)(rv));
+ if(ret != rv)
+ debug(D_VARIABLES, "Request to insert RRDVAR '%s' into index failed. Already exists.", rv->name);
+
+ return ret;
+}
+
+static inline RRDVAR *rrdvar_index_del(avl_tree_lock *tree, RRDVAR *rv) {
+ RRDVAR *ret = (RRDVAR *)avl_remove_lock(tree, (avl *)(rv));
+ if(!ret)
+ error("Request to remove RRDVAR '%s' from index failed. Not Found.", rv->name);
+
+ return ret;
+}
+
+static inline RRDVAR *rrdvar_index_find(avl_tree_lock *tree, const char *name, uint32_t hash) {
+ RRDVAR tmp;
+ tmp.name = (char *)name;
+ tmp.hash = (hash)?hash:simple_hash(tmp.name);
+
+ return (RRDVAR *)avl_search_lock(tree, (avl *)&tmp);
+}
+
+static inline void rrdvar_free(RRDHOST *host, avl_tree_lock *tree, RRDVAR *rv) {
+ (void)host;
+
+ if(!rv) return;
+
+ if(tree)
+ rrdvar_index_del(tree, rv);
+
+ freez(rv->name);
+ freez(rv);
+}
+
+static inline RRDVAR *rrdvar_create_and_index(const char *scope, avl_tree_lock *tree, const char *name, int type, calculated_number *value) {
+ char *variable = strdupz(name);
+ rrdvar_fix_name(variable);
+ uint32_t hash = simple_hash(variable);
+
+ RRDVAR *rv = rrdvar_index_find(tree, variable, hash);
+ if(unlikely(!rv)) {
+ debug(D_VARIABLES, "Variable '%s' not found in scope '%s'. Creating a new one.", variable, scope);
+
+ rv = callocz(1, sizeof(RRDVAR));
+ rv->name = variable;
+ rv->hash = hash;
+ rv->type = type;
+ rv->value = value;
+
+ RRDVAR *ret = rrdvar_index_add(tree, rv);
+ if(unlikely(ret != rv)) {
+ debug(D_VARIABLES, "Variable '%s' in scope '%s' already exists", variable, scope);
+ rrdvar_free(NULL, NULL, rv);
+ rv = NULL;
+ }
+ else
+ debug(D_VARIABLES, "Variable '%s' created in scope '%s'", variable, scope);
+ }
+ else {
+ // already exists
+ freez(variable);
+ rv = NULL;
+ }
+
+ return rv;
+}
+
+// ----------------------------------------------------------------------------
+// RRDVAR lookup
+
+calculated_number rrdvar2number(RRDVAR *rv) {
+ switch(rv->type) {
+ case RRDVAR_TYPE_CALCULATED: {
+ calculated_number *n = (calculated_number *)rv->value;
+ return *n;
+ }
+
+ case RRDVAR_TYPE_TIME_T: {
+ time_t *n = (time_t *)rv->value;
+ return *n;
+ }
+
+ case RRDVAR_TYPE_COLLECTED: {
+ collected_number *n = (collected_number *)rv->value;
+ return *n;
+ }
+
+ case RRDVAR_TYPE_TOTAL: {
+ total_number *n = (total_number *)rv->value;
+ return *n;
+ }
+
+ case RRDVAR_TYPE_INT: {
+ int *n = (int *)rv->value;
+ return *n;
+ }
+
+ default:
+ error("I don't know how to convert RRDVAR type %d to calculated_number", rv->type);
+ return NAN;
+ }
+}
+
+void dump_variable(void *data) {
+ RRDVAR *rv = (RRDVAR *)data;
+ debug(D_HEALTH, "%50s : %20.5Lf", rv->name, rrdvar2number(rv));
+}
+
+int health_variable_lookup(const char *variable, uint32_t hash, RRDCALC *rc, calculated_number *result) {
+ RRDSET *st = rc->rrdset;
+ RRDVAR *rv;
+
+ if(!st) return 0;
+
+ rv = rrdvar_index_find(&st->variables_root_index, variable, hash);
+ if(rv) {
+ *result = rrdvar2number(rv);
+ return 1;
+ }
+
+ rv = rrdvar_index_find(&st->rrdfamily->variables_root_index, variable, hash);
+ if(rv) {
+ *result = rrdvar2number(rv);
+ return 1;
+ }
+
+ rv = rrdvar_index_find(&st->rrdhost->variables_root_index, variable, hash);
+ if(rv) {
+ *result = rrdvar2number(rv);
+ return 1;
+ }
+
+ debug(D_HEALTH, "Available local chart '%s' variables:", st->id);
+ avl_traverse_lock(&st->variables_root_index, dump_variable);
+
+ debug(D_HEALTH, "Available family '%s' variables:", st->rrdfamily->family);
+ avl_traverse_lock(&st->rrdfamily->variables_root_index, dump_variable);
+
+ debug(D_HEALTH, "Available host '%s' variables:", st->rrdhost->hostname);
+ avl_traverse_lock(&st->rrdhost->variables_root_index, dump_variable);
+
+ return 0;
+}
+
+// ----------------------------------------------------------------------------
+// RRDSETVAR management
+
+RRDSETVAR *rrdsetvar_create(RRDSET *st, const char *variable, int type, void *value, uint32_t options) {
+ debug(D_VARIABLES, "RRDVARSET create for chart id '%s' name '%s' with variable name '%s'", st->id, st->name, variable);
+ RRDSETVAR *rs = (RRDSETVAR *)callocz(1, sizeof(RRDSETVAR));
+
+ char buffer[RRDVAR_MAX_LENGTH + 1];
+ snprintfz(buffer, RRDVAR_MAX_LENGTH, "%s.%s", st->id, variable);
+ rs->fullid = strdupz(buffer);
+
+ snprintfz(buffer, RRDVAR_MAX_LENGTH, "%s.%s", st->name, variable);
+ rs->fullname = strdupz(buffer);
+
+ rs->variable = strdupz(variable);
+
+ rs->type = type;
+ rs->value = value;
+ rs->options = options;
+ rs->rrdset = st;
+
+ rs->local = rrdvar_create_and_index("local", &st->variables_root_index, rs->variable, rs->type, rs->value);
+ rs->family = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index, rs->fullid, rs->type, rs->value);
+ rs->host = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->fullid, rs->type, rs->value);
+ rs->family_name = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index, rs->fullname, rs->type, rs->value);
+ rs->host_name = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->fullname, rs->type, rs->value);
+
+ rs->next = st->variables;
+ st->variables = rs;
+
+ return rs;
+}
+
+void rrdsetvar_rename_all(RRDSET *st) {
+ debug(D_VARIABLES, "RRDSETVAR rename for chart id '%s' name '%s'", st->id, st->name);
+
+ // only these 2 can change name
+ // rs->family_name
+ // rs->host_name
+
+ char buffer[RRDVAR_MAX_LENGTH + 1];
+ RRDSETVAR *rs, *next = st->variables;
+ while((rs = next)) {
+ next = rs->next;
+
+ snprintfz(buffer, RRDVAR_MAX_LENGTH, "%s.%s", st->name, rs->variable);
+
+ if (strcmp(buffer, rs->fullname)) {
+ // name changed
+ rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rs->family_name);
+ rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->host_name);
+
+ freez(rs->fullname);
+ rs->fullname = strdupz(st->name);
+ rs->family_name = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index, rs->fullname, rs->type, rs->value);
+ rs->host_name = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->fullname, rs->type, rs->value);
+ }
+ }
+
+ rrdsetcalc_link_matching(st);
+}
+
+void rrdsetvar_free(RRDSETVAR *rs) {
+ RRDSET *st = rs->rrdset;
+ debug(D_VARIABLES, "RRDSETVAR free for chart id '%s' name '%s', variable '%s'", st->id, st->name, rs->variable);
+
+ rrdvar_free(st->rrdhost, &st->variables_root_index, rs->local);
+ rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rs->family);
+ rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->host);
+ rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rs->family_name);
+ rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->host_name);
+
+ if(st->variables == rs) {
+ st->variables = rs->next;
+ }
+ else {
+ RRDSETVAR *t;
+ for (t = st->variables; t && t->next != rs; t = t->next);
+ if(!t) error("RRDSETVAR '%s' not found in chart '%s' variables linked list", rs->fullname, st->id);
+ else t->next = rs->next;
+ }
+
+ freez(rs->fullid);
+ freez(rs->fullname);
+ freez(rs->variable);
+ freez(rs);
+}
+
+// ----------------------------------------------------------------------------
+// RRDDIMVAR management
+
+#define RRDDIMVAR_ID_MAX 1024
+
+RRDDIMVAR *rrddimvar_create(RRDDIM *rd, int type, const char *prefix, const char *suffix, void *value, uint32_t options) {
+ RRDSET *st = rd->rrdset;
+
+ debug(D_VARIABLES, "RRDDIMSET create for chart id '%s' name '%s', dimension id '%s', name '%s%s%s'", st->id, st->name, rd->id, (prefix)?prefix:"", rd->name, (suffix)?suffix:"");
+
+ if(!prefix) prefix = "";
+ if(!suffix) suffix = "";
+
+ char buffer[RRDDIMVAR_ID_MAX + 1];
+ RRDDIMVAR *rs = (RRDDIMVAR *)callocz(1, sizeof(RRDDIMVAR));
+
+ rs->prefix = strdupz(prefix);
+ rs->suffix = strdupz(suffix);
+
+ snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s%s%s", rs->prefix, rd->id, rs->suffix);
+ rs->id = strdupz(buffer);
+
+ snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s%s%s", rs->prefix, rd->name, rs->suffix);
+ rs->name = strdupz(buffer);
+
+ snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", rd->rrdset->id, rs->id);
+ rs->fullidid = strdupz(buffer);
+
+ snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", rd->rrdset->id, rs->name);
+ rs->fullidname = strdupz(buffer);
+
+ snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", rd->rrdset->name, rs->id);
+ rs->fullnameid = strdupz(buffer);
+
+ snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", rd->rrdset->name, rs->name);
+ rs->fullnamename = strdupz(buffer);
+
+ rs->type = type;
+ rs->value = value;
+ rs->options = options;
+ rs->rrddim = rd;
+
+ rs->local_id = rrdvar_create_and_index("local", &st->variables_root_index, rs->id, rs->type, rs->value);
+ rs->local_name = rrdvar_create_and_index("local", &st->variables_root_index, rs->name, rs->type, rs->value);
+
+ rs->family_id = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index, rs->id, rs->type, rs->value);
+ rs->family_name = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index, rs->name, rs->type, rs->value);
+
+ rs->host_fullidid = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->fullidid, rs->type, rs->value);
+ rs->host_fullidname = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->fullidname, rs->type, rs->value);
+ rs->host_fullnameid = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->fullnameid, rs->type, rs->value);
+ rs->host_fullnamename = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->fullnamename, rs->type, rs->value);
+
+ rs->next = rd->variables;
+ rd->variables = rs;
+
+ return rs;
+}
+
+void rrddimvar_rename_all(RRDDIM *rd) {
+ RRDSET *st = rd->rrdset;
+ debug(D_VARIABLES, "RRDDIMSET rename for chart id '%s' name '%s', dimension id '%s', name '%s'", st->id, st->name, rd->id, rd->name);
+
+ RRDDIMVAR *rs, *next = rd->variables;
+ while((rs = next)) {
+ next = rs->next;
+
+ if (strcmp(rd->name, rs->name)) {
+ char buffer[RRDDIMVAR_ID_MAX + 1];
+ // name changed
+
+ // name
+ rrdvar_free(st->rrdhost, &st->variables_root_index, rs->local_name);
+ freez(rs->name);
+ snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s%s%s", rs->prefix, rd->name, rs->suffix);
+ rs->name = strdupz(buffer);
+ rs->local_name = rrdvar_create_and_index("local", &st->variables_root_index, rs->name, rs->type, rs->value);
+
+ rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->host_fullidname);
+ freez(rs->fullidname);
+ snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->id, rs->name);
+ rs->fullidname = strdupz(buffer);
+ rs->host_fullidname = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index,
+ rs->fullidname, rs->type, rs->value);
+
+ // fullnameid
+ rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->host_fullnameid);
+ freez(rs->fullnameid);
+ snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->name, rs->id);
+ rs->fullnameid = strdupz(buffer);
+ rs->host_fullnameid = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index,
+ rs->fullnameid, rs->type, rs->value);
+
+ // fullnamename
+ rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->host_fullnamename);
+ freez(rs->fullnamename);
+ snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->name, rs->name);
+ rs->fullnamename = strdupz(buffer);
+ rs->host_fullnamename = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index,
+ rs->fullnamename, rs->type, rs->value);
+ }
+ }
+}
+
+void rrddimvar_free(RRDDIMVAR *rs) {
+ RRDDIM *rd = rs->rrddim;
+ RRDSET *st = rd->rrdset;
+ debug(D_VARIABLES, "RRDDIMSET free for chart id '%s' name '%s', dimension id '%s', name '%s', prefix='%s', suffix='%s'", st->id, st->name, rd->id, rd->name, rs->prefix, rs->suffix);
+
+ rrdvar_free(st->rrdhost, &st->variables_root_index, rs->local_id);
+ rrdvar_free(st->rrdhost, &st->variables_root_index, rs->local_name);
+
+ rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rs->family_id);
+ rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rs->family_name);
+
+ rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->host_fullidid);
+ rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->host_fullidname);
+ rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->host_fullnameid);
+ rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->host_fullnamename);
+
+ if(rd->variables == rs) {
+ debug(D_VARIABLES, "RRDDIMSET removing first entry for chart id '%s' name '%s', dimension id '%s', name '%s'", st->id, st->name, rd->id, rd->name);
+ rd->variables = rs->next;
+ }
+ else {
+ debug(D_VARIABLES, "RRDDIMSET removing non-first entry for chart id '%s' name '%s', dimension id '%s', name '%s'", st->id, st->name, rd->id, rd->name);
+ RRDDIMVAR *t;
+ for (t = rd->variables; t && t->next != rs; t = t->next) ;
+ if(!t) error("RRDDIMVAR '%s' not found in dimension '%s/%s' variables linked list", rs->name, st->id, rd->id);
+ else t->next = rs->next;
+ }
+
+ freez(rs->prefix);
+ freez(rs->suffix);
+ freez(rs->id);
+ freez(rs->name);
+ freez(rs->fullidid);
+ freez(rs->fullidname);
+ freez(rs->fullnameid);
+ freez(rs->fullnamename);
+ freez(rs);
+}
+
+// ----------------------------------------------------------------------------
+// RRDCALC management
+
+static inline const char *rrdcalc_status2string(int status) {
+ switch(status) {
+ case RRDCALC_STATUS_UNINITIALIZED:
+ return "UNINITIALIZED";
+
+ case RRDCALC_STATUS_UNDEFINED:
+ return "UNDEFINED";
+
+ case RRDCALC_STATUS_CLEAR:
+ return "CLEAR";
+
+ case RRDCALC_STATUS_RAISED:
+ return "RAISED";
+
+ case RRDCALC_STATUS_WARNING:
+ return "WARNING";
+
+ case RRDCALC_STATUS_CRITICAL:
+ return "CRITICAL";
+
+ default:
+ return "UNKNOWN";
+ }
+}
+
+static void rrdsetcalc_link(RRDSET *st, RRDCALC *rc) {
+ debug(D_HEALTH, "Health linking alarm '%s.%s' to chart '%s' of host '%s'", rc->chart?rc->chart:"NOCHART", rc->name, st->id, st->rrdhost->hostname);
+
+ rc->last_status_change = time(NULL);
+ rc->rrdset = st;
+
+ rc->rrdset_next = st->alarms;
+ rc->rrdset_prev = NULL;
+ st->alarms = rc;
+
+ if(rc->update_every < rc->rrdset->update_every) {
+ error("Health alarm '%s.%s' has update every %d, less than chart update every %d. Setting alarm update frequency to %d.", rc->rrdset->id, rc->name, rc->update_every, rc->rrdset->update_every, rc->rrdset->update_every);
+ rc->update_every = rc->rrdset->update_every;
+ }
+
+ if(!isnan(rc->green) && isnan(st->green)) {
+ debug(D_HEALTH, "Health alarm '%s.%s' green threshold set from %Lf to %Lf.", rc->rrdset->id, rc->name, rc->rrdset->green, rc->green);
+ st->green = rc->green;
+ }
+
+ if(!isnan(rc->red) && isnan(st->red)) {
+ debug(D_HEALTH, "Health alarm '%s.%s' red threshold set from %Lf to %Lf.", rc->rrdset->id, rc->name, rc->rrdset->red, rc->red);
+ st->red = rc->red;
+ }
+
+ rc->local = rrdvar_create_and_index("local", &st->variables_root_index, rc->name, RRDVAR_TYPE_CALCULATED, &rc->value);
+ rc->family = rrdvar_create_and_index("family", &st->rrdfamily->variables_root_index, rc->name, RRDVAR_TYPE_CALCULATED, &rc->value);
+
+ char fullname[RRDVAR_MAX_LENGTH + 1];
+ snprintfz(fullname, RRDVAR_MAX_LENGTH, "%s.%s", st->id, rc->name);
+ rc->hostid = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, fullname, RRDVAR_TYPE_CALCULATED, &rc->value);
+
+ snprintfz(fullname, RRDVAR_MAX_LENGTH, "%s.%s", st->name, rc->name);
+ rc->hostname = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, fullname, RRDVAR_TYPE_CALCULATED, &rc->value);
+
+ if(!rc->units) rc->units = strdupz(st->units);
+}
+
+static inline int rrdcalc_is_matching_this_rrdset(RRDCALC *rc, RRDSET *st) {
+ if( (rc->hash_chart == st->hash && !strcmp(rc->chart, st->id)) ||
+ (rc->hash_chart == st->hash_name && !strcmp(rc->chart, st->name)))
+ return 1;
+
+ return 0;
+}
+
+// this has to be called while the RRDHOST is locked
+inline void rrdsetcalc_link_matching(RRDSET *st) {
+ // debug(D_HEALTH, "find matching alarms for chart '%s'", st->id);
+
+ RRDCALC *rc;
+ for(rc = st->rrdhost->alarms; rc ; rc = rc->next) {
+ if(rc->rrdset) continue;
+
+ if(rrdcalc_is_matching_this_rrdset(rc, st))
+ rrdsetcalc_link(st, rc);
+ }
+}
+
+// this has to be called while the RRDHOST is locked
+inline void rrdsetcalc_unlink(RRDCALC *rc) {
+ RRDSET *st = rc->rrdset;
+
+ if(!st) {
+ error("Requested to unlink RRDCALC '%s.%s' which is not linked to any RRDSET", rc->chart?rc->chart:"NOCHART", rc->name);
+ return;
+ }
+
+ RRDHOST *host = st->rrdhost;
+
+ debug(D_HEALTH, "Health unlinking alarm '%s.%s' from chart '%s' of host '%s'", rc->chart?rc->chart:"NOCHART", rc->name, st->id, host->hostname);
+
+ // unlink it
+ if(rc->rrdset_prev)
+ rc->rrdset_prev->rrdset_next = rc->rrdset_next;
+
+ if(rc->rrdset_next)
+ rc->rrdset_next->rrdset_prev = rc->rrdset_prev;
+
+ if(st->alarms == rc)
+ st->alarms = rc->rrdset_next;
+
+ rc->rrdset_prev = rc->rrdset_next = NULL;
+
+ rrdvar_free(st->rrdhost, &st->variables_root_index, rc->local);
+ rc->local = NULL;
+
+ rrdvar_free(st->rrdhost, &st->rrdfamily->variables_root_index, rc->family);
+ rc->family = NULL;
+
+ rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rc->hostid);
+ rc->hostid = NULL;
+
+ rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rc->hostname);
+ rc->hostname = NULL;
+
+ rc->rrdset = NULL;
+
+ // RRDCALC will remain in RRDHOST
+ // so that if the matching chart is found in the future
+ // it will be applied automatically
+}
+
+RRDCALC *rrdcalc_find(RRDSET *st, const char *name) {
+ RRDCALC *rc;
+ uint32_t hash = simple_hash(name);
+
+ for( rc = st->alarms; rc ; rc = rc->rrdset_next ) {
+ if(rc->hash == hash && !strcmp(rc->name, name))
+ return rc;
+ }
+
+ return NULL;
+}
+
+static inline int rrdcalc_exists(RRDHOST *host, const char *name, uint32_t hash) {
+ RRDCALC *rc;
+
+ // make sure it does not already exist
+ for(rc = host->alarms; rc ; rc = rc->next) {
+ if (rc->hash == hash && !strcmp(name, rc->name)) {
+ error("Health alarm '%s' already exists in host '%s'.", name, host->hostname);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static inline void rrdcalc_create_part2(RRDHOST *host, RRDCALC *rc) {
+ rrdhost_check_rdlock(host);
+
+ if(rc->calculation) {
+ rc->calculation->this = &rc->value;
+ rc->calculation->after = &rc->db_after;
+ rc->calculation->before = &rc->db_before;
+ rc->calculation->rrdcalc = rc;
+ }
+
+ if(rc->warning) {
+ rc->warning->this = &rc->value;
+ rc->warning->after = &rc->db_after;
+ rc->warning->before = &rc->db_before;
+ rc->warning->rrdcalc = rc;
+ }
+
+ if(rc->critical) {
+ rc->critical->this = &rc->value;
+ rc->critical->after = &rc->db_after;
+ rc->critical->before = &rc->db_before;
+ rc->critical->rrdcalc = rc;
+ }
+
+ // link it to the host
+ rc->next = host->alarms;
+ host->alarms = rc;
+
+ // link it to its chart
+ RRDSET *st;
+ for(st = host->rrdset_root; st ; st = st->next) {
+ if(rrdcalc_is_matching_this_rrdset(rc, st)) {
+ rrdsetcalc_link(st, rc);
+ break;
+ }
+ }
+}
+
+static inline uint32_t rrdcalc_fullname(char *fullname, size_t len, const char *chart, const char *name) {
+ snprintfz(fullname, len - 1, "%s%s%s", chart?chart:"", chart?".":"", name);
+ rrdvar_fix_name(fullname);
+ return simple_hash(fullname);
+}
+
+static inline RRDCALC *rrdcalc_create(RRDHOST *host, const char *name, const char *chart, const char *dimensions,
+ const char *units, const char *info,
+ int group_method, int after, int before, int update_every, uint32_t options,
+ calculated_number green, calculated_number red,
+ const char *exec, const char *source,
+ const char *calc, const char *warn, const char *crit) {
+
+ char fullname[RRDVAR_MAX_LENGTH + 1];
+ uint32_t hash = rrdcalc_fullname(fullname, RRDVAR_MAX_LENGTH + 1, chart, name);
+
+ if(rrdcalc_exists(host, fullname, hash))
+ return NULL;
+
+ RRDCALC *rc = callocz(1, sizeof(RRDCALC));
+
+ rc->name = strdupz(name);
+ rc->hash = simple_hash(rc->name);
+
+ rc->chart = strdupz(chart);
+ rc->hash_chart = simple_hash(rc->chart);
+
+ if(dimensions) rc->dimensions = strdupz(dimensions);
+
+ rc->green = green;
+ rc->red = red;
+ rc->value = NAN;
+ rc->old_value = NAN;
+
+ rc->group = group_method;
+ rc->after = after;
+ rc->before = before;
+ rc->update_every = update_every;
+ rc->options = options;
+
+ if(exec) rc->exec = strdupz(exec);
+ if(source) rc->source = strdupz(source);
+ if(units) rc->units = strdupz(units);
+ if(info) rc->info = strdupz(info);
+
+ if(calc) {
+ rc->calculation = expression_parse(calc, NULL, NULL);
+ if(!rc->calculation)
+ error("Health alarm '%s.%s': failed to parse calculation expression '%s'", chart, name, calc);
+ }
+ if(warn) {
+ rc->warning = expression_parse(warn, NULL, NULL);
+ if(!rc->warning)
+ error("Health alarm '%s.%s': failed to re-parse warning expression '%s'", chart, name, warn);
+ }
+ if(crit) {
+ rc->critical = expression_parse(crit, NULL, NULL);
+ if(!rc->critical)
+ error("Health alarm '%s.%s': failed to re-parse critical expression '%s'", chart, name, crit);
+ }
+
+ debug(D_HEALTH, "Health runtime added alarm '%s.%s': exec '%s', green %Lf, red %Lf, lookup: group %d, after %d, before %d, options %u, dimensions '%s', update every %d, calculation '%s', warning '%s', critical '%s', source '%s",
+ (rc->chart)?rc->chart:"NOCHART",
+ rc->name,
+ (rc->exec)?rc->exec:"DEFAULT",
+ rc->green,
+ rc->red,
+ rc->group,
+ rc->after,
+ rc->before,
+ rc->options,
+ (rc->dimensions)?rc->dimensions:"NONE",
+ rc->update_every,
+ (rc->calculation)?rc->calculation->parsed_as:"NONE",
+ (rc->warning)?rc->warning->parsed_as:"NONE",
+ (rc->critical)?rc->critical->parsed_as:"NONE",
+ rc->source
+ );
+
+ rrdcalc_create_part2(host, rc);
+ return rc;
+}
+
+void rrdcalc_free(RRDHOST *host, RRDCALC *rc) {
+ if(!rc) return;
+
+ debug(D_HEALTH, "Health removing alarm '%s.%s' of host '%s'", rc->chart?rc->chart:"NOCHART", rc->name, host->hostname);
+
+ // unlink it from RRDSET
+ if(rc->rrdset) rrdsetcalc_unlink(rc);
+
+ // unlink it from RRDHOST
+ if(rc == host->alarms)
+ host->alarms = rc->next;
+
+ else if(host->alarms) {
+ RRDCALC *t, *last = host->alarms;
+
+ for(t = last->next; t && t != rc; last = t, t = t->next) ;
+ if(last && last->next == rc)
+ last->next = rc->next;
+ else
+ error("Cannot unlink alarm '%s.%s' from host '%s': not found", rc->chart?rc->chart:"NOCHART", rc->name, host->hostname);
+ }
+ else
+ error("Cannot unlink unlink '%s.%s' from host '%s': This host does not have any calculations", rc->chart?rc->chart:"NOCHART", rc->name, host->hostname);
+
+ expression_free(rc->calculation);
+ expression_free(rc->warning);
+ expression_free(rc->critical);
+
+ freez(rc->name);
+ freez(rc->chart);
+ freez(rc->family);
+ freez(rc->dimensions);
+ freez(rc->exec);
+ freez(rc->source);
+ freez(rc->units);
+ freez(rc->info);
+ freez(rc);
+}
+
+// ----------------------------------------------------------------------------
+// RRDCALCTEMPLATE management
+
+void rrdcalctemplate_link_matching(RRDSET *st) {
+ RRDCALCTEMPLATE *rt;
+
+ for(rt = st->rrdhost->templates; rt ; rt = rt->next) {
+ if(rt->hash_context == st->hash_context && !strcmp(rt->context, st->context)) {
+
+ RRDCALC *rc = rrdcalc_create(st->rrdhost, rt->name, st->id,
+ rt->dimensions, rt->units, rt->info, rt->group, rt->after, rt->before, rt->update_every, rt->options,
+ rt->green, rt->red, rt->exec, rt->source,
+ (rt->calculation)?rt->calculation->source:NULL,
+ (rt->warning)?rt->warning->source:NULL,
+ (rt->critical)?rt->critical->source:NULL);
+
+ if(!rc)
+ error("Health tried to create alarm from template '%s', but it failed", rt->name);
+
+#ifdef NETDATA_INTERNAL_CHECKS
+ else if(rc->rrdset != st)
+ error("Health alarm '%s.%s' should be linked to chart '%s', but it is not", rc->chart?rc->chart:"NOCHART", rc->name, st->id);
+#else
+ (void)rc;
+#endif
+ }
+ }
+}
+
+static inline void rrdcalctemplate_free(RRDHOST *host, RRDCALCTEMPLATE *rt) {
+ debug(D_HEALTH, "Health removing template '%s' of host '%s'", rt->name, host->hostname);
+
+ if(host->templates) {
+ if(host->templates == rt) {
+ host->templates = rt->next;
+ }
+ else {
+ RRDCALCTEMPLATE *t, *last = host->templates;
+ for (t = last->next; t && t != rt; last = t, t = t->next ) ;
+ if(last && last->next == rt) {
+ last->next = rt->next;
+ rt->next = NULL;
+ }
+ else
+ error("Cannot find RRDCALCTEMPLATE '%s' linked in host '%s'", rt->name, host->hostname);
+ }
+ }
+
+ expression_free(rt->calculation);
+ expression_free(rt->warning);
+ expression_free(rt->critical);
+
+ freez(rt->name);
+ freez(rt->exec);
+ freez(rt->context);
+ freez(rt->source);
+ freez(rt->units);
+ freez(rt->info);
+ freez(rt->dimensions);
+ freez(rt);
+}
+
+// ----------------------------------------------------------------------------
+// load health configuration
+
+#define HEALTH_CONF_MAX_LINE 4096
+
+#define HEALTH_ALARM_KEY "alarm"
+#define HEALTH_TEMPLATE_KEY "template"
+#define HEALTH_ON_KEY "on"
+#define HEALTH_LOOKUP_KEY "lookup"
+#define HEALTH_CALC_KEY "calc"
+#define HEALTH_EVERY_KEY "every"
+#define HEALTH_GREEN_KEY "green"
+#define HEALTH_RED_KEY "red"
+#define HEALTH_WARN_KEY "warn"
+#define HEALTH_CRIT_KEY "crit"
+#define HEALTH_EXEC_KEY "exec"
+#define HEALTH_UNITS_KEY "units"
+#define HEALTH_INFO_KEY "info"
+
+static inline int rrdcalc_add_alarm_from_config(RRDHOST *host, RRDCALC *rc) {
+ {
+ char fullname[RRDVAR_MAX_LENGTH + 1];
+ uint32_t hash = rrdcalc_fullname(fullname, RRDVAR_MAX_LENGTH + 1, rc->chart, rc->name);
+
+ if (rrdcalc_exists(host, fullname, hash))
+ return 0;
+ }
+
+ if(!rc->chart) {
+ error("Health configuration for alarm '%s' does not have a chart", rc->name);
+ return 0;
+ }
+
+ if(!rc->update_every) {
+ error("Health configuration for alarm '%s.%s' has no frequency (parameter 'every'). Ignoring it.", rc->chart?rc->chart:"NOCHART", rc->name);
+ return 0;
+ }
+
+ if(!RRDCALC_HAS_DB_LOOKUP(rc) && !rc->warning && !rc->critical) {
+ error("Health configuration for alarm '%s.%s' is useless (no calculation, no warning and no critical evaluation)", rc->chart?rc->chart:"NOCHART", rc->name);
+ return 0;
+ }
+
+ debug(D_HEALTH, "Health configuration adding alarm '%s.%s': exec '%s', green %Lf, red %Lf, lookup: group %d, after %d, before %d, options %u, dimensions '%s', update every %d, calculation '%s', warning '%s', critical '%s', source '%s",
+ rc->chart?rc->chart:"NOCHART",
+ rc->name,
+ (rc->exec)?rc->exec:"DEFAULT",
+ rc->green,
+ rc->red,
+ rc->group,
+ rc->after,
+ rc->before,
+ rc->options,
+ (rc->dimensions)?rc->dimensions:"NONE",
+ rc->update_every,
+ (rc->calculation)?rc->calculation->parsed_as:"NONE",
+ (rc->warning)?rc->warning->parsed_as:"NONE",
+ (rc->critical)?rc->critical->parsed_as:"NONE",
+ rc->source
+ );
+
+ rrdcalc_create_part2(host, rc);
+ return 1;
+}
+
+static inline int rrdcalctemplate_add_template_from_config(RRDHOST *host, RRDCALCTEMPLATE *rt) {
+ if(!rt->context) {
+ error("Health configuration for template '%s' does not have a context", rt->name);
+ return 0;
+ }
+
+ if(!rt->update_every) {
+ error("Health configuration for template '%s' has no frequency (parameter 'every'). Ignoring it.", rt->name);
+ return 0;
+ }
+
+ if(!RRDCALCTEMPLATE_HAS_CALCULATION(rt) && !rt->warning && !rt->critical) {
+ error("Health configuration for template '%s' is useless (no calculation, no warning and no critical evaluation)", rt->name);
+ return 0;
+ }
+
+ RRDCALCTEMPLATE *t;
+ for (t = host->templates; t ; t = t->next) {
+ if(t->hash_name == rt->hash_name && !strcmp(t->name, rt->name)) {
+ error("Health configuration template '%s' already exists for host '%s'.", rt->name, host->hostname);
+ return 0;
+ }
+ }
+
+ debug(D_HEALTH, "Health configuration adding template '%s': context '%s', exec '%s', green %Lf, red %Lf, lookup: group %d, after %d, before %d, options %u, dimensions '%s', update every %d, calculation '%s', warning '%s', critical '%s', source '%s'",
+ rt->name,
+ (rt->context)?rt->context:"NONE",
+ (rt->exec)?rt->exec:"DEFAULT",
+ rt->green,
+ rt->red,
+ rt->group,
+ rt->after,
+ rt->before,
+ rt->options,
+ (rt->dimensions)?rt->dimensions:"NONE",
+ rt->update_every,
+ (rt->calculation)?rt->calculation->parsed_as:"NONE",
+ (rt->warning)?rt->warning->parsed_as:"NONE",
+ (rt->critical)?rt->critical->parsed_as:"NONE",
+ rt->source
+ );
+
+ rt->next = host->templates;
+ host->templates = rt;
+ return 1;
+}
+
+static inline int health_parse_duration(char *string, int *result) {
+ // make sure it is a number
+ if(!*string || !(isdigit(*string) || *string == '+' || *string == '-')) {
+ *result = 0;
+ return 0;
+ }
+
+ char *e = NULL;
+ calculated_number n = strtold(string, &e);
+ if(e && *e) {
+ switch (*e) {
+ case 'Y':
+ *result = (int) (n * 86400 * 365);
+ break;
+ case 'M':
+ *result = (int) (n * 86400 * 30);
+ break;
+ case 'w':
+ *result = (int) (n * 86400 * 7);
+ break;
+ case 'd':
+ *result = (int) (n * 86400);
+ break;
+ case 'h':
+ *result = (int) (n * 3600);
+ break;
+ case 'm':
+ *result = (int) (n * 60);
+ break;
+
+ default:
+ case 's':
+ *result = (int) (n);
+ break;
+ }
+ }
+ else
+ *result = (int)(n);
+
+ return 1;
+}
+
+static inline int health_parse_db_lookup(
+ size_t line, const char *path, const char *file, char *string,
+ int *group_method, int *after, int *before, int *every,
+ uint32_t *options, char **dimensions
+) {
+ debug(D_HEALTH, "Health configuration parsing database lookup %zu@%s/%s: %s", line, path, file, string);
+
+ if(*dimensions) freez(*dimensions);
+ *dimensions = NULL;
+ *after = 0;
+ *before = 0;
+ *every = 0;
+ *options = 0;
+
+ char *s = string, *key;
+
+ // first is the group method
+ key = s;
+ while(*s && !isspace(*s)) s++;
+ while(*s && isspace(*s)) *s++ = '\0';
+ if(!*s) {
+ error("Health configuration invalid chart calculation at line %zu of file '%s/%s': expected group method followed by the 'after' time, but got '%s'",
+ line, path, file, key);
+ return 0;
+ }
+
+ if((*group_method = web_client_api_request_v1_data_group(key, -1)) == -1) {
+ error("Health configuration at line %zu of file '%s/%s': invalid group method '%s'",
+ line, path, file, key);
+ return 0;
+ }
+
+ // then is the 'after' time
+ key = s;
+ while(*s && !isspace(*s)) s++;
+ while(*s && isspace(*s)) *s++ = '\0';
+
+ if(!health_parse_duration(key, after)) {
+ error("Health configuration at line %zu of file '%s/%s': invalid duration '%s' after group method",
+ line, path, file, key);
+ return 0;
+ }
+
+ // sane defaults
+ *every = abs(*after);
+
+ // now we may have optional parameters
+ while(*s) {
+ key = s;
+ while(*s && !isspace(*s)) s++;
+ while(*s && isspace(*s)) *s++ = '\0';
+ if(!*key) break;
+
+ if(!strcasecmp(key, "at")) {
+ char *value = s;
+ while(*s && !isspace(*s)) s++;
+ while(*s && isspace(*s)) *s++ = '\0';
+
+ if (!health_parse_duration(value, before)) {
+ error("Health configuration at line %zu of file '%s/%s': invalid duration '%s' for '%s' keyword",
+ line, path, file, value, key);
+ }
+ }
+ else if(!strcasecmp(key, HEALTH_EVERY_KEY)) {
+ char *value = s;
+ while(*s && !isspace(*s)) s++;
+ while(*s && isspace(*s)) *s++ = '\0';
+
+ if (!health_parse_duration(value, every)) {
+ error("Health configuration at line %zu of file '%s/%s': invalid duration '%s' for '%s' keyword",
+ line, path, file, value, key);
+ }
+ }
+ else if(!strcasecmp(key, "absolute") || !strcasecmp(key, "abs") || !strcasecmp(key, "absolute_sum")) {
+ *options |= RRDR_OPTION_ABSOLUTE;
+ }
+ else if(!strcasecmp(key, "min2max")) {
+ *options |= RRDR_OPTION_MIN2MAX;
+ }
+ else if(!strcasecmp(key, "null2zero")) {
+ *options |= RRDR_OPTION_NULL2ZERO;
+ }
+ else if(!strcasecmp(key, "percentage")) {
+ *options |= RRDR_OPTION_PERCENTAGE;
+ }
+ else if(!strcasecmp(key, "unaligned")) {
+ *options |= RRDR_OPTION_NOT_ALIGNED;
+ }
+ else if(!strcasecmp(key, "of")) {
+ if(*s && strcasecmp(s, "all"))
+ *dimensions = strdupz(s);
+ break;
+ }
+ else {
+ error("Health configuration at line %zu of file '%s/%s': unknown keyword '%s'",
+ line, path, file, key);
+ }
+ }
+
+ return 1;
+}
+
+static inline char *health_source_file(size_t line, const char *path, const char *filename) {
+ char buffer[FILENAME_MAX + 1];
+ snprintfz(buffer, FILENAME_MAX, "%zu@%s/%s", line, path, filename);
+ return strdupz(buffer);
+}
+
+static inline void strip_quotes(char *s) {
+ while(*s) {
+ if(*s == '\'' || *s == '"') *s = ' ';
+ s++;
+ }
+}
+
+int health_readfile(const char *path, const char *filename) {
+ debug(D_HEALTH, "Health configuration reading file '%s/%s'", path, filename);
+
+ static uint32_t hash_alarm = 0, hash_template = 0, hash_on = 0, hash_calc = 0, hash_green = 0, hash_red = 0, hash_warn = 0, hash_crit = 0, hash_exec = 0, hash_every = 0, hash_lookup = 0, hash_units = 0, hash_info = 0;
+ char buffer[HEALTH_CONF_MAX_LINE + 1];
+
+ if(unlikely(!hash_alarm)) {
+ hash_alarm = simple_uhash(HEALTH_ALARM_KEY);
+ hash_template = simple_uhash(HEALTH_TEMPLATE_KEY);
+ hash_on = simple_uhash(HEALTH_ON_KEY);
+ hash_calc = simple_uhash(HEALTH_CALC_KEY);
+ hash_lookup = simple_uhash(HEALTH_LOOKUP_KEY);
+ hash_green = simple_uhash(HEALTH_GREEN_KEY);
+ hash_red = simple_uhash(HEALTH_RED_KEY);
+ hash_warn = simple_uhash(HEALTH_WARN_KEY);
+ hash_crit = simple_uhash(HEALTH_CRIT_KEY);
+ hash_exec = simple_uhash(HEALTH_EXEC_KEY);
+ hash_every = simple_uhash(HEALTH_EVERY_KEY);
+ hash_units = simple_hash(HEALTH_UNITS_KEY);
+ hash_info = simple_hash(HEALTH_INFO_KEY);
+ }
+
+ snprintfz(buffer, HEALTH_CONF_MAX_LINE, "%s/%s", path, filename);
+ FILE *fp = fopen(buffer, "r");
+ if(!fp) {
+ error("Health configuration cannot read file '%s'.", buffer);
+ return 0;
+ }
+
+ RRDCALC *rc = NULL;
+ RRDCALCTEMPLATE *rt = NULL;
+
+ size_t line = 0, append = 0;
+ char *s;
+ while((s = fgets(&buffer[append], (int)(HEALTH_CONF_MAX_LINE - append), fp)) || append) {
+ int stop_appending = !s;
+ line++;
+ // info("Line %zu of file '%s/%s': '%s'", line, path, filename, s);
+ s = trim(buffer);
+ if(!s) continue;
+ // info("Trimmed line %zu of file '%s/%s': '%s'", line, path, filename, s);
+
+ append = strlen(s);
+ if(!stop_appending && s[append - 1] == '\\') {
+ s[append - 1] = ' ';
+ append = &s[append] - buffer;
+ if(append < HEALTH_CONF_MAX_LINE)
+ continue;
+ else {
+ error("Health configuration has too long muli-line at line %zu of file '%s/%s'.", line, path, filename);
+ }
+ }
+ append = 0;
+
+ char *key = s;
+ while(*s && *s != ':') s++;
+ if(!*s) {
+ error("Health configuration has invalid line %zu of file '%s/%s'. It does not contain a ':'. Ignoring it.", line, path, filename);
+ continue;
+ }
+ *s = '\0';
+ s++;
+
+ char *value = s;
+ key = trim(key);
+ value = trim(value);
+
+ if(!key) {
+ error("Health configuration has invalid line %zu of file '%s/%s'. Keyword is empty. Ignoring it.", line, path, filename);
+ continue;
+ }
+
+ if(!value) {
+ error("Health configuration has invalid line %zu of file '%s/%s'. value is empty. Ignoring it.", line, path, filename);
+ continue;
+ }
+
+ // info("Health file '%s/%s', key '%s', value '%s'", path, filename, key, value);
+ uint32_t hash = simple_uhash(key);
+
+ if(hash == hash_alarm && !strcasecmp(key, HEALTH_ALARM_KEY)) {
+ if(rc && !rrdcalc_add_alarm_from_config(&localhost, rc))
+ rrdcalc_free(&localhost, rc);
+
+ if(rt) {
+ if (!rrdcalctemplate_add_template_from_config(&localhost, rt))
+ rrdcalctemplate_free(&localhost, rt);
+ rt = NULL;
+ }
+
+ rc = callocz(1, sizeof(RRDCALC));
+ rc->name = strdupz(value);
+ rc->hash = simple_hash(rc->name);
+ rc->source = health_source_file(line, path, filename);
+ rc->green = NAN;
+ rc->red = NAN;
+ rc->value = NAN;
+ rc->old_value = NAN;
+
+ if(rrdvar_fix_name(rc->name))
+ error("Health configuration renamed alarm '%s' to '%s'", value, rc->name);
+ }
+ else if(hash == hash_template && !strcasecmp(key, HEALTH_TEMPLATE_KEY)) {
+ if(rc) {
+ if(!rrdcalc_add_alarm_from_config(&localhost, rc))
+ rrdcalc_free(&localhost, rc);
+ rc = NULL;
+ }
+
+ if(rt && !rrdcalctemplate_add_template_from_config(&localhost, rt))
+ rrdcalctemplate_free(&localhost, rt);
+
+ rt = callocz(1, sizeof(RRDCALCTEMPLATE));
+ rt->name = strdupz(value);
+ rt->hash_name = simple_hash(rt->name);
+ rt->source = health_source_file(line, path, filename);
+ rt->green = NAN;
+ rt->red = NAN;
+
+ if(rrdvar_fix_name(rt->name))
+ error("Health configuration renamed template '%s' to '%s'", value, rt->name);
+ }
+ else if(rc) {
+ if(hash == hash_on && !strcasecmp(key, HEALTH_ON_KEY)) {
+ if(rc->chart) {
+ if(strcmp(rc->chart, value))
+ info("Health configuration at line %zu of file '%s/%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
+ line, path, filename, rc->name, key, rc->chart, value, value);
+
+ freez(rc->chart);
+ }
+ rc->chart = strdupz(value);
+ rc->hash_chart = simple_hash(rc->chart);
+ }
+ else if(hash == hash_lookup && !strcasecmp(key, HEALTH_LOOKUP_KEY)) {
+ health_parse_db_lookup(line, path, filename, value, &rc->group, &rc->after, &rc->before,
+ &rc->update_every,
+ &rc->options, &rc->dimensions);
+ }
+ else if(hash == hash_every && !strcasecmp(key, HEALTH_EVERY_KEY)) {
+ if(!health_parse_duration(value, &rc->update_every))
+ info("Health configuration at line %zu of file '%s/%s' for alarm '%s' at key '%s' cannot parse duration: '%s'.",
+ line, path, filename, rc->name, key, value);
+ }
+ else if(hash == hash_green && !strcasecmp(key, HEALTH_GREEN_KEY)) {
+ char *e;
+ rc->green = strtold(value, &e);
+ if(e && *e) {
+ info("Health configuration at line %zu of file '%s/%s' for alarm '%s' at key '%s' leaves this string unmatched: '%s'.",
+ line, path, filename, rc->name, key, e);
+ }
+ }
+ else if(hash == hash_red && !strcasecmp(key, HEALTH_RED_KEY)) {
+ char *e;
+ rc->red = strtold(value, &e);
+ if(e && *e) {
+ info("Health configuration at line %zu of file '%s/%s' for alarm '%s' at key '%s' leaves this string unmatched: '%s'.",
+ line, path, filename, rc->name, key, e);
+ }
+ }
+ else if(hash == hash_calc && !strcasecmp(key, HEALTH_CALC_KEY)) {
+ const char *failed_at = NULL;
+ int error = 0;
+ rc->calculation = expression_parse(value, &failed_at, &error);
+ if(!rc->calculation) {
+ error("Health configuration at line %zu of file '%s/%s' for alarm '%s' at key '%s' has unparse-able expression '%s': %s at '%s'",
+ line, path, filename, rc->name, key, value, expression_strerror(error), failed_at);
+ }
+ }
+ else if(hash == hash_warn && !strcasecmp(key, HEALTH_WARN_KEY)) {
+ const char *failed_at = NULL;
+ int error = 0;
+ rc->warning = expression_parse(value, &failed_at, &error);
+ if(!rc->warning) {
+ error("Health configuration at line %zu of file '%s/%s' for alarm '%s' at key '%s' has unparse-able expression '%s': %s at '%s'",
+ line, path, filename, rc->name, key, value, expression_strerror(error), failed_at);
+ }
+ }
+ else if(hash == hash_crit && !strcasecmp(key, HEALTH_CRIT_KEY)) {
+ const char *failed_at = NULL;
+ int error = 0;
+ rc->critical = expression_parse(value, &failed_at, &error);
+ if(!rc->critical) {
+ error("Health configuration at line %zu of file '%s/%s' for alarm '%s' at key '%s' has unparse-able expression '%s': %s at '%s'",
+ line, path, filename, rc->name, key, value, expression_strerror(error), failed_at);
+ }
+ }
+ else if(hash == hash_exec && !strcasecmp(key, HEALTH_EXEC_KEY)) {
+ if(rc->exec) {
+ if(strcmp(rc->exec, value))
+ info("Health configuration at line %zu of file '%s/%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
+ line, path, filename, rc->name, key, rc->exec, value, value);
+
+ freez(rc->exec);
+ }
+ rc->exec = strdupz(value);
+ }
+ else if(hash == hash_units && !strcasecmp(key, HEALTH_UNITS_KEY)) {
+ if(rc->units) {
+ if(strcmp(rc->units, value))
+ info("Health configuration at line %zu of file '%s/%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
+ line, path, filename, rc->name, key, rc->units, value, value);
+
+ freez(rc->units);
+ }
+ rc->units = strdupz(value);
+ strip_quotes(rc->units);
+ }
+ else if(hash == hash_info && !strcasecmp(key, HEALTH_INFO_KEY)) {
+ if(rc->info) {
+ if(strcmp(rc->info, value))
+ info("Health configuration at line %zu of file '%s/%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
+ line, path, filename, rc->name, key, rc->info, value, value);
+
+ freez(rc->info);
+ }
+ rc->info = strdupz(value);
+ strip_quotes(rc->info);
+ }
+ else {
+ error("Health configuration at line %zu of file '%s/%s' for alarm '%s' has unknown key '%s'.",
+ line, path, filename, rc->name, key);
+ }
+ }
+ else if(rt) {
+ if(hash == hash_on && !strcasecmp(key, HEALTH_ON_KEY)) {
+ if(rt->context) {
+ if(strcmp(rt->context, value))
+ info("Health configuration at line %zu of file '%s/%s' for template '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
+ line, path, filename, rt->name, key, rt->context, value, value);
+
+ freez(rt->context);
+ }
+ rt->context = strdupz(value);
+ rt->hash_context = simple_hash(rt->context);
+ }
+ else if(hash == hash_lookup && !strcasecmp(key, HEALTH_LOOKUP_KEY)) {
+ health_parse_db_lookup(line, path, filename, value, &rt->group, &rt->after, &rt->before,
+ &rt->update_every,
+ &rt->options, &rt->dimensions);
+ }
+ else if(hash == hash_every && !strcasecmp(key, HEALTH_EVERY_KEY)) {
+ if(!health_parse_duration(value, &rt->update_every))
+ info("Health configuration at line %zu of file '%s/%s' for template '%s' at key '%s' cannot parse duration: '%s'.",
+ line, path, filename, rt->name, key, value);
+ }
+ else if(hash == hash_green && !strcasecmp(key, HEALTH_GREEN_KEY)) {
+ char *e;
+ rt->green = strtold(value, &e);
+ if(e && *e) {
+ info("Health configuration at line %zu of file '%s/%s' for template '%s' at key '%s' leaves this string unmatched: '%s'.",
+ line, path, filename, rt->name, key, e);
+ }
+ }
+ else if(hash == hash_red && !strcasecmp(key, HEALTH_RED_KEY)) {
+ char *e;
+ rt->red = strtold(value, &e);
+ if(e && *e) {
+ info("Health configuration at line %zu of file '%s/%s' for template '%s' at key '%s' leaves this string unmatched: '%s'.",
+ line, path, filename, rt->name, key, e);
+ }
+ }
+ else if(hash == hash_calc && !strcasecmp(key, HEALTH_CALC_KEY)) {
+ const char *failed_at = NULL;
+ int error = 0;
+ rt->calculation = expression_parse(value, &failed_at, &error);
+ if(!rt->calculation) {
+ error("Health configuration at line %zu of file '%s/%s' for template '%s' at key '%s' has unparse-able expression '%s': %s at '%s'",
+ line, path, filename, rt->name, key, value, expression_strerror(error), failed_at);
+ }
+ }
+ else if(hash == hash_warn && !strcasecmp(key, HEALTH_WARN_KEY)) {
+ const char *failed_at = NULL;
+ int error = 0;
+ rt->warning = expression_parse(value, &failed_at, &error);
+ if(!rt->warning) {
+ error("Health configuration at line %zu of file '%s/%s' for template '%s' at key '%s' has unparse-able expression '%s': %s at '%s'",
+ line, path, filename, rt->name, key, value, expression_strerror(error), failed_at);
+ }
+ }
+ else if(hash == hash_crit && !strcasecmp(key, HEALTH_CRIT_KEY)) {
+ const char *failed_at = NULL;
+ int error = 0;
+ rt->critical = expression_parse(value, &failed_at, &error);
+ if(!rt->critical) {
+ error("Health configuration at line %zu of file '%s/%s' for template '%s' at key '%s' has unparse-able expression '%s': %s at '%s'",
+ line, path, filename, rt->name, key, value, expression_strerror(error), failed_at);
+ }
+ }
+ else if(hash == hash_exec && !strcasecmp(key, HEALTH_EXEC_KEY)) {
+ if(rt->exec) {
+ if(strcmp(rt->exec, value))
+ info("Health configuration at line %zu of file '%s/%s' for template '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
+ line, path, filename, rt->name, key, rt->exec, value, value);
+
+ freez(rt->exec);
+ }
+ rt->exec = strdupz(value);
+ }
+ else if(hash == hash_units && !strcasecmp(key, HEALTH_UNITS_KEY)) {
+ if(rt->units) {
+ if(strcmp(rt->units, value))
+ info("Health configuration at line %zu of file '%s/%s' for template '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
+ line, path, filename, rt->name, key, rt->units, value, value);
+
+ freez(rt->units);
+ }
+ rt->units = strdupz(value);
+ strip_quotes(rt->units);
+ }
+ else if(hash == hash_info && !strcasecmp(key, HEALTH_INFO_KEY)) {
+ if(rt->info) {
+ if(strcmp(rt->info, value))
+ info("Health configuration at line %zu of file '%s/%s' for template '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
+ line, path, filename, rt->name, key, rt->info, value, value);
+
+ freez(rt->info);
+ }
+ rt->info = strdupz(value);
+ strip_quotes(rt->info);
+ }
+ else {
+ error("Health configuration at line %zu of file '%s/%s' for template '%s' has unknown key '%s'.",
+ line, path, filename, rt->name, key);
+ }
+ }
+ else {
+ error("Health configuration at line %zu of file '%s/%s' has unknown key '%s'. Expected either '" HEALTH_ALARM_KEY "' or '" HEALTH_TEMPLATE_KEY "'.",
+ line, path, filename, key);
+ }
+ }
+
+ if(rc && !rrdcalc_add_alarm_from_config(&localhost, rc))
+ rrdcalc_free(&localhost, rc);
+
+ if(rt && !rrdcalctemplate_add_template_from_config(&localhost, rt))
+ rrdcalctemplate_free(&localhost, rt);
+
+ fclose(fp);
+ return 1;
+}
+
+void health_readdir(const char *path) {
+ size_t pathlen = strlen(path);
+
+ debug(D_HEALTH, "Health configuration reading directory '%s'", path);
+
+ DIR *dir = opendir(path);
+ if (!dir) {
+ error("Health configuration cannot open directory '%s'.", path);
+ return;
+ }
+
+ struct dirent *de = NULL;
+ while ((de = readdir(dir))) {
+ size_t len = strlen(de->d_name);
+
+ if(de->d_type == DT_DIR
+ && (
+ (de->d_name[0] == '.' && de->d_name[1] == '\0')
+ || (de->d_name[0] == '.' && de->d_name[1] == '.' && de->d_name[2] == '\0')
+ ))
+ continue;
+
+ else if(de->d_type == DT_DIR) {
+ char *s = mallocz(pathlen + strlen(de->d_name) + 2);
+ strcpy(s, path);
+ strcat(s, "/");
+ strcat(s, de->d_name);
+ health_readdir(s);
+ freez(s);
+ continue;
+ }
+
+ else if((de->d_type == DT_LNK || de->d_type == DT_REG) &&
+ len > 5 && !strcmp(&de->d_name[len - 5], ".conf")) {
+ health_readfile(path, de->d_name);
+ }
+ }
+
+ closedir(dir);
+}
+
+static inline char *health_config_dir(void) {
+ char buffer[FILENAME_MAX + 1];
+ snprintfz(buffer, FILENAME_MAX, "%s/health.d", config_get("global", "config directory", CONFIG_DIR));
+ return config_get("health", "health configuration directory", buffer);
+}
+
+void health_init(void) {
+ debug(D_HEALTH, "Health configuration initializing");
+
+ if(!(health_enabled = config_get_boolean("health", "enabled", 1))) {
+ debug(D_HEALTH, "Health is disabled.");
+ return;
+ }
+
+ char *path = health_config_dir();
+
+ {
+ char buffer[FILENAME_MAX + 1];
+ snprintfz(buffer, FILENAME_MAX, "%s/alarm-email.sh", config_get("global", "plugins directory", PLUGINS_DIR));
+ health_default_exec = config_get("health", "script to execute on alarm", buffer);
+ }
+
+ long n = config_get_number("health", "in memory max health log entries", (long)localhost.health_log.max);
+ if(n < 2) {
+ error("Health configuration has invalid max log entries %ld. Using default %u", n, localhost.health_log.max);
+ config_set_number("health", "in memory max health log entries", (long)localhost.health_log.max);
+ }
+ else localhost.health_log.max = (unsigned int)n;
+
+ rrdhost_rwlock(&localhost);
+ health_readdir(path);
+ rrdhost_unlock(&localhost);
+}
+
+// ----------------------------------------------------------------------------
+// JSON generation
+
+static inline void health_string2json(BUFFER *wb, const char *prefix, const char *label, const char *value, const char *suffix) {
+ if(value && *value)
+ buffer_sprintf(wb, "%s\"%s\":\"%s\"%s", prefix, label, value, suffix);
+ else
+ buffer_sprintf(wb, "%s\"%s\":null%s", prefix, label, suffix);
+}
+
+static inline void health_alarm_entry2json_nolock(BUFFER *wb, ALARM_ENTRY *ae) {
+ buffer_sprintf(wb, "\n\t{\n"
+ "\t\t\"id\":%u,\n"
+ "\t\t\"name\":\"%s\",\n"
+ "\t\t\"chart\":\"%s\",\n"
+ "\t\t\"family\":\"%s\",\n"
+ "\t\t\"processed\":%s,\n"
+ "\t\t\"updated\":%s,\n"
+ "\t\t\"exec_run\":%s,\n"
+ "\t\t\"exec_failed\":%s,\n"
+ "\t\t\"exec\":\"%s\",\n"
+ "\t\t\"exec_code\":%d,\n"
+ "\t\t\"source\":\"%s\",\n"
+ "\t\t\"units\":\"%s\",\n"
+ "\t\t\"info\":\"%s\",\n"
+ "\t\t\"when\":%lu,\n"
+ "\t\t\"duration\":%lu,\n"
+ "\t\t\"non_clear_duration\":%lu,\n"
+ "\t\t\"status\":\"%s\",\n"
+ "\t\t\"old_status\":\"%s\",\n",
+ ae->id,
+ ae->name,
+ ae->chart,
+ ae->family,
+ (ae->notifications & HEALTH_ENTRY_NOTIFICATIONS_PROCESSED)?"true":"false",
+ (ae->notifications & HEALTH_ENTRY_NOTIFICATIONS_UPDATED)?"true":"false",
+ (ae->notifications & HEALTH_ENTRY_NOTIFICATIONS_EXEC_RUN)?"true":"false",
+ (ae->notifications & HEALTH_ENTRY_NOTIFICATIONS_EXEC_FAILED)?"true":"false",
+ ae->exec?ae->exec:health_default_exec,
+ ae->exec_code,
+ ae->source,
+ ae->units?ae->units:"",
+ ae->info?ae->info:"",
+ (unsigned long)ae->when,
+ (unsigned long)ae->duration,
+ (unsigned long)ae->non_clear_duration,
+ rrdcalc_status2string(ae->new_status),
+ rrdcalc_status2string(ae->old_status)
+ );
+
+ buffer_strcat(wb, "\t\t\"value\":");
+ buffer_rrd_value(wb, ae->new_value);
+ buffer_strcat(wb, ",\n");
+
+ buffer_strcat(wb, "\t\t\"old_value\":");
+ buffer_rrd_value(wb, ae->old_value);
+ buffer_strcat(wb, "\n");
+
+ buffer_strcat(wb, "\t}");
+}
+
+void health_alarm_log2json(RRDHOST *host, BUFFER *wb) {
+ pthread_rwlock_rdlock(&host->health_log.alarm_log_rwlock);
+
+ buffer_strcat(wb, "[");
+
+ unsigned int max = host->health_log.max;
+ unsigned int count = 0;
+ ALARM_ENTRY *ae;
+ for(ae = host->health_log.alarms; ae && count < max ; count++, ae = ae->next) {
+ if(likely(count)) buffer_strcat(wb, ",");
+ health_alarm_entry2json_nolock(wb, ae);
+ }
+
+ buffer_strcat(wb, "\n]\n");
+
+ pthread_rwlock_unlock(&host->health_log.alarm_log_rwlock);
+}
+
+static inline void health_rrdcalc2json_nolock(BUFFER *wb, RRDCALC *rc) {
+ buffer_sprintf(wb,
+ "\t\t\"%s.%s\": {\n"
+ "\t\t\t\"name\": \"%s\",\n"
+ "\t\t\t\"chart\": \"%s\",\n"
+ "\t\t\t\"family\": \"%s\",\n"
+ "\t\t\t\"active\": %s,\n"
+ "\t\t\t\"exec\": \"%s\",\n"
+ "\t\t\t\"source\": \"%s\",\n"
+ "\t\t\t\"units\": \"%s\",\n"
+ "\t\t\t\"info\": \"%s\",\n"
+ "\t\t\t\"status\": \"%s\",\n"
+ "\t\t\t\"last_status_change\": %lu,\n"
+ "\t\t\t\"last_updated\": %lu,\n"
+ "\t\t\t\"next_update\": %lu,\n"
+ "\t\t\t\"update_every\": %d,\n"
+ , rc->chart, rc->name
+ , rc->name
+ , rc->chart
+ , (rc->rrdset && rc->rrdset->family)?rc->rrdset->family:""
+ , (rc->rrdset)?"true":"false"
+ , rc->exec?rc->exec:health_default_exec
+ , rc->source
+ , rc->units?rc->units:""
+ , rc->info?rc->info:""
+ , rrdcalc_status2string(rc->status)
+ , (unsigned long)rc->last_status_change
+ , (unsigned long)rc->last_updated
+ , (unsigned long)rc->next_update
+ , rc->update_every
+ );
+
+ if(RRDCALC_HAS_DB_LOOKUP(rc)) {
+ if(rc->dimensions && *rc->dimensions)
+ health_string2json(wb, "\t\t\t", "lookup_dimensions", rc->dimensions, ",\n");
+
+ buffer_sprintf(wb,
+ "\t\t\t\"db_after\": %lu,\n"
+ "\t\t\t\"db_before\": %lu,\n"
+ "\t\t\t\"lookup_method\": \"%s\",\n"
+ "\t\t\t\"lookup_after\": %d,\n"
+ "\t\t\t\"lookup_before\": %d,\n"
+ "\t\t\t\"lookup_options\": \"",
+ (unsigned long) rc->db_after,
+ (unsigned long) rc->db_before,
+ group_method2string(rc->group),
+ rc->after,
+ rc->before
+ );
+ buffer_data_options2string(wb, rc->options);
+ buffer_strcat(wb, "\",\n");
+ }
+
+ if(rc->calculation) {
+ health_string2json(wb, "\t\t\t", "calc", rc->calculation->source, ",\n");
+ health_string2json(wb, "\t\t\t", "calc_parsed", rc->calculation->parsed_as, ",\n");
+ }
+
+ if(rc->warning) {
+ health_string2json(wb, "\t\t\t", "warn", rc->warning->source, ",\n");
+ health_string2json(wb, "\t\t\t", "warn_parsed", rc->warning->parsed_as, ",\n");
+ }
+
+ if(rc->critical) {
+ health_string2json(wb, "\t\t\t", "crit", rc->critical->source, ",\n");
+ health_string2json(wb, "\t\t\t", "crit_parsed", rc->critical->parsed_as, ",\n");
+ }
+
+ buffer_strcat(wb, "\t\t\t\"green\":");
+ buffer_rrd_value(wb, rc->green);
+ buffer_strcat(wb, ",\n");
+
+ buffer_strcat(wb, "\t\t\t\"red\":");
+ buffer_rrd_value(wb, rc->red);
+ buffer_strcat(wb, ",\n");
+
+ buffer_strcat(wb, "\t\t\t\"value\":");
+ buffer_rrd_value(wb, rc->value);
+ buffer_strcat(wb, "\n");
+
+ buffer_strcat(wb, "\t\t}");
+}
+
+//void health_rrdcalctemplate2json_nolock(BUFFER *wb, RRDCALCTEMPLATE *rt) {
+//
+//}
+
+void health_alarms2json(RRDHOST *host, BUFFER *wb, int all) {
+ int i;
+ rrdhost_rdlock(&localhost);
+
+ buffer_strcat(wb, "{\n\t\"alarms\": {\n");
+ RRDCALC *rc;
+ for(i = 0, rc = host->alarms; rc ; rc = rc->next) {
+ if(!rc->rrdset)
+ continue;
+
+ if(!all && !(rc->status == RRDCALC_STATUS_WARNING || rc->status == RRDCALC_STATUS_CRITICAL))
+ continue;
+
+ if(likely(i)) buffer_strcat(wb, ",\n");
+ health_rrdcalc2json_nolock(wb, rc);
+ i++;
+ }
+
+// buffer_strcat(wb, "\n\t},\n\t\"templates\": {");
+
+// RRDCALCTEMPLATE *rt;
+// for(rt = host->templates; rt ; rt = rt->next)
+// health_rrdcalctemplate2json_nolock(wb, rt);
+
+ buffer_sprintf(wb, "\n\t},\n\t\"now\": %lu\n}\n", (unsigned long)time(NULL));
+ rrdhost_unlock(&localhost);
+}
+
+
+// ----------------------------------------------------------------------------
+// re-load health configuration
+
+static inline void health_free_all_nolock(RRDHOST *host) {
+ while(host->templates)
+ rrdcalctemplate_free(host, host->templates);
+
+ while(host->alarms)
+ rrdcalc_free(host, host->alarms);
+}
+
+void health_reload(void) {
+ if(!health_enabled) {
+ error("Health reload is requested, but health is not enabled.");
+ return;
+ }
+
+ char *path = health_config_dir();
+
+ rrdhost_rwlock(&localhost);
+ health_free_all_nolock(&localhost);
+ rrdhost_unlock(&localhost);
+
+ RRDSET *st;
+ for(st = localhost.rrdset_root; st ; st = st->next) {
+ st->green = NAN;
+ st->red = NAN;
+ }
+
+ rrdhost_rwlock(&localhost);
+ health_readdir(path);
+ rrdhost_unlock(&localhost);
+
+ for(st = localhost.rrdset_root; st ; st = st->next) {
+ rrdhost_rwlock(&localhost);
+
+ rrdsetcalc_link_matching(st);
+ rrdcalctemplate_link_matching(st);
+
+ rrdhost_unlock(&localhost);
+ }
+}
+
+
+// ----------------------------------------------------------------------------
+// health main thread and friends
+
+static inline int rrdcalc_isrunnable(RRDCALC *rc, time_t now, time_t *next_run) {
+ if (unlikely(!rc->rrdset)) {
+ debug(D_HEALTH, "Health not running alarm '%s.%s'. It is not linked to a chart.", rc->chart?rc->chart:"NOCHART", rc->name);
+ return 0;
+ }
+
+ if (unlikely(!rc->update_every)) {
+ debug(D_HEALTH, "Health not running alarm '%s.%s'. It does not have an update frequency", rc->chart?rc->chart:"NOCHART", rc->name);
+ return 0;
+ }
+
+ if (unlikely(rc->next_update > now)) {
+ if (*next_run > rc->next_update)
+ *next_run = rc->next_update;
+
+ debug(D_HEALTH, "Health not examining alarm '%s.%s' yet (will do in %d secs).", rc->chart?rc->chart:"NOCHART", rc->name, (int) (rc->next_update - now));
+ return 0;
+ }
+
+ return 1;
+}
+
+static inline int rrdcalc_value2status(calculated_number n) {
+ if(isnan(n)) return RRDCALC_STATUS_UNDEFINED;
+ if(n) return RRDCALC_STATUS_RAISED;
+ return RRDCALC_STATUS_CLEAR;
+}
+
+static inline void health_alarm_execute(ALARM_ENTRY *ae) {
+ if(ae->old_status == RRDCALC_STATUS_UNINITIALIZED && ae->new_status == RRDCALC_STATUS_CLEAR)
+ return;
+
+ char buffer[FILENAME_MAX + 1];
+ pid_t command_pid;
+
+ const char *exec = ae->exec;
+ if(!exec) exec = health_default_exec;
+
+ snprintfz(buffer, FILENAME_MAX, "exec %s '%s' '%s' '%s' '%s' '%s' '%0.0Lf' '%0.0Lf' '%s' '%u' '%u' '%s' '%s'",
+ exec,
+ ae->name,
+ ae->chart?ae->chart:"NOCAHRT",
+ ae->family?ae->family:"NOFAMILY",
+ rrdcalc_status2string(ae->new_status),
+ rrdcalc_status2string(ae->old_status),
+ ae->new_value,
+ ae->old_value,
+ ae->source?ae->source:"UNKNOWN",
+ (uint32_t)ae->duration,
+ (uint32_t)ae->non_clear_duration,
+ ae->units?ae->units:"",
+ ae->info?ae->info:""
+ );
+
+ ae->notifications |= HEALTH_ENTRY_NOTIFICATIONS_EXEC_RUN;
+
+ debug(D_HEALTH, "executing command '%s'", buffer);
+ FILE *fp = mypopen(buffer, &command_pid);
+ if(!fp) {
+ error("HEALTH: Cannot popen(\"%s\", \"r\").", buffer);
+ return;
+ }
+ debug(D_HEALTH, "HEALTH reading from command");
+ char *s = fgets(buffer, FILENAME_MAX, fp);
+ (void)s;
+ debug(D_HEALTH, "HEALTH closing command");
+ ae->exec_code = mypclose(fp, command_pid);
+ debug(D_HEALTH, "done executing command - returned with code %d", ae->exec_code);
+
+ if(ae->exec_code != 0)
+ ae->notifications |= HEALTH_ENTRY_NOTIFICATIONS_EXEC_FAILED;
+}
+
+static inline void health_process_notifications(ALARM_ENTRY *ae) {
+ info("Health alarm '%s.%s' = %0.2Lf - changed status from %s to %s",
+ ae->chart?ae->chart:"NOCHART", ae->name,
+ ae->new_value,
+ rrdcalc_status2string(ae->old_status),
+ rrdcalc_status2string(ae->new_status)
+ );
+
+ health_alarm_execute(ae);
+}
+
+static inline void health_alarm_log(RRDHOST *host, time_t when,
+ const char *name, const char *chart, const char *family,
+ const char *exec, time_t duration,
+ calculated_number old_value, calculated_number new_value,
+ int old_status, int new_status,
+ const char *source,
+ const char *units,
+ const char *info
+) {
+ ALARM_ENTRY *ae = callocz(1, sizeof(ALARM_ENTRY));
+ ae->name = strdupz(name);
+ ae->hash_name = simple_hash(ae->name);
+
+ if(chart) {
+ ae->chart = strdupz(chart);
+ ae->hash_chart = simple_hash(ae->chart);
+ }
+
+ if(family)
+ ae->family = strdupz(family);
+
+ if(exec) ae->exec = strdupz(exec);
+ if(source) ae->source = strdupz(source);
+ if(units) ae->units = strdupz(units);
+ if(info) ae->info = strdupz(info);
+
+ ae->id = host->health_log.nextid++;
+ ae->when = when;
+ ae->old_value = old_value;
+ ae->new_value = new_value;
+ ae->old_status = old_status;
+ ae->new_status = new_status;
+ ae->duration = duration;
+
+ if(ae->old_status == RRDCALC_STATUS_WARNING || ae->old_status == RRDCALC_STATUS_CRITICAL)
+ ae->non_clear_duration += ae->duration;
+
+ // link it
+ pthread_rwlock_wrlock(&host->health_log.alarm_log_rwlock);
+ ae->next = host->health_log.alarms;
+ host->health_log.alarms = ae;
+ host->health_log.count++;
+ pthread_rwlock_unlock(&host->health_log.alarm_log_rwlock);
+
+ // match previous alarms
+ pthread_rwlock_rdlock(&host->health_log.alarm_log_rwlock);
+ ALARM_ENTRY *t;
+ for(t = host->health_log.alarms ; t ; t = t->next) {
+ if(t != ae &&
+ t->hash_name == ae->hash_name &&
+ t->hash_chart == ae->hash_chart &&
+ !strcmp(t->name, ae->name) &&
+ t->chart && ae->chart && !strcmp(t->chart, ae->chart)) {
+
+ if(!(t->notifications & HEALTH_ENTRY_NOTIFICATIONS_UPDATED) && !t->updated_by) {
+ t->notifications |= HEALTH_ENTRY_NOTIFICATIONS_UPDATED;
+ t->updated_by = ae;
+
+ if((t->new_status == RRDCALC_STATUS_WARNING || t->new_status == RRDCALC_STATUS_CRITICAL) &&
+ (t->old_status == RRDCALC_STATUS_WARNING || t->old_status == RRDCALC_STATUS_CRITICAL))
+ ae->non_clear_duration += t->non_clear_duration;
+ }
+ else {
+ // no need to continue
+ break;
+ }
+ }
+ }
+ pthread_rwlock_unlock(&host->health_log.alarm_log_rwlock);
+}
+
+static inline void health_alarm_log_process(RRDHOST *host) {
+ static uint32_t last_processed = 0;
+ ALARM_ENTRY *ae;
+
+ pthread_rwlock_rdlock(&host->health_log.alarm_log_rwlock);
+
+ for(ae = host->health_log.alarms; ae ;ae = ae->next) {
+ if(last_processed >= ae->id) break;
+
+ if(!(ae->notifications & HEALTH_ENTRY_NOTIFICATIONS_PROCESSED) &&
+ !(ae->notifications & HEALTH_ENTRY_NOTIFICATIONS_UPDATED)) {
+ ae->notifications |= HEALTH_ENTRY_NOTIFICATIONS_PROCESSED;
+ health_process_notifications(ae);
+ }
+ }
+
+ if(host->health_log.alarms)
+ last_processed = host->health_log.alarms->id;
+
+ pthread_rwlock_unlock(&host->health_log.alarm_log_rwlock);
+
+ if(host->health_log.count <= host->health_log.max)
+ return;
+
+ // cleanup excess entries in the log
+ pthread_rwlock_wrlock(&host->health_log.alarm_log_rwlock);
+
+ ALARM_ENTRY *last = NULL;
+ unsigned int count = host->health_log.max;
+ for(ae = host->health_log.alarms; ae && count ; count--, last = ae, ae = ae->next) ;
+
+ if(ae && last && last->next == ae)
+ last->next = NULL;
+ else
+ ae = NULL;
+
+ while(ae) {
+ ALARM_ENTRY *t = ae->next;
+
+ freez(ae->name);
+ freez(ae->chart);
+ freez(ae->family);
+ freez(ae->exec);
+ freez(ae->source);
+ freez(ae->units);
+ freez(ae->info);
+ freez(ae);
+
+ ae = t;
+ }
+
+ pthread_rwlock_unlock(&host->health_log.alarm_log_rwlock);
+}
+
+void *health_main(void *ptr) {
+ (void)ptr;
+
+ info("HEALTH thread created with task id %d", gettid());
+
+ if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
+ error("Cannot set pthread cancel type to DEFERRED.");
+
+ if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
+ error("Cannot set pthread cancel state to ENABLE.");
+
+ int min_run_every = (int)config_get_number("health", "run at least every seconds", 10);
+ if(min_run_every < 1) min_run_every = 1;
+
+ BUFFER *wb = buffer_create(100);
+
+ unsigned int loop = 0;
+ while(health_enabled) {
+ loop++;
+ debug(D_HEALTH, "Health monitoring iteration no %u started", loop);
+
+ int oldstate, runnable = 0;
+ time_t now = time(NULL);
+ time_t next_run = now + min_run_every;
+ RRDCALC *rc;
+
+ if (unlikely(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate) != 0))
+ error("Cannot set pthread cancel state to DISABLE.");
+
+ rrdhost_rdlock(&localhost);
+
+ // the first loop is to lookup values from the db
+ for (rc = localhost.alarms; rc; rc = rc->next) {
+ if (unlikely(!rrdcalc_isrunnable(rc, now, &next_run)))
+ continue;
+
+ runnable++;
+ rc->old_value = rc->value;
+
+ // 1. if there is database lookup, do it
+ // 2. if there is calculation expression, run it
+
+ if (unlikely(RRDCALC_HAS_DB_LOOKUP(rc))) {
+ time_t old_db_timestamp = rc->db_before;
+ int value_is_null = 0;
+
+ int ret = rrd2value(rc->rrdset, wb, &rc->value,
+ rc->dimensions, 1, rc->after, rc->before, rc->group,
+ rc->options, &rc->db_after, &rc->db_before, &value_is_null);
+
+ if (unlikely(ret != 200)) {
+ // database lookup failed
+ rc->value = NAN;
+
+ debug(D_HEALTH, "Health alarm '%s.%s': database lookup returned error %d", rc->chart?rc->chart:"NOCHART", rc->name, ret);
+
+ if (unlikely(!(rc->rrdcalc_flags & RRDCALC_FLAG_DB_ERROR))) {
+ rc->rrdcalc_flags |= RRDCALC_FLAG_DB_ERROR;
+ error("Health alarm '%s.%s': database lookup returned error %d", rc->chart?rc->chart:"NOCHART", rc->name, ret);
+ }
+ }
+ else if (unlikely(rc->rrdcalc_flags & RRDCALC_FLAG_DB_ERROR))
+ rc->rrdcalc_flags &= ~RRDCALC_FLAG_DB_ERROR;
+
+ if (unlikely(old_db_timestamp == rc->db_before)) {
+ // database is stale
+
+ debug(D_HEALTH, "Health alarm '%s.%s': database is stale", rc->chart?rc->chart:"NOCHART", rc->name);
+
+ if (unlikely(!(rc->rrdcalc_flags & RRDCALC_FLAG_DB_STALE))) {
+ rc->rrdcalc_flags |= RRDCALC_FLAG_DB_STALE;
+ error("Health alarm '%s.%s': database is stale", rc->chart?rc->chart:"NOCHART", rc->name);
+ }
+ }
+ else if (unlikely(rc->rrdcalc_flags & RRDCALC_FLAG_DB_STALE))
+ rc->rrdcalc_flags &= ~RRDCALC_FLAG_DB_STALE;
+
+ if (unlikely(value_is_null)) {
+ // collected value is null
+
+ rc->value = NAN;
+
+ debug(D_HEALTH, "Health alarm '%s.%s': database lookup returned empty value (possibly value is not collected yet)",
+ rc->chart?rc->chart:"NOCHART", rc->name);
+
+ if (unlikely(!(rc->rrdcalc_flags & RRDCALC_FLAG_DB_NAN))) {
+ rc->rrdcalc_flags |= RRDCALC_FLAG_DB_NAN;
+ error("Health alarm '%s.%s': database lookup returned empty value (possibly value is not collected yet)",
+ rc->chart?rc->chart:"NOCHART", rc->name);
+ }
+ }
+ else if (unlikely(rc->rrdcalc_flags & RRDCALC_FLAG_DB_NAN))
+ rc->rrdcalc_flags &= ~RRDCALC_FLAG_DB_NAN;
+
+ debug(D_HEALTH, "Health alarm '%s.%s': database lookup gave value "
+ CALCULATED_NUMBER_FORMAT, rc->chart?rc->chart:"NOCHART", rc->name, rc->value);
+ }
+
+ if(unlikely(rc->calculation)) {
+ if (unlikely(!expression_evaluate(rc->calculation))) {
+ // calculation failed
+
+ rc->value = NAN;
+
+ debug(D_HEALTH, "Health alarm '%s.%s': failed to evaluate calculation with error: %s",
+ rc->chart?rc->chart:"NOCHART", rc->name, buffer_tostring(rc->calculation->error_msg));
+
+ if (unlikely(!(rc->rrdcalc_flags & RRDCALC_FLAG_CALC_ERROR))) {
+ rc->rrdcalc_flags |= RRDCALC_FLAG_CALC_ERROR;
+ error("Health alarm '%s.%s': failed to evaluate calculation with error: %s",
+ rc->chart?rc->chart:"NOCHART", rc->name, buffer_tostring(rc->calculation->error_msg));
+ }
+ }
+ else {
+ if (unlikely(rc->rrdcalc_flags & RRDCALC_FLAG_CALC_ERROR))
+ rc->rrdcalc_flags &= ~RRDCALC_FLAG_CALC_ERROR;
+
+ debug(D_HEALTH, "Health alarm '%s.%s': calculation expression gave value "
+ CALCULATED_NUMBER_FORMAT
+ ": %s (source: %s)",
+ rc->chart?rc->chart:"NOCHART", rc->name,
+ rc->calculation->result,
+ buffer_tostring(rc->calculation->error_msg),
+ rc->source
+ );
+
+ rc->value = rc->calculation->result;
+ }
+ }
+ }
+ rrdhost_unlock(&localhost);
+
+ if (runnable) {
+ rrdhost_rdlock(&localhost);
+
+ for (rc = localhost.alarms; rc; rc = rc->next) {
+ if (unlikely(!rrdcalc_isrunnable(rc, now, &next_run)))
+ continue;
+
+ int warning_status = RRDCALC_STATUS_UNDEFINED;
+ int critical_status = RRDCALC_STATUS_UNDEFINED;
+
+ if(unlikely(rc->warning)) {
+ if(unlikely(!expression_evaluate(rc->warning))) {
+ // calculation failed
+
+ debug(D_HEALTH, "Health alarm '%s.%s': warning expression failed with error: %s",
+ rc->chart?rc->chart:"NOCHART", rc->name, buffer_tostring(rc->warning->error_msg));
+
+ if (unlikely(!(rc->rrdcalc_flags & RRDCALC_FLAG_WARN_ERROR))) {
+ rc->rrdcalc_flags |= RRDCALC_FLAG_WARN_ERROR;
+ error("Health alarm '%s.%s': warning expression failed with error: %s",
+ rc->chart?rc->chart:"NOCHART", rc->name, buffer_tostring(rc->warning->error_msg));
+ }
+ }
+ else {
+ if(unlikely(rc->rrdcalc_flags & RRDCALC_FLAG_WARN_ERROR))
+ rc->rrdcalc_flags &= ~RRDCALC_FLAG_WARN_ERROR;
+
+ debug(D_HEALTH, "Health alarm '%s.%s': warning expression gave value "
+ CALCULATED_NUMBER_FORMAT
+ ": %s (source: %s)",
+ rc->chart?rc->chart:"NOCHART", rc->name,
+ rc->warning->result,
+ buffer_tostring(rc->warning->error_msg),
+ rc->source
+ );
+
+ warning_status = rrdcalc_value2status(rc->warning->result);
+ }
+ }
+
+ if(unlikely(rc->critical)) {
+ if(unlikely(!expression_evaluate(rc->critical))) {
+ // calculation failed
+
+ debug(D_HEALTH, "Health alarm '%s.%s': critical expression failed with error: %s",
+ rc->chart?rc->chart:"NOCHART", rc->name, buffer_tostring(rc->critical->error_msg));
+
+ if (unlikely(!(rc->rrdcalc_flags & RRDCALC_FLAG_CRIT_ERROR))) {
+ rc->rrdcalc_flags |= RRDCALC_FLAG_CRIT_ERROR;
+ error("Health alarm '%s.%s': critical expression failed with error: %s",
+ rc->chart?rc->chart:"NOCHART", rc->name, buffer_tostring(rc->critical->error_msg));
+ }
+ }
+ else {
+ if(unlikely(rc->rrdcalc_flags & RRDCALC_FLAG_CRIT_ERROR))
+ rc->rrdcalc_flags &= ~RRDCALC_FLAG_CRIT_ERROR;
+
+ debug(D_HEALTH, "Health alarm '%s.%s': critical expression gave value "
+ CALCULATED_NUMBER_FORMAT
+ ": %s (source: %s)",
+ rc->chart?rc->chart:"NOCHART", rc->name,
+ rc->critical->result,
+ buffer_tostring(rc->critical->error_msg),
+ rc->source
+ );
+
+ critical_status = rrdcalc_value2status(rc->critical->result);
+ }
+ }
+
+ int status = RRDCALC_STATUS_UNDEFINED;
+
+ switch(warning_status) {
+ case RRDCALC_STATUS_CLEAR:
+ status = RRDCALC_STATUS_CLEAR;
+ break;
+
+ case RRDCALC_STATUS_RAISED:
+ status = RRDCALC_STATUS_WARNING;
+ break;
+
+ default:
+ break;
+ }
+
+ switch(critical_status) {
+ case RRDCALC_STATUS_CLEAR:
+ if(status == RRDCALC_STATUS_UNDEFINED)
+ status = RRDCALC_STATUS_CLEAR;
+ break;
+
+ case RRDCALC_STATUS_RAISED:
+ status = RRDCALC_STATUS_CRITICAL;
+ break;
+
+ default:
+ break;
+ }
+
+ if(status != rc->status) {
+ health_alarm_log(&localhost, time(NULL), rc->name, rc->rrdset->id, rc->rrdset->family, rc->exec, now - rc->last_status_change, rc->old_value, rc->value, rc->status, status, rc->source, rc->units, rc->info);
+ rc->last_status_change = now;
+ rc->status = status;
+ }
+
+ rc->last_updated = now;
+ rc->next_update = now + rc->update_every;
+
+ if (next_run > rc->next_update)
+ next_run = rc->next_update;
+ }
+
+ rrdhost_unlock(&localhost);
+ }
+
+ if (unlikely(pthread_setcancelstate(oldstate, NULL) != 0))
+ error("Cannot set pthread cancel state to RESTORE (%d).", oldstate);
+
+ // execute notifications
+ // and cleanup
+ health_alarm_log_process(&localhost);
+
+ now = time(NULL);
+ if(now < next_run) {
+ debug(D_HEALTH, "Health monitoring iteration no %u done. Next iteration in %d secs",
+ loop, (int) (next_run - now));
+ sleep_usec(1000000 * (unsigned long long) (next_run - now));
+ }
+ else {
+ debug(D_HEALTH, "Health monitoring iteration no %u done. Next iteration now", loop);
+ }
+ }
+
+ buffer_free(wb);
+
+ info("HEALTH thread exiting");
+ pthread_exit(NULL);
+ return NULL;
+}
diff --git a/src/health.h b/src/health.h
new file mode 100644
index 000000000..ef1158a29
--- /dev/null
+++ b/src/health.h
@@ -0,0 +1,283 @@
+#ifndef NETDATA_HEALTH_H
+#define NETDATA_HEALTH_H
+
+extern int health_enabled;
+
+extern int rrdvar_compare(void *a, void *b);
+
+#define RRDVAR_TYPE_CALCULATED 1
+#define RRDVAR_TYPE_TIME_T 2
+#define RRDVAR_TYPE_COLLECTED 3
+#define RRDVAR_TYPE_TOTAL 4
+#define RRDVAR_TYPE_INT 5
+
+// the variables as stored in the variables indexes
+// there are 3 indexes:
+// 1. at each chart (RRDSET.variables_root_index)
+// 2. at each context (RRDFAMILY.variables_root_index)
+// 3. at each host (RRDHOST.variables_root_index)
+typedef struct rrdvar {
+ avl avl;
+
+ char *name;
+ uint32_t hash;
+
+ int type;
+ void *value;
+
+ time_t last_updated;
+} RRDVAR;
+
+// variables linked to charts
+// We link variables to point to the values that are already
+// calculated / processed by the normal data collection process
+// This means, there will be no speed penalty for using
+// these variables
+typedef struct rrdsetvar {
+ char *fullid; // chart type.chart id.variable
+ char *fullname; // chart type.chart name.variable
+ char *variable; // variable
+
+ int type;
+ void *value;
+
+ uint32_t options;
+
+ RRDVAR *local;
+ RRDVAR *family;
+ RRDVAR *host;
+ RRDVAR *family_name;
+ RRDVAR *host_name;
+
+ struct rrdset *rrdset;
+
+ struct rrdsetvar *next;
+} RRDSETVAR;
+
+
+// variables linked to individual dimensions
+// We link variables to point the values that are already
+// calculated / processed by the normal data collection process
+// This means, there will be no speed penalty for using
+// these variables
+typedef struct rrddimvar {
+ char *prefix;
+ char *suffix;
+
+ char *id; // dimension id
+ char *name; // dimension name
+ char *fullidid; // chart type.chart id.dimension id
+ char *fullidname; // chart type.chart id.dimension name
+ char *fullnameid; // chart type.chart name.dimension id
+ char *fullnamename; // chart type.chart name.dimension name
+
+ int type;
+ void *value;
+
+ uint32_t options;
+
+ RRDVAR *local_id;
+ RRDVAR *local_name;
+
+ RRDVAR *family_id;
+ RRDVAR *family_name;
+
+ RRDVAR *host_fullidid;
+ RRDVAR *host_fullidname;
+ RRDVAR *host_fullnameid;
+ RRDVAR *host_fullnamename;
+
+ struct rrddim *rrddim;
+
+ struct rrddimvar *next;
+} RRDDIMVAR;
+
+// calculated variables (defined in health configuration)
+// These aggregate time-series data at fixed intervals
+// (defined in their update_every member below)
+// These increase the overhead of netdata.
+//
+// These calculations are allocated and linked (->next)
+// under RRDHOST.
+// Then are also linked to RRDSET (of course only when the
+// chart is found, via ->rrdset_next and ->rrdset_prev).
+// This double-linked list is maintained sorted at all times
+// having as RRDSET.calculations the RRDCALC to be processed
+// next.
+
+#define RRDCALC_STATUS_UNINITIALIZED 0
+#define RRDCALC_STATUS_UNDEFINED -1
+#define RRDCALC_STATUS_CLEAR 1
+#define RRDCALC_STATUS_RAISED 2
+#define RRDCALC_STATUS_WARNING 3
+#define RRDCALC_STATUS_CRITICAL 4
+
+#define RRDCALC_FLAG_DB_ERROR 0x00000001
+#define RRDCALC_FLAG_DB_NAN 0x00000002
+#define RRDCALC_FLAG_DB_STALE 0x00000004
+#define RRDCALC_FLAG_CALC_ERROR 0x00000008
+#define RRDCALC_FLAG_WARN_ERROR 0x00000010
+#define RRDCALC_FLAG_CRIT_ERROR 0x00000020
+
+typedef struct rrdcalc {
+ char *name;
+ uint32_t hash;
+
+ char *exec;
+
+ char *chart; // the chart id this should be linked to
+ uint32_t hash_chart;
+
+ char *source; // the source of this calculation
+ char *units;
+ char *info;
+
+ char *dimensions; // the chart dimensions
+
+ int group; // grouping method: average, max, etc.
+ int before; // ending point in time-series
+ int after; // starting point in time-series
+ uint32_t options; // calculation options
+ int update_every; // update frequency for the calculation
+
+ time_t last_updated;
+ time_t next_update;
+
+ EVAL_EXPRESSION *calculation;
+ EVAL_EXPRESSION *warning;
+ EVAL_EXPRESSION *critical;
+
+ uint32_t rrdcalc_flags;
+ int status;
+
+ time_t db_after;
+ time_t db_before;
+ time_t last_status_change;
+
+ calculated_number value;
+ calculated_number old_value;
+
+ calculated_number green;
+ calculated_number red;
+
+ RRDVAR *local;
+ RRDVAR *family;
+ RRDVAR *hostid;
+ RRDVAR *hostname;
+
+ struct rrdset *rrdset;
+ struct rrdcalc *rrdset_next;
+ struct rrdcalc *rrdset_prev;
+
+ struct rrdcalc *next;
+} RRDCALC;
+
+#define RRDCALC_HAS_DB_LOOKUP(rc) ((rc)->after)
+
+// RRDCALCTEMPLATE
+// these are to be applied to charts found dynamically
+// based on their context.
+typedef struct rrdcalctemplate {
+ char *name;
+ uint32_t hash_name;
+
+ char *exec;
+
+ char *context;
+ uint32_t hash_context;
+
+ char *source; // the source of this template
+ char *units;
+ char *info;
+
+ char *dimensions;
+
+ int group; // grouping method: average, max, etc.
+ int before; // ending point in time-series
+ int after; // starting point in time-series
+ uint32_t options; // calculation options
+ int update_every; // update frequency for the calculation
+
+ EVAL_EXPRESSION *calculation;
+ EVAL_EXPRESSION *warning;
+ EVAL_EXPRESSION *critical;
+
+ calculated_number green;
+ calculated_number red;
+
+ struct rrdcalctemplate *next;
+} RRDCALCTEMPLATE;
+
+#define RRDCALCTEMPLATE_HAS_CALCULATION(rt) ((rt)->after)
+
+#define HEALTH_ENTRY_NOTIFICATIONS_PROCESSED 0x00000001
+#define HEALTH_ENTRY_NOTIFICATIONS_UPDATED 0x00000002
+#define HEALTH_ENTRY_NOTIFICATIONS_EXEC_RUN 0x00000004
+#define HEALTH_ENTRY_NOTIFICATIONS_EXEC_FAILED 0x00000008
+
+typedef struct alarm_entry {
+ uint32_t id;
+
+ time_t when;
+ time_t duration;
+ time_t non_clear_duration;
+
+ char *name;
+ uint32_t hash_name;
+
+ char *chart;
+ uint32_t hash_chart;
+
+ char *family;
+
+ char *exec;
+ int exec_code;
+
+ char *source;
+ char *units;
+ char *info;
+
+ calculated_number old_value;
+ calculated_number new_value;
+ int old_status;
+ int new_status;
+
+ uint32_t notifications;
+
+ struct alarm_entry *updated_by;
+ struct alarm_entry *next;
+} ALARM_ENTRY;
+
+typedef struct alarm_log {
+ uint32_t nextid;
+ unsigned int count;
+ unsigned int max;
+ ALARM_ENTRY *alarms;
+ pthread_rwlock_t alarm_log_rwlock;
+} ALARM_LOG;
+
+#include "rrd.h"
+
+extern void rrdsetvar_rename_all(RRDSET *st);
+extern RRDSETVAR *rrdsetvar_create(RRDSET *st, const char *variable, int type, void *value, uint32_t options);
+extern void rrdsetvar_free(RRDSETVAR *rs);
+
+extern void rrddimvar_rename_all(RRDDIM *rd);
+extern RRDDIMVAR *rrddimvar_create(RRDDIM *rd, int type, const char *prefix, const char *suffix, void *value, uint32_t options);
+extern void rrddimvar_free(RRDDIMVAR *rs);
+
+extern void rrdsetcalc_link_matching(RRDSET *st);
+extern void rrdsetcalc_unlink(RRDCALC *rc);
+extern void rrdcalctemplate_link_matching(RRDSET *st);
+extern RRDCALC *rrdcalc_find(RRDSET *st, const char *name);
+
+extern void health_init(void);
+extern void *health_main(void *ptr);
+
+extern void health_reload(void);
+
+extern int health_variable_lookup(const char *variable, uint32_t hash, RRDCALC *rc, calculated_number *result);
+extern void health_alarms2json(RRDHOST *host, BUFFER *wb, int all);
+extern void health_alarm_log2json(RRDHOST *host, BUFFER *wb);
+
+#endif //NETDATA_HEALTH_H
diff --git a/src/log.c b/src/log.c
index 717126a69..e952c5724 100644
--- a/src/log.c
+++ b/src/log.c
@@ -1,241 +1,344 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <time.h>
-#include <syslog.h>
-#include <errno.h>
-#include <string.h>
-#include <unistd.h>
-#include <stdlib.h>
-
-#include "log.h"
#include "common.h"
-
-// ----------------------------------------------------------------------------
-// LOG
-
const char *program_name = "";
unsigned long long debug_flags = DEBUG;
-int silent = 0;
+int access_log_syslog = 1;
+int error_log_syslog = 1;
+int output_log_syslog = 1; // debug log
-int access_fd = -1;
+int stdaccess_fd = -1;
FILE *stdaccess = NULL;
-int access_log_syslog = 1;
-int error_log_syslog = 1;
-int output_log_syslog = 1; // debug log
+const char *stdaccess_filename = NULL;
+const char *stderr_filename = NULL;
+const char *stdout_filename = NULL;
+
+void syslog_init(void) {
+ static int i = 0;
+
+ if(!i) {
+ openlog(program_name, LOG_PID, LOG_DAEMON);
+ i = 1;
+ }
+}
+
+int open_log_file(int fd, FILE **fp, const char *filename, int *enabled_syslog) {
+ int f, t;
+
+ if(!filename || !*filename || !strcmp(filename, "none"))
+ filename = "/dev/null";
+
+ if(!strcmp(filename, "syslog")) {
+ filename = "/dev/null";
+ syslog_init();
+ if(enabled_syslog) *enabled_syslog = 1;
+ }
+ else if(enabled_syslog) *enabled_syslog = 0;
+
+ // don't do anything if the user is willing
+ // to have the standard one
+ if(!strcmp(filename, "system")) {
+ if(fd != -1) return fd;
+ filename = "stdout";
+ }
+
+ if(!strcmp(filename, "stdout"))
+ f = STDOUT_FILENO;
+
+ else if(!strcmp(filename, "stderr"))
+ f = STDERR_FILENO;
+
+ else {
+ f = open(filename, O_WRONLY | O_APPEND | O_CREAT, 0664);
+ if(f == -1) {
+ error("Cannot open file '%s'. Leaving %d to its default.", filename, fd);
+ return fd;
+ }
+ }
+
+ // if there is a level-2 file pointer
+ // flush it before switching the level-1 fds
+ if(fp && *fp)
+ fflush(*fp);
+
+ if(fd != f && fd != -1) {
+ // it automatically closes
+ t = dup2(f, fd);
+ if (t == -1) {
+ error("Cannot dup2() new fd %d to old fd %d for '%s'", f, fd, filename);
+ close(f);
+ return fd;
+ }
+ // info("dup2() new fd %d to old fd %d for '%s'", f, fd, filename);
+ close(f);
+ }
+ else fd = f;
+
+ if(fp && !*fp) {
+ *fp = fdopen(fd, "a");
+ if (!*fp)
+ error("Cannot fdopen() fd %d ('%s')", fd, filename);
+ else {
+ if (setvbuf(*fp, NULL, _IOLBF, 0) != 0)
+ error("Cannot set line buffering on fd %d ('%s')", fd, filename);
+ }
+ }
+
+ return fd;
+}
+
+void reopen_all_log_files() {
+ if(stdout_filename)
+ open_log_file(STDOUT_FILENO, &stdout, stdout_filename, &output_log_syslog);
+
+ if(stderr_filename)
+ open_log_file(STDERR_FILENO, &stderr, stderr_filename, &error_log_syslog);
+
+ if(stdaccess_filename)
+ stdaccess_fd = open_log_file(stdaccess_fd, &stdaccess, stdaccess_filename, &access_log_syslog);
+}
+
+void open_all_log_files() {
+ // disable stdin
+ open_log_file(STDIN_FILENO, &stdin, "/dev/null", NULL);
+
+ open_log_file(STDOUT_FILENO, &stdout, stdout_filename, &output_log_syslog);
+ open_log_file(STDERR_FILENO, &stderr, stderr_filename, &error_log_syslog);
+ stdaccess_fd = open_log_file(stdaccess_fd, &stdaccess, stdaccess_filename, &access_log_syslog);
+}
+
+// ----------------------------------------------------------------------------
+// error log throttling
time_t error_log_throttle_period = 1200;
unsigned long error_log_errors_per_period = 200;
int error_log_limit(int reset) {
- static time_t start = 0;
- static unsigned long counter = 0, prevented = 0;
-
- // do not throttle if the period is 0
- if(error_log_throttle_period == 0)
- return 0;
-
- // prevent all logs if the errors per period is 0
- if(error_log_errors_per_period == 0)
- return 1;
-
- time_t now = time(NULL);
- if(!start) start = now;
-
- if(reset) {
- if(prevented) {
- log_date(stderr);
- fprintf(stderr, "%s: Resetting logging for process '%s' (prevented %lu logs in the last %ld seconds).\n"
- , program_name
- , program_name
- , prevented
- , now - start
- );
- }
-
- start = now;
- counter = 0;
- prevented = 0;
- }
-
- // detect if we log too much
- counter++;
-
- if(now - start > error_log_throttle_period) {
- if(prevented) {
- log_date(stderr);
- fprintf(stderr, "%s: Resuming logging from process '%s' (prevented %lu logs in the last %ld seconds).\n"
- , program_name
- , program_name
- , prevented
- , error_log_throttle_period
- );
- }
-
- // restart the period accounting
- start = now;
- counter = 1;
- prevented = 0;
-
- // log this error
- return 0;
- }
-
- if(counter > error_log_errors_per_period) {
- if(!prevented) {
- log_date(stderr);
- fprintf(stderr, "%s: Too many logs (%lu logs in %ld seconds, threshold is set to %lu logs in %ld seconds). Preventing more logs from process '%s' for %ld seconds.\n"
- , program_name
- , counter
- , now - start
- , error_log_errors_per_period
- , error_log_throttle_period
- , program_name
- , start + error_log_throttle_period - now);
- }
-
- prevented++;
-
- // prevent logging this error
- return 1;
- }
-
- return 0;
+ static time_t start = 0;
+ static unsigned long counter = 0, prevented = 0;
+
+ // do not throttle if the period is 0
+ if(error_log_throttle_period == 0)
+ return 0;
+
+ // prevent all logs if the errors per period is 0
+ if(error_log_errors_per_period == 0)
+ return 1;
+
+ time_t now = time(NULL);
+ if(!start) start = now;
+
+ if(reset) {
+ if(prevented) {
+ log_date(stderr);
+ fprintf(stderr, "%s: Resetting logging for process '%s' (prevented %lu logs in the last %ld seconds).\n"
+ , program_name
+ , program_name
+ , prevented
+ , now - start
+ );
+ }
+
+ start = now;
+ counter = 0;
+ prevented = 0;
+ }
+
+ // detect if we log too much
+ counter++;
+
+ if(now - start > error_log_throttle_period) {
+ if(prevented) {
+ log_date(stderr);
+ fprintf(stderr, "%s: Resuming logging from process '%s' (prevented %lu logs in the last %ld seconds).\n"
+ , program_name
+ , program_name
+ , prevented
+ , error_log_throttle_period
+ );
+ }
+
+ // restart the period accounting
+ start = now;
+ counter = 1;
+ prevented = 0;
+
+ // log this error
+ return 0;
+ }
+
+ if(counter > error_log_errors_per_period) {
+ if(!prevented) {
+ log_date(stderr);
+ fprintf(stderr, "%s: Too many logs (%lu logs in %ld seconds, threshold is set to %lu logs in %ld seconds). Preventing more logs from process '%s' for %ld seconds.\n"
+ , program_name
+ , counter
+ , now - start
+ , error_log_errors_per_period
+ , error_log_throttle_period
+ , program_name
+ , start + error_log_throttle_period - now);
+ }
+
+ prevented++;
+
+ // prevent logging this error
+ return 1;
+ }
+
+ return 0;
}
+// ----------------------------------------------------------------------------
+// print the date
+
+// FIXME
+// this should print the date in a buffer the way it
+// is now, logs from multiple threads may be multiplexed
+
void log_date(FILE *out)
{
- char outstr[200];
- time_t t;
- struct tm *tmp, tmbuf;
+ char outstr[24];
+ time_t t;
+ struct tm *tmp, tmbuf;
- t = time(NULL);
- tmp = localtime_r(&t, &tmbuf);
+ t = time(NULL);
+ tmp = localtime_r(&t, &tmbuf);
- if (tmp == NULL) return;
- if (strftime(outstr, sizeof(outstr), "%y-%m-%d %H:%M:%S", tmp) == 0) return;
+ if (tmp == NULL) return;
+ if (unlikely(strftime(outstr, sizeof(outstr), "%y-%m-%d %H:%M:%S", tmp) == 0)) return;
- fprintf(out, "%s: ", outstr);
+ fprintf(out, "%s: ", outstr);
}
+// ----------------------------------------------------------------------------
+// debug log
+
void debug_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... )
{
- va_list args;
-
- log_date(stdout);
- va_start( args, fmt );
- fprintf(stdout, "DEBUG (%04lu@%-10.10s:%-15.15s): %s: ", line, file, function, program_name);
- vfprintf( stdout, fmt, args );
- va_end( args );
- fprintf(stdout, "\n");
- // fflush( stdout );
-
- if(output_log_syslog) {
- va_start( args, fmt );
- vsyslog(LOG_ERR, fmt, args );
- va_end( args );
- }
+ va_list args;
+
+ log_date(stdout);
+ va_start( args, fmt );
+ printf("DEBUG (%04lu@%-10.10s:%-15.15s): %s: ", line, file, function, program_name);
+ vprintf(fmt, args);
+ va_end( args );
+ putchar('\n');
+
+ if(output_log_syslog) {
+ va_start( args, fmt );
+ vsyslog(LOG_ERR, fmt, args );
+ va_end( args );
+ }
+
+ fflush(stdout);
}
+// ----------------------------------------------------------------------------
+// info log
+
void info_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... )
{
- va_list args;
+ va_list args;
- // prevent logging too much
- if(error_log_limit(0)) return;
+ // prevent logging too much
+ if(error_log_limit(0)) return;
- log_date(stderr);
+ log_date(stderr);
- va_start( args, fmt );
- if(debug_flags) fprintf(stderr, "INFO (%04lu@%-10.10s:%-15.15s): %s: ", line, file, function, program_name);
- else fprintf(stderr, "INFO: %s: ", program_name);
- vfprintf( stderr, fmt, args );
- va_end( args );
+ va_start( args, fmt );
+ if(debug_flags) fprintf(stderr, "INFO (%04lu@%-10.10s:%-15.15s): %s: ", line, file, function, program_name);
+ else fprintf(stderr, "INFO: %s: ", program_name);
+ vfprintf( stderr, fmt, args );
+ va_end( args );
- fprintf(stderr, "\n");
+ fputc('\n', stderr);
- if(error_log_syslog) {
- va_start( args, fmt );
- vsyslog(LOG_INFO, fmt, args );
- va_end( args );
- }
+ if(error_log_syslog) {
+ va_start( args, fmt );
+ vsyslog(LOG_INFO, fmt, args );
+ va_end( args );
+ }
}
+// ----------------------------------------------------------------------------
+// error log
+
void error_int( const char *prefix, const char *file, const char *function, const unsigned long line, const char *fmt, ... )
{
- va_list args;
-
- // prevent logging too much
- if(error_log_limit(0)) return;
-
- log_date(stderr);
-
- va_start( args, fmt );
- if(debug_flags) fprintf(stderr, "%s (%04lu@%-10.10s:%-15.15s): %s: ", prefix, line, file, function, program_name);
- else fprintf(stderr, "%s: %s: ", prefix, program_name);
- vfprintf( stderr, fmt, args );
- va_end( args );
-
- if(errno) {
- char buf[200];
- char *s = strerror_r(errno, buf, 200);
- fprintf(stderr, " (errno %d, %s)\n", errno, s);
- errno = 0;
- }
- else fprintf(stderr, "\n");
-
- if(error_log_syslog) {
- va_start( args, fmt );
- vsyslog(LOG_ERR, fmt, args );
- va_end( args );
- }
+ va_list args;
+
+ // prevent logging too much
+ if(error_log_limit(0)) return;
+
+ log_date(stderr);
+
+ va_start( args, fmt );
+ if(debug_flags) fprintf(stderr, "%s (%04lu@%-10.10s:%-15.15s): %s: ", prefix, line, file, function, program_name);
+ else fprintf(stderr, "%s: %s: ", prefix, program_name);
+ vfprintf( stderr, fmt, args );
+ va_end( args );
+
+ if(errno) {
+ char buf[1024];
+ fprintf(stderr, " (errno %d, %s)\n", errno, strerror_r(errno, buf, 1023));
+ errno = 0;
+ }
+ else
+ fputc('\n', stderr);
+
+ if(error_log_syslog) {
+ va_start( args, fmt );
+ vsyslog(LOG_ERR, fmt, args );
+ va_end( args );
+ }
}
void fatal_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... )
{
- va_list args;
+ va_list args;
- log_date(stderr);
+ log_date(stderr);
- va_start( args, fmt );
- if(debug_flags) fprintf(stderr, "FATAL (%04lu@%-10.10s:%-15.15s): %s: ", line, file, function, program_name);
- else fprintf(stderr, "FATAL: %s: ", program_name);
- vfprintf( stderr, fmt, args );
- va_end( args );
+ va_start( args, fmt );
+ if(debug_flags) fprintf(stderr, "FATAL (%04lu@%-10.10s:%-15.15s): %s: ", line, file, function, program_name);
+ else fprintf(stderr, "FATAL: %s: ", program_name);
+ vfprintf( stderr, fmt, args );
+ va_end( args );
- perror(" # ");
- fprintf(stderr, "\n");
+ perror(" # ");
+ fputc('\n', stderr);
- if(error_log_syslog) {
- va_start( args, fmt );
- vsyslog(LOG_CRIT, fmt, args );
- va_end( args );
- }
+ if(error_log_syslog) {
+ va_start( args, fmt );
+ vsyslog(LOG_CRIT, fmt, args );
+ va_end( args );
+ }
- exit(1);
+ netdata_cleanup_and_exit(1);
}
+// ----------------------------------------------------------------------------
+// access log
+
void log_access( const char *fmt, ... )
{
- va_list args;
-
- if(stdaccess) {
- log_date(stdaccess);
-
- va_start( args, fmt );
- vfprintf( stdaccess, fmt, args );
- va_end( args );
- fprintf( stdaccess, "\n");
- // fflush( stdaccess );
- }
-
- if(access_log_syslog) {
- va_start( args, fmt );
- vsyslog(LOG_INFO, fmt, args );
- va_end( args );
- }
+ va_list args;
+
+ if(stdaccess) {
+ log_date(stdaccess);
+
+ va_start( args, fmt );
+ vfprintf( stdaccess, fmt, args );
+ va_end( args );
+ fputc('\n', stdaccess);
+ }
+
+ if(access_log_syslog) {
+ va_start( args, fmt );
+ vsyslog(LOG_INFO, fmt, args );
+ va_end( args );
+ }
}
diff --git a/src/log.h b/src/log.h
index 3f811d9fb..93d95321e 100644
--- a/src/log.h
+++ b/src/log.h
@@ -1,18 +1,14 @@
-#include <stdio.h>
-#include <stdarg.h>
-#include <time.h>
-
#ifndef NETDATA_LOG_H
#define NETDATA_LOG_H 1
-#define D_WEB_BUFFER 0x00000001
-#define D_WEB_CLIENT 0x00000002
-#define D_LISTENER 0x00000004
-#define D_WEB_DATA 0x00000008
-#define D_OPTIONS 0x00000010
+#define D_WEB_BUFFER 0x00000001
+#define D_WEB_CLIENT 0x00000002
+#define D_LISTENER 0x00000004
+#define D_WEB_DATA 0x00000008
+#define D_OPTIONS 0x00000010
#define D_PROCNETDEV_LOOP 0x00000020
-#define D_RRD_STATS 0x00000040
-#define D_WEB_CLIENT_ACCESS 0x00000080
+#define D_RRD_STATS 0x00000040
+#define D_WEB_CLIENT_ACCESS 0x00000080
#define D_TC_LOOP 0x00000100
#define D_DEFLATE 0x00000200
#define D_CONFIG 0x00000400
@@ -20,13 +16,15 @@
#define D_CHILDS 0x00001000
#define D_EXIT 0x00002000
#define D_CHECKS 0x00004000
-#define D_NFACCT_LOOP 0x00008000
-#define D_PROCFILE 0x00010000
-#define D_RRD_CALLS 0x00020000
-#define D_DICTIONARY 0x00040000
-#define D_MEMORY 0x00080000
+#define D_NFACCT_LOOP 0x00008000
+#define D_PROCFILE 0x00010000
+#define D_RRD_CALLS 0x00020000
+#define D_DICTIONARY 0x00040000
+#define D_MEMORY 0x00080000
#define D_CGROUP 0x00100000
-#define D_REGISTRY 0x00200000
+#define D_REGISTRY 0x00200000
+#define D_VARIABLES 0x00400000
+#define D_HEALTH 0x00800000
//#define DEBUG (D_WEB_CLIENT_ACCESS|D_LISTENER|D_RRD_STATS)
//#define DEBUG 0xffffffff
@@ -36,11 +34,13 @@ extern unsigned long long debug_flags;
extern const char *program_name;
-extern int silent;
-
-extern int access_fd;
+extern int stdaccess_fd;
extern FILE *stdaccess;
+extern const char *stdaccess_filename;
+extern const char *stderr_filename;
+extern const char *stdout_filename;
+
extern int access_log_syslog;
extern int error_log_syslog;
extern int output_log_syslog;
@@ -49,19 +49,23 @@ extern time_t error_log_throttle_period;
extern unsigned long error_log_errors_per_period;
extern int error_log_limit(int reset);
+extern void open_all_log_files();
+extern void reopen_all_log_files();
+
#define error_log_limit_reset() do { error_log_limit(1); } while(0)
+#define error_log_limit_unlimited() do { error_log_throttle_period = 0; } while(0)
-#define debug(type, args...) do { if(unlikely(!silent && (debug_flags & type))) debug_int(__FILE__, __FUNCTION__, __LINE__, ##args); } while(0)
+#define debug(type, args...) do { if(unlikely(debug_flags & type)) debug_int(__FILE__, __FUNCTION__, __LINE__, ##args); } while(0)
#define info(args...) info_int(__FILE__, __FUNCTION__, __LINE__, ##args)
#define infoerr(args...) error_int("INFO", __FILE__, __FUNCTION__, __LINE__, ##args)
#define error(args...) error_int("ERROR", __FILE__, __FUNCTION__, __LINE__, ##args)
#define fatal(args...) fatal_int(__FILE__, __FUNCTION__, __LINE__, ##args)
extern void log_date(FILE *out);
-extern void debug_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... );
-extern void info_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... );
-extern void error_int( const char *prefix, const char *file, const char *function, const unsigned long line, const char *fmt, ... );
-extern void fatal_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... ) __attribute__ ((noreturn));
-extern void log_access( const char *fmt, ... );
+extern void debug_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... ) __attribute__ (( format (printf, 4, 5)));
+extern void info_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... ) __attribute__ (( format (printf, 4, 5)));
+extern void error_int( const char *prefix, const char *file, const char *function, const unsigned long line, const char *fmt, ... ) __attribute__ (( format (printf, 5, 6)));
+extern void fatal_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... ) __attribute__ ((noreturn, format (printf, 4, 5)));
+extern void log_access( const char *fmt, ... ) __attribute__ (( format (printf, 1, 2)));
#endif /* NETDATA_LOG_H */
diff --git a/src/main.c b/src/main.c
index ec3c59ca7..4d55a1425 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1,573 +1,702 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <signal.h>
-#include <syslog.h>
-#include <pthread.h>
-#include <string.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <sys/time.h>
-#include <sys/resource.h>
-#include <sys/mman.h>
-#include <sys/prctl.h>
-
#include "common.h"
-#include "log.h"
-#include "daemon.h"
-#include "web_server.h"
-#include "popen.h"
-#include "appconfig.h"
-#include "web_client.h"
-#include "rrd.h"
-#include "rrd2json.h"
-
-#include "unit_test.h"
-
-#include "plugins_d.h"
-#include "plugin_idlejitter.h"
-#include "plugin_tc.h"
-#include "plugin_checks.h"
-#include "plugin_proc.h"
-#include "plugin_nfacct.h"
-#include "registry.h"
-
-#include "main.h"
extern void *cgroups_main(void *ptr);
-volatile sig_atomic_t netdata_exit = 0;
-
-void netdata_cleanup_and_exit(int ret)
-{
- netdata_exit = 1;
- rrdset_save_all();
- // kill_childs();
-
- // let it log a few more error messages
- error_log_limit_reset();
+void netdata_cleanup_and_exit(int ret) {
+ netdata_exit = 1;
- if(pidfd != -1) {
- if(ftruncate(pidfd, 0) != 0)
- error("Cannot truncate pidfile '%s'.", pidfile);
+ error_log_limit_unlimited();
- close(pidfd);
- pidfd = -1;
- }
+ info("Called: netdata_cleanup_and_exit()");
+#ifdef NETDATA_INTERNAL_CHECKS
+ rrdset_free_all();
+#else
+ rrdset_save_all();
+#endif
+ // kill_childs();
- if(pidfile[0]) {
- if(unlink(pidfile) != 0)
- error("Cannot unlink pidfile '%s'.", pidfile);
- }
+ if(pidfile[0]) {
+ if(unlink(pidfile) != 0)
+ error("Cannot unlink pidfile '%s'.", pidfile);
+ }
- info("NetData exiting. Bye bye...");
- exit(ret);
+ info("NetData exiting. Bye bye...");
+ exit(ret);
}
struct netdata_static_thread {
- char *name;
+ char *name;
- char *config_section;
- char *config_name;
+ char *config_section;
+ char *config_name;
- int enabled;
+ int enabled;
- pthread_t *thread;
-
- void (*init_routine) (void);
- void *(*start_routine) (void *);
-};
-
-struct netdata_static_thread static_threads[] = {
- {"tc", "plugins", "tc", 1, NULL, NULL, tc_main},
- {"idlejitter", "plugins", "idlejitter", 1, NULL, NULL, cpuidlejitter_main},
- {"proc", "plugins", "proc", 1, NULL, NULL, proc_main},
- {"cgroups", "plugins", "cgroups", 1, NULL, NULL, cgroups_main},
+ pthread_t *thread;
+ void (*init_routine) (void);
+ void *(*start_routine) (void *);
+} static_threads[] = {
#ifdef INTERNAL_PLUGIN_NFACCT
- // nfacct requires root access
- // so, we build it as an external plugin with setuid to root
- {"nfacct", "plugins", "nfacct", 1, NULL, NULL, nfacct_main},
+// nfacct requires root access
+ // so, we build it as an external plugin with setuid to root
+ {"nfacct", "plugins", "nfacct", 1, NULL, NULL, nfacct_main},
#endif
- {"plugins.d", NULL, NULL, 1, NULL, NULL, pluginsd_main},
- {"check", "plugins", "checks", 0, NULL, NULL, checks_main},
- {"web", NULL, NULL, 1, NULL, NULL, socket_listen_main},
- {NULL, NULL, NULL, 0, NULL, NULL, NULL}
+ {"tc", "plugins", "tc", 1, NULL, NULL, tc_main},
+ {"idlejitter", "plugins", "idlejitter", 1, NULL, NULL, cpuidlejitter_main},
+ {"proc", "plugins", "proc", 1, NULL, NULL, proc_main},
+ {"cgroups", "plugins", "cgroups", 1, NULL, NULL, cgroups_main},
+ {"check", "plugins", "checks", 0, NULL, NULL, checks_main},
+ {"health", NULL, NULL, 1, NULL, NULL, health_main},
+ {"plugins.d", NULL, NULL, 1, NULL, NULL, pluginsd_main},
+ {"web", NULL, NULL, 1, NULL, NULL, socket_listen_main_multi_threaded},
+ {"web-single-threaded", NULL, NULL, 0, NULL, NULL, socket_listen_main_single_threaded},
+ {NULL, NULL, NULL, 0, NULL, NULL, NULL}
};
+void web_server_threading_selection(void) {
+ int threaded = config_get_boolean("global", "multi threaded web server", 1);
+
+ int i;
+ for(i = 0; static_threads[i].name ; i++) {
+ if(static_threads[i].start_routine == socket_listen_main_multi_threaded)
+ static_threads[i].enabled = threaded?1:0;
+
+ if(static_threads[i].start_routine == socket_listen_main_single_threaded)
+ static_threads[i].enabled = threaded?0:1;
+ }
+
+ web_client_timeout = (int) config_get_number("global", "disconnect idle web clients after seconds", DEFAULT_DISCONNECT_IDLE_WEB_CLIENTS_AFTER_SECONDS);
+
+ web_donotrack_comply = config_get_boolean("global", "respect web browser do not track policy", web_donotrack_comply);
+
+#ifdef NETDATA_WITH_ZLIB
+ web_enable_gzip = config_get_boolean("global", "enable web responses gzip compression", web_enable_gzip);
+
+ char *s = config_get("global", "web compression strategy", "default");
+ if(!strcmp(s, "default"))
+ web_gzip_strategy = Z_DEFAULT_STRATEGY;
+ else if(!strcmp(s, "filtered"))
+ web_gzip_strategy = Z_FILTERED;
+ else if(!strcmp(s, "huffman only"))
+ web_gzip_strategy = Z_HUFFMAN_ONLY;
+ else if(!strcmp(s, "rle"))
+ web_gzip_strategy = Z_RLE;
+ else if(!strcmp(s, "fixed"))
+ web_gzip_strategy = Z_FIXED;
+ else {
+ error("Invalid compression strategy '%s'. Valid strategies are 'default', 'filtered', 'huffman only', 'rle' and 'fixed'. Proceeding with 'default'.", s);
+ web_gzip_strategy = Z_DEFAULT_STRATEGY;
+ }
+
+ web_gzip_level = (int)config_get_number("global", "web compression level", 3);
+ if(web_gzip_level < 1) {
+ error("Invalid compression level %d. Valid levels are 1 (fastest) to 9 (best ratio). Proceeding with level 1 (fastest compression).", web_gzip_level);
+ web_gzip_level = 1;
+ }
+ else if(web_gzip_level > 9) {
+ error("Invalid compression level %d. Valid levels are 1 (fastest) to 9 (best ratio). Proceeding with level 9 (best compression).", web_gzip_level);
+ web_gzip_level = 9;
+ }
+#endif /* NETDATA_WITH_ZLIB */
+}
+
+
int killpid(pid_t pid, int sig)
{
- int ret = -1;
- debug(D_EXIT, "Request to kill pid %d", pid);
-
- errno = 0;
- if(kill(pid, 0) == -1) {
- switch(errno) {
- case ESRCH:
- error("Request to kill pid %d, but it is not running.", pid);
- break;
-
- case EPERM:
- error("Request to kill pid %d, but I do not have enough permissions.", pid);
- break;
-
- default:
- error("Request to kill pid %d, but I received an error.", pid);
- break;
- }
- }
- else {
- errno = 0;
- ret = kill(pid, sig);
- if(ret == -1) {
- switch(errno) {
- case ESRCH:
- error("Cannot kill pid %d, but it is not running.", pid);
- break;
-
- case EPERM:
- error("Cannot kill pid %d, but I do not have enough permissions.", pid);
- break;
-
- default:
- error("Cannot kill pid %d, but I received an error.", pid);
- break;
- }
- }
- }
-
- return ret;
+ int ret = -1;
+ debug(D_EXIT, "Request to kill pid %d", pid);
+
+ errno = 0;
+ if(kill(pid, 0) == -1) {
+ switch(errno) {
+ case ESRCH:
+ error("Request to kill pid %d, but it is not running.", pid);
+ break;
+
+ case EPERM:
+ error("Request to kill pid %d, but I do not have enough permissions.", pid);
+ break;
+
+ default:
+ error("Request to kill pid %d, but I received an error.", pid);
+ break;
+ }
+ }
+ else {
+ errno = 0;
+ ret = kill(pid, sig);
+ if(ret == -1) {
+ switch(errno) {
+ case ESRCH:
+ error("Cannot kill pid %d, but it is not running.", pid);
+ break;
+
+ case EPERM:
+ error("Cannot kill pid %d, but I do not have enough permissions.", pid);
+ break;
+
+ default:
+ error("Cannot kill pid %d, but I received an error.", pid);
+ break;
+ }
+ }
+ }
+
+ return ret;
}
void kill_childs()
{
- siginfo_t info;
-
- struct web_client *w;
- for(w = web_clients; w ; w = w->next) {
- debug(D_EXIT, "Stopping web client %s", w->client_ip);
- pthread_cancel(w->thread);
- pthread_join(w->thread, NULL);
- }
-
- int i;
- for (i = 0; static_threads[i].name != NULL ; i++) {
- if(static_threads[i].thread) {
- debug(D_EXIT, "Stopping %s thread", static_threads[i].name);
- pthread_cancel(*static_threads[i].thread);
- pthread_join(*static_threads[i].thread, NULL);
- static_threads[i].thread = NULL;
- }
- }
-
- if(tc_child_pid) {
- info("Killing tc-qos-helper procees");
- if(killpid(tc_child_pid, SIGTERM) != -1)
- waitid(P_PID, (id_t) tc_child_pid, &info, WEXITED);
- }
- tc_child_pid = 0;
-
- struct plugind *cd;
- for(cd = pluginsd_root ; cd ; cd = cd->next) {
- debug(D_EXIT, "Stopping %s plugin thread", cd->id);
- pthread_cancel(cd->thread);
- pthread_join(cd->thread, NULL);
-
- if(cd->pid && !cd->obsolete) {
- debug(D_EXIT, "killing %s plugin process", cd->id);
- if(killpid(cd->pid, SIGTERM) != -1)
- waitid(P_PID, (id_t) cd->pid, &info, WEXITED);
- }
- }
-
- // if, for any reason there is any child exited
- // catch it here
- waitid(P_PID, 0, &info, WEXITED|WNOHANG);
-
- debug(D_EXIT, "All threads/childs stopped.");
+ siginfo_t info;
+
+ struct web_client *w;
+ for(w = web_clients; w ; w = w->next) {
+ debug(D_EXIT, "Stopping web client %s", w->client_ip);
+ pthread_cancel(w->thread);
+ pthread_join(w->thread, NULL);
+ }
+
+ int i;
+ for (i = 0; static_threads[i].name != NULL ; i++) {
+ if(static_threads[i].thread) {
+ debug(D_EXIT, "Stopping %s thread", static_threads[i].name);
+ pthread_cancel(*static_threads[i].thread);
+ pthread_join(*static_threads[i].thread, NULL);
+ static_threads[i].thread = NULL;
+ }
+ }
+
+ if(tc_child_pid) {
+ info("Killing tc-qos-helper procees");
+ if(killpid(tc_child_pid, SIGTERM) != -1)
+ waitid(P_PID, (id_t) tc_child_pid, &info, WEXITED);
+ }
+ tc_child_pid = 0;
+
+ struct plugind *cd;
+ for(cd = pluginsd_root ; cd ; cd = cd->next) {
+ debug(D_EXIT, "Stopping %s plugin thread", cd->id);
+ pthread_cancel(cd->thread);
+ pthread_join(cd->thread, NULL);
+
+ if(cd->pid && !cd->obsolete) {
+ debug(D_EXIT, "killing %s plugin process", cd->id);
+ if(killpid(cd->pid, SIGTERM) != -1)
+ waitid(P_PID, (id_t) cd->pid, &info, WEXITED);
+ }
+ }
+
+ // if, for any reason there is any child exited
+ // catch it here
+ waitid(P_PID, 0, &info, WEXITED|WNOHANG);
+
+ debug(D_EXIT, "All threads/childs stopped.");
}
+struct option_def options[] = {
+ // opt description arg name default value
+ {'c', "Load alternate configuration file", "config_file", CONFIG_DIR "/" CONFIG_FILENAME},
+ {'D', "Disable fork into background", NULL, NULL},
+ {'h', "Display help message", NULL, NULL},
+ {'P', "File to save a pid while running", "FILE", NULL},
+ {'i', "The IP address to listen to.", "address", "All addresses"},
+ {'k', "Check daemon configuration.", NULL, NULL},
+ {'p', "Port to listen. Can be from 1 to 65535.", "port_number", "19999"},
+ {'s', "Path to access host /proc and /sys when running in a container.", "PATH", NULL},
+ {'t', "The frequency in seconds, for data collection. \
+Same as 'update every' config file option.", "seconds", "1"},
+ {'u', "System username to run as.", "username", "netdata"},
+ {'v', "Version of the program", NULL, NULL},
+ {'W', "vendor options.", "stacksize=N|unittest|debug_flags=N", NULL},
+};
+
+void help(int exitcode) {
+ FILE *stream;
+ if(exitcode == 0)
+ stream = stdout;
+ else
+ stream = stderr;
+
+ int num_opts = sizeof(options) / sizeof(struct option_def);
+ int i;
+ int max_len_arg = 0;
+
+ // Compute maximum argument length
+ for( i = 0; i < num_opts; i++ ) {
+ if(options[i].arg_name) {
+ int len_arg = (int)strlen(options[i].arg_name);
+ if(len_arg > max_len_arg) max_len_arg = len_arg;
+ }
+ }
+
+ fprintf(stream, "SYNOPSIS: netdata [options]\n");
+ fprintf(stream, "\n");
+ fprintf(stream, "Options:\n");
+
+ // Output options description.
+ for( i = 0; i < num_opts; i++ ) {
+ fprintf(stream, " -%c %-*s %s", options[i].val, max_len_arg, options[i].arg_name ? options[i].arg_name : "", options[i].description);
+ if(options[i].default_value) {
+ fprintf(stream, " Default: %s\n", options[i].default_value);
+ } else {
+ fprintf(stream, "\n");
+ }
+ }
+
+ fflush(stream);
+ exit(exitcode);
+}
+
+// TODO: Remove this function with the nix major release.
+void remove_option(int opt_index, int *argc, char **argv) {
+ int i = opt_index;
+ // remove the options.
+ do {
+ *argc = *argc - 1;
+ for(i = opt_index; i < *argc; i++) {
+ argv[i] = argv[i+1];
+ }
+ i = opt_index;
+ } while(argv[i][0] != '-' && opt_index >= *argc);
+}
+
+static const char *verify_required_directory(const char *dir) {
+ if(chdir(dir) == -1)
+ fatal("Cannot cd to directory '%s'", dir);
+
+ DIR *d = opendir(dir);
+ if(!d)
+ fatal("Cannot examine the contents of directory '%s'", dir);
+ closedir(d);
+
+ return dir;
+}
int main(int argc, char **argv)
{
- int i;
- int config_loaded = 0;
- int dont_fork = 0;
- size_t wanted_stacksize = 0, stacksize = 0;
- pthread_attr_t attr;
-
- // global initialization
- get_HZ();
-
- // set the name for logging
- program_name = "netdata";
-
- // parse the arguments
- for(i = 1; i < argc ; i++) {
- if(strcmp(argv[i], "-c") == 0 && (i+1) < argc) {
- if(load_config(argv[i+1], 1) != 1) {
- error("Cannot load configuration file %s.", argv[i+1]);
- exit(1);
- }
- else {
- debug(D_OPTIONS, "Configuration loaded from %s.", argv[i+1]);
- config_loaded = 1;
- }
- i++;
- }
- else if(strcmp(argv[i], "-df") == 0 && (i+1) < argc) { config_set("global", "debug flags", argv[i+1]); debug_flags = strtoull(argv[i+1], NULL, 0); i++; }
- else if(strcmp(argv[i], "-p") == 0 && (i+1) < argc) { config_set("global", "port", argv[i+1]); i++; }
- else if(strcmp(argv[i], "-u") == 0 && (i+1) < argc) { config_set("global", "run as user", argv[i+1]); i++; }
- else if(strcmp(argv[i], "-l") == 0 && (i+1) < argc) { config_set("global", "history", argv[i+1]); i++; }
- else if(strcmp(argv[i], "-t") == 0 && (i+1) < argc) { config_set("global", "update every", argv[i+1]); i++; }
- else if(strcmp(argv[i], "-ch") == 0 && (i+1) < argc) { config_set("global", "host access prefix", argv[i+1]); i++; }
- else if(strcmp(argv[i], "-stacksize") == 0 && (i+1) < argc) { config_set("global", "pthread stack size", argv[i+1]); i++; }
- else if(strcmp(argv[i], "-nodaemon") == 0 || strcmp(argv[i], "-nd") == 0) dont_fork = 1;
- else if(strcmp(argv[i], "-pidfile") == 0 && (i+1) < argc) {
- i++;
- strncpyz(pidfile, argv[i], FILENAME_MAX);
- }
- else if(strcmp(argv[i], "--unittest") == 0) {
- rrd_update_every = 1;
- if(run_all_mockup_tests()) exit(1);
- if(unit_test_storage()) exit(1);
- fprintf(stderr, "\n\nALL TESTS PASSED\n\n");
- exit(0);
- }
- else {
- fprintf(stderr, "Cannot understand option '%s'.\n", argv[i]);
- fprintf(stderr, "\nUSAGE: %s [-d] [-l LINES_TO_SAVE] [-u UPDATE_TIMER] [-p LISTEN_PORT] [-df debug flags].\n\n", argv[0]);
- fprintf(stderr, " -c CONFIG FILE the configuration file to load. Default: %s.\n", CONFIG_DIR "/" CONFIG_FILENAME);
- fprintf(stderr, " -l LINES_TO_SAVE can be from 5 to %d lines in JSON data. Default: %d.\n", RRD_HISTORY_ENTRIES_MAX, RRD_DEFAULT_HISTORY_ENTRIES);
- fprintf(stderr, " -t UPDATE_TIMER can be from 1 to %d seconds. Default: %d.\n", UPDATE_EVERY_MAX, UPDATE_EVERY);
- fprintf(stderr, " -p LISTEN_PORT can be from 1 to %d. Default: %d.\n", 65535, LISTEN_PORT);
- fprintf(stderr, " -u USERNAME can be any system username to run as. Default: none.\n");
- fprintf(stderr, " -ch path to access host /proc and /sys when running in a container. Default: empty.\n");
- fprintf(stderr, " -nd or -nodeamon to disable forking in the background. Default: unset.\n");
- fprintf(stderr, " -df FLAGS debug options. Default: 0x%08llx.\n", debug_flags);
- fprintf(stderr, " -stacksize BYTES to overwrite the pthread stack size.\n");
- fprintf(stderr, " -pidfile FILENAME to save a pid while running.\n");
- exit(1);
- }
- }
-
- if(!config_loaded) load_config(NULL, 0);
-
- // prepare configuration environment variables for the plugins
- setenv("NETDATA_CONFIG_DIR" , config_get("global", "config directory" , CONFIG_DIR) , 1);
- setenv("NETDATA_PLUGINS_DIR", config_get("global", "plugins directory" , PLUGINS_DIR), 1);
- setenv("NETDATA_WEB_DIR" , config_get("global", "web files directory", WEB_DIR) , 1);
- setenv("NETDATA_CACHE_DIR" , config_get("global", "cache directory" , CACHE_DIR) , 1);
- setenv("NETDATA_LIB_DIR" , config_get("global", "lib directory" , VARLIB_DIR) , 1);
- setenv("NETDATA_LOG_DIR" , config_get("global", "log directory" , LOG_DIR) , 1);
- setenv("NETDATA_HOST_PREFIX", config_get("global", "host access prefix" , "") , 1);
- setenv("HOME" , config_get("global", "home directory" , CACHE_DIR) , 1);
-
- // avoid extended to stat(/etc/localtime)
- // http://stackoverflow.com/questions/4554271/how-to-avoid-excessive-stat-etc-localtime-calls-in-strftime-on-linux
- setenv("TZ", ":/etc/localtime", 0);
-
- // cd to /tmp to avoid any plugins writing files at random places
- if(chdir("/tmp")) error("netdata: ERROR: Cannot cd to /tmp");
-
- char *input_log_file = NULL;
- char *output_log_file = NULL;
- char *error_log_file = NULL;
- char *access_log_file = NULL;
- char *user = NULL;
- {
- char buffer[1024];
-
- // --------------------------------------------------------------------
-
- sprintf(buffer, "0x%08llx", 0ULL);
- char *flags = config_get("global", "debug flags", buffer);
- setenv("NETDATA_DEBUG_FLAGS", flags, 1);
-
- debug_flags = strtoull(flags, NULL, 0);
- debug(D_OPTIONS, "Debug flags set to '0x%8llx'.", debug_flags);
-
- if(debug_flags != 0) {
- struct rlimit rl = { RLIM_INFINITY, RLIM_INFINITY };
- if(setrlimit(RLIMIT_CORE, &rl) != 0)
- info("Cannot request unlimited core dumps for debugging... Proceeding anyway...");
- prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
- }
-
- // --------------------------------------------------------------------
+ int i, check_config = 0;
+ int config_loaded = 0;
+ int dont_fork = 0;
+ size_t wanted_stacksize = 0, stacksize = 0;
+ pthread_attr_t attr;
+
+ // global initialization
+ get_HZ();
+
+ // set the name for logging
+ program_name = "netdata";
+
+ // parse command line.
+
+ // parse depercated options
+ // TODO: Remove this block with the next major release.
+ {
+ i = 1;
+ while(i < argc) {
+ if(strcmp(argv[i], "-pidfile") == 0 && (i+1) < argc) {
+ strncpyz(pidfile, argv[i+1], FILENAME_MAX);
+ fprintf(stderr, "%s: deprecated option -- %s -- please use -P instead.\n", argv[0], argv[i]);
+ remove_option(i, &argc, argv);
+ }
+ else if(strcmp(argv[i], "-nodaemon") == 0 || strcmp(argv[i], "-nd") == 0) {
+ dont_fork = 1;
+ fprintf(stderr, "%s: deprecated option -- %s -- please use -D instead.\n ", argv[0], argv[i]);
+ remove_option(i, &argc, argv);
+ }
+ else if(strcmp(argv[i], "-ch") == 0 && (i+1) < argc) {
+ config_set("global", "host access prefix", argv[i+1]);
+ fprintf(stderr, "%s: deprecated option -- %s -- please use -s instead.\n", argv[0], argv[i]);
+ remove_option(i, &argc, argv);
+ }
+ else if(strcmp(argv[i], "-l") == 0 && (i+1) < argc) {
+ config_set("global", "history", argv[i+1]);
+ fprintf(stderr, "%s: deprecated option -- %s -- This option will be removed with V2.*.\n", argv[0], argv[i]);
+ remove_option(i, &argc, argv);
+ }
+ else i++;
+ }
+ }
+
+ // parse options
+ {
+ int num_opts = sizeof(options) / sizeof(struct option_def);
+ char optstring[(num_opts * 2) + 1];
+
+ int string_i = 0;
+ for( i = 0; i < num_opts; i++ ) {
+ optstring[string_i] = options[i].val;
+ string_i++;
+ if(options[i].arg_name) {
+ optstring[string_i] = ':';
+ string_i++;
+ }
+ }
+
+ int opt;
+ while( (opt = getopt(argc, argv, optstring)) != -1 ) {
+ switch(opt) {
+ case 'c':
+ if(load_config(optarg, 1) != 1) {
+ error("Cannot load configuration file %s.", optarg);
+ exit(1);
+ }
+ else {
+ debug(D_OPTIONS, "Configuration loaded from %s.", optarg);
+ config_loaded = 1;
+ }
+ break;
+ case 'D':
+ dont_fork = 1;
+ break;
+ case 'h':
+ help(0);
+ break;
+ case 'i':
+ config_set("global", "bind to", optarg);
+ break;
+ case 'k':
+ dont_fork = 1;
+ check_config = 1;
+ break;
+ case 'P':
+ strncpy(pidfile, optarg, FILENAME_MAX);
+ pidfile[FILENAME_MAX] = '\0';
+ break;
+ case 'p':
+ config_set("global", "default port", optarg);
+ break;
+ case 's':
+ config_set("global", "host access prefix", optarg);
+ break;
+ case 't':
+ config_set("global", "update every", optarg);
+ break;
+ case 'u':
+ config_set("global", "run as user", optarg);
+ break;
+ case 'v':
+ // TODO: Outsource version to makefile which can compute version from git.
+ printf("netdata 1.3.0\n");
+ return 0;
+ case 'W':
+ {
+ char* stacksize_string = "stacksize=";
+ char* debug_flags_string = "debug_flags=";
+ if(strcmp(optarg, "unittest") == 0) {
+ rrd_update_every = 1;
+ if(run_all_mockup_tests()) exit(1);
+ if(unit_test_storage()) exit(1);
+ fprintf(stderr, "\n\nALL TESTS PASSED\n\n");
+ exit(0);
+ } else if(strncmp(optarg, stacksize_string, strlen(stacksize_string)) == 0) {
+ optarg += strlen(stacksize_string);
+ config_set("global", "pthread stack size", optarg);
+ } else if(strncmp(optarg, debug_flags_string, strlen(debug_flags_string)) == 0) {
+ optarg += strlen(debug_flags_string);
+ config_set("global", "debug flags", optarg);
+ debug_flags = strtoull(optarg, NULL, 0);
+ }
+ }
+ break;
+ default: /* ? */
+ help(1);
+ break;
+ }
+ }
+ }
+
+ if(!config_loaded)
+ load_config(NULL, 0);
+
+ {
+ char *config_dir = config_get("global", "config directory", CONFIG_DIR);
+
+ // prepare configuration environment variables for the plugins
+ setenv("NETDATA_CONFIG_DIR" , verify_required_directory(config_dir) , 1);
+ setenv("NETDATA_PLUGINS_DIR", verify_required_directory(config_get("global", "plugins directory" , PLUGINS_DIR)), 1);
+ setenv("NETDATA_WEB_DIR" , verify_required_directory(config_get("global", "web files directory", WEB_DIR)) , 1);
+ setenv("NETDATA_CACHE_DIR" , verify_required_directory(config_get("global", "cache directory" , CACHE_DIR)) , 1);
+ setenv("NETDATA_LIB_DIR" , verify_required_directory(config_get("global", "lib directory" , VARLIB_DIR)) , 1);
+ setenv("NETDATA_LOG_DIR" , verify_required_directory(config_get("global", "log directory" , LOG_DIR)) , 1);
+
+ setenv("NETDATA_HOST_PREFIX", config_get("global", "host access prefix" , "") , 1);
+ setenv("HOME" , config_get("global", "home directory" , CACHE_DIR) , 1);
+
+ // disable buffering for python plugins
+ setenv("PYTHONUNBUFFERED", "1", 1);
+
+ // avoid flood calls to stat(/etc/localtime)
+ // http://stackoverflow.com/questions/4554271/how-to-avoid-excessive-stat-etc-localtime-calls-in-strftime-on-linux
+ setenv("TZ", ":/etc/localtime", 0);
+
+ // work while we are cd into config_dir
+ // to allow the plugins refer to their config
+ // files using relative filenames
+ if(chdir(config_dir) == -1)
+ fatal("Cannot cd to '%s'", config_dir);
+
+ char path[1024 + 1], *p = getenv("PATH");
+ if(!p) p = "/bin:/usr/bin";
+ snprintfz(path, 1024, "%s:%s", p, "/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin");
+ setenv("PATH", config_get("plugins", "PATH environment variable", path), 1);
+ }
+
+ char *user = NULL;
+ {
+ char *flags = config_get("global", "debug flags", "0x00000000");
+ setenv("NETDATA_DEBUG_FLAGS", flags, 1);
+
+ debug_flags = strtoull(flags, NULL, 0);
+ debug(D_OPTIONS, "Debug flags set to '0x%8llx'.", debug_flags);
+
+ if(debug_flags != 0) {
+ struct rlimit rl = { RLIM_INFINITY, RLIM_INFINITY };
+ if(setrlimit(RLIMIT_CORE, &rl) != 0)
+ info("Cannot request unlimited core dumps for debugging... Proceeding anyway...");
+ prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
+ }
+
+ // --------------------------------------------------------------------
#ifdef MADV_MERGEABLE
- enable_ksm = config_get_boolean("global", "memory deduplication (ksm)", enable_ksm);
+ enable_ksm = config_get_boolean("global", "memory deduplication (ksm)", enable_ksm);
#else
#warning "Kernel memory deduplication (KSM) is not available"
#endif
- // --------------------------------------------------------------------
-
-
- global_host_prefix = config_get("global", "host access prefix", "");
- setenv("NETDATA_HOST_PREFIX", global_host_prefix, 1);
-
- // --------------------------------------------------------------------
-
- output_log_file = config_get("global", "debug log", LOG_DIR "/debug.log");
- if(strcmp(output_log_file, "syslog") == 0) {
- output_log_syslog = 1;
- output_log_file = NULL;
- }
- else if(strcmp(output_log_file, "none") == 0) {
- output_log_syslog = 0;
- output_log_file = NULL;
- }
- else output_log_syslog = 0;
-
- // --------------------------------------------------------------------
-
- error_log_file = config_get("global", "error log", LOG_DIR "/error.log");
- if(strcmp(error_log_file, "syslog") == 0) {
- error_log_syslog = 1;
- error_log_file = NULL;
- }
- else if(strcmp(error_log_file, "none") == 0) {
- error_log_syslog = 0;
- error_log_file = NULL;
- // optimization - do not even generate debug log entries
- }
- else error_log_syslog = 0;
-
- error_log_throttle_period = config_get_number("global", "errors flood protection period", error_log_throttle_period);
- setenv("NETDATA_ERRORS_THROTTLE_PERIOD", config_get("global", "errors flood protection period" , ""), 1);
-
- error_log_errors_per_period = config_get_number("global", "errors to trigger flood protection", error_log_errors_per_period);
- setenv("NETDATA_ERRORS_PER_PERIOD" , config_get("global", "errors to trigger flood protection", ""), 1);
-
- // --------------------------------------------------------------------
-
- access_log_file = config_get("global", "access log", LOG_DIR "/access.log");
- if(strcmp(access_log_file, "syslog") == 0) {
- access_log_syslog = 1;
- access_log_file = NULL;
- }
- else if(strcmp(access_log_file, "none") == 0) {
- access_log_syslog = 0;
- access_log_file = NULL;
- }
- else access_log_syslog = 0;
-
- // --------------------------------------------------------------------
-
- rrd_memory_mode = rrd_memory_mode_id(config_get("global", "memory mode", rrd_memory_mode_name(rrd_memory_mode)));
-
- // --------------------------------------------------------------------
-
- if(gethostname(buffer, HOSTNAME_MAX) == -1)
- error("WARNING: Cannot get machine hostname.");
- hostname = config_get("global", "hostname", buffer);
- debug(D_OPTIONS, "hostname set to '%s'", hostname);
-
- // --------------------------------------------------------------------
-
- rrd_default_history_entries = (int) config_get_number("global", "history", RRD_DEFAULT_HISTORY_ENTRIES);
- if(rrd_default_history_entries < 5 || rrd_default_history_entries > RRD_HISTORY_ENTRIES_MAX) {
- info("Invalid save lines %d given. Defaulting to %d.", rrd_default_history_entries, RRD_DEFAULT_HISTORY_ENTRIES);
- rrd_default_history_entries = RRD_DEFAULT_HISTORY_ENTRIES;
- }
- else {
- debug(D_OPTIONS, "save lines set to %d.", rrd_default_history_entries);
- }
-
- // --------------------------------------------------------------------
-
- rrd_update_every = (int) config_get_number("global", "update every", UPDATE_EVERY);
- if(rrd_update_every < 1 || rrd_update_every > 600) {
- info("Invalid update timer %d given. Defaulting to %d.", rrd_update_every, UPDATE_EVERY_MAX);
- rrd_update_every = UPDATE_EVERY;
- }
- else debug(D_OPTIONS, "update timer set to %d.", rrd_update_every);
-
- // let the plugins know the min update_every
- {
- char buf[51];
- snprintfz(buf, 50, "%d", rrd_update_every);
- setenv("NETDATA_UPDATE_EVERY", buf, 1);
- }
-
- // --------------------------------------------------------------------
-
- // block signals while initializing threads.
- // this causes the threads to block signals.
- sigset_t sigset;
- sigfillset(&sigset);
-
- if(pthread_sigmask(SIG_BLOCK, &sigset, NULL) == -1) {
- error("Could not block signals for threads");
- }
-
- // Catch signals which we want to use to quit savely
- struct sigaction sa;
- sigemptyset(&sa.sa_mask);
- sigaddset(&sa.sa_mask, SIGHUP);
- sigaddset(&sa.sa_mask, SIGINT);
- sigaddset(&sa.sa_mask, SIGTERM);
- sa.sa_handler = sig_handler;
- sa.sa_flags = 0;
- if(sigaction(SIGHUP, &sa, NULL) == -1) {
- error("Failed to change signal handler for SIGHUP");
- }
- if(sigaction(SIGINT, &sa, NULL) == -1) {
- error("Failed to change signal handler for SIGINT");
- }
- if(sigaction(SIGTERM, &sa, NULL) == -1) {
- error("Failed to change signal handler for SIGTERM");
- }
- // Ignore SIGPIPE completely.
- // INFO: If we add signals here we have to unblock them
- // at popen.c when running a external plugin.
- sa.sa_handler = SIG_IGN;
- if(sigaction(SIGPIPE, &sa, NULL) == -1) {
- error("Failed to change signal handler for SIGTERM");
- }
-
- // --------------------------------------------------------------------
-
- i = pthread_attr_init(&attr);
- if(i != 0)
- fatal("pthread_attr_init() failed with code %d.", i);
-
- i = pthread_attr_getstacksize(&attr, &stacksize);
- if(i != 0)
- fatal("pthread_attr_getstacksize() failed with code %d.", i);
- else
- debug(D_OPTIONS, "initial pthread stack size is %zu bytes", stacksize);
-
- wanted_stacksize = config_get_number("global", "pthread stack size", stacksize);
-
- // --------------------------------------------------------------------
-
- for (i = 0; static_threads[i].name != NULL ; i++) {
- struct netdata_static_thread *st = &static_threads[i];
-
- if(st->config_name) st->enabled = config_get_boolean(st->config_section, st->config_name, st->enabled);
- if(st->enabled && st->init_routine) st->init_routine();
- }
-
- // --------------------------------------------------------------------
-
- // get the user we should run
- // IMPORTANT: this is required before web_files_uid()
- user = config_get("global", "run as user" , (getuid() == 0)?NETDATA_USER:"");
-
- // IMPORTANT: these have to run once, while single threaded
- web_files_uid(); // IMPORTANT: web_files_uid() before web_files_gid()
- web_files_gid();
-
- // --------------------------------------------------------------------
-
- listen_backlog = (int) config_get_number("global", "http port listen backlog", LISTEN_BACKLOG);
-
- listen_port = (int) config_get_number("global", "port", LISTEN_PORT);
- if(listen_port < 1 || listen_port > 65535) {
- info("Invalid listen port %d given. Defaulting to %d.", listen_port, LISTEN_PORT);
- listen_port = LISTEN_PORT;
- }
- else debug(D_OPTIONS, "Listen port set to %d.", listen_port);
-
- int ip = 0;
- char *ipv = config_get("global", "ip version", "any");
- if(!strcmp(ipv, "any") || !strcmp(ipv, "both") || !strcmp(ipv, "all")) ip = 0;
- else if(!strcmp(ipv, "ipv4") || !strcmp(ipv, "IPV4") || !strcmp(ipv, "IPv4") || !strcmp(ipv, "4")) ip = 4;
- else if(!strcmp(ipv, "ipv6") || !strcmp(ipv, "IPV6") || !strcmp(ipv, "IPv6") || !strcmp(ipv, "6")) ip = 6;
- else info("Cannot understand ip version '%s'. Assuming 'any'.", ipv);
-
- if(ip == 0 || ip == 6) listen_fd = create_listen_socket6(config_get("global", "bind socket to IP", "*"), listen_port, listen_backlog);
- if(listen_fd < 0) {
- listen_fd = create_listen_socket4(config_get("global", "bind socket to IP", "*"), listen_port, listen_backlog);
- if(listen_fd >= 0 && ip != 4) info("Managed to open an IPv4 socket on port %d.", listen_port);
- }
-
- if(listen_fd < 0) fatal("Cannot listen socket.");
- }
-
- // never become a problem
- if(nice(20) == -1) error("Cannot lower my CPU priority.");
-
- if(become_daemon(dont_fork, 0, user, input_log_file, output_log_file, error_log_file, access_log_file, &access_fd, &stdaccess) == -1)
- fatal("Cannot demonize myself.");
+ // --------------------------------------------------------------------
+
+ global_host_prefix = config_get("global", "host access prefix", "");
+ setenv("NETDATA_HOST_PREFIX", global_host_prefix, 1);
+
+ // --------------------------------------------------------------------
+
+ stdout_filename = config_get("global", "debug log", LOG_DIR "/debug.log");
+ stderr_filename = config_get("global", "error log", LOG_DIR "/error.log");
+ stdaccess_filename = config_get("global", "access log", LOG_DIR "/access.log");
+
+ error_log_throttle_period = config_get_number("global", "errors flood protection period", error_log_throttle_period);
+ setenv("NETDATA_ERRORS_THROTTLE_PERIOD", config_get("global", "errors flood protection period" , ""), 1);
+
+ error_log_errors_per_period = (unsigned long)config_get_number("global", "errors to trigger flood protection", error_log_errors_per_period);
+ setenv("NETDATA_ERRORS_PER_PERIOD" , config_get("global", "errors to trigger flood protection", ""), 1);
+
+ if(check_config) {
+ stdout_filename = stderr_filename = stdaccess_filename = "system";
+ error_log_throttle_period = 0;
+ error_log_errors_per_period = 0;
+ }
+
+ // --------------------------------------------------------------------
+
+ rrd_memory_mode = rrd_memory_mode_id(config_get("global", "memory mode", rrd_memory_mode_name(rrd_memory_mode)));
+
+ // --------------------------------------------------------------------
+
+ {
+ char hostnamebuf[HOSTNAME_MAX + 1];
+ if(gethostname(hostnamebuf, HOSTNAME_MAX) == -1)
+ error("WARNING: Cannot get machine hostname.");
+ hostname = config_get("global", "hostname", hostnamebuf);
+ debug(D_OPTIONS, "hostname set to '%s'", hostname);
+ setenv("NETDATA_HOSTNAME", hostname, 1);
+ }
+
+ // --------------------------------------------------------------------
+
+ rrd_default_history_entries = (int) config_get_number("global", "history", RRD_DEFAULT_HISTORY_ENTRIES);
+ if(rrd_default_history_entries < 5 || rrd_default_history_entries > RRD_HISTORY_ENTRIES_MAX) {
+ info("Invalid save lines %d given. Defaulting to %d.", rrd_default_history_entries, RRD_DEFAULT_HISTORY_ENTRIES);
+ rrd_default_history_entries = RRD_DEFAULT_HISTORY_ENTRIES;
+ }
+ else {
+ debug(D_OPTIONS, "save lines set to %d.", rrd_default_history_entries);
+ }
+
+ // --------------------------------------------------------------------
+
+ rrd_update_every = (int) config_get_number("global", "update every", UPDATE_EVERY);
+ if(rrd_update_every < 1 || rrd_update_every > 600) {
+ info("Invalid update timer %d given. Defaulting to %d.", rrd_update_every, UPDATE_EVERY_MAX);
+ rrd_update_every = UPDATE_EVERY;
+ }
+ else debug(D_OPTIONS, "update timer set to %d.", rrd_update_every);
+
+ // let the plugins know the min update_every
+ {
+ char buf[16];
+ snprintfz(buf, 15, "%d", rrd_update_every);
+ setenv("NETDATA_UPDATE_EVERY", buf, 1);
+ }
+
+ // --------------------------------------------------------------------
+
+ // block signals while initializing threads.
+ // this causes the threads to block signals.
+ sigset_t sigset;
+ sigfillset(&sigset);
+ if(pthread_sigmask(SIG_BLOCK, &sigset, NULL) == -1)
+ error("Could not block signals for threads");
+
+ // Catch signals which we want to use
+ struct sigaction sa;
+ sa.sa_flags = 0;
+
+ // ingore all signals while we run in a signal handler
+ sigfillset(&sa.sa_mask);
+
+ // INFO: If we add signals here we have to unblock them
+ // at popen.c when running a external plugin.
+
+ // Ignore SIGPIPE completely.
+ sa.sa_handler = SIG_IGN;
+ if(sigaction(SIGPIPE, &sa, NULL) == -1)
+ error("Failed to change signal handler for SIGPIPE");
+
+ sa.sa_handler = sig_handler_exit;
+ if(sigaction(SIGINT, &sa, NULL) == -1)
+ error("Failed to change signal handler for SIGINT");
+
+ sa.sa_handler = sig_handler_exit;
+ if(sigaction(SIGTERM, &sa, NULL) == -1)
+ error("Failed to change signal handler for SIGTERM");
+
+ sa.sa_handler = sig_handler_logrotate;
+ if(sigaction(SIGHUP, &sa, NULL) == -1)
+ error("Failed to change signal handler for SIGHUP");
+
+ // save database on SIGUSR1
+ sa.sa_handler = sig_handler_save;
+ if(sigaction(SIGUSR1, &sa, NULL) == -1)
+ error("Failed to change signal handler for SIGUSR1");
+
+ // reload health configuration on SIGUSR2
+ sa.sa_handler = sig_handler_reload_health;
+ if(sigaction(SIGUSR2, &sa, NULL) == -1)
+ error("Failed to change signal handler for SIGUSR2");
+
+ // --------------------------------------------------------------------
+
+ i = pthread_attr_init(&attr);
+ if(i != 0)
+ fatal("pthread_attr_init() failed with code %d.", i);
+
+ i = pthread_attr_getstacksize(&attr, &stacksize);
+ if(i != 0)
+ fatal("pthread_attr_getstacksize() failed with code %d.", i);
+ else
+ debug(D_OPTIONS, "initial pthread stack size is %zu bytes", stacksize);
+
+ wanted_stacksize = (size_t)config_get_number("global", "pthread stack size", (long)stacksize);
+
+ // --------------------------------------------------------------------
+
+ for (i = 0; static_threads[i].name != NULL ; i++) {
+ struct netdata_static_thread *st = &static_threads[i];
+
+ if(st->config_name) st->enabled = config_get_boolean(st->config_section, st->config_name, st->enabled);
+ if(st->enabled && st->init_routine) st->init_routine();
+ }
+
+ // --------------------------------------------------------------------
+
+ // get the user we should run
+ // IMPORTANT: this is required before web_files_uid()
+ user = config_get("global", "run as user" , (getuid() == 0)?NETDATA_USER:"");
+
+ // IMPORTANT: these have to run once, while single threaded
+ web_files_uid(); // IMPORTANT: web_files_uid() before web_files_gid()
+ web_files_gid();
+
+ // --------------------------------------------------------------------
+
+ if(!check_config)
+ create_listen_sockets();
+ }
+
+ // initialize the log files
+ open_all_log_files();
#ifdef NETDATA_INTERNAL_CHECKS
- if(debug_flags != 0) {
- struct rlimit rl = { RLIM_INFINITY, RLIM_INFINITY };
- if(setrlimit(RLIMIT_CORE, &rl) != 0)
- info("Cannot request unlimited core dumps for debugging... Proceeding anyway...");
- prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
- }
+ if(debug_flags != 0) {
+ struct rlimit rl = { RLIM_INFINITY, RLIM_INFINITY };
+ if(setrlimit(RLIMIT_CORE, &rl) != 0)
+ info("Cannot request unlimited core dumps for debugging... Proceeding anyway...");
+ prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
+ }
#endif /* NETDATA_INTERNAL_CHECKS */
- if(output_log_syslog || error_log_syslog || access_log_syslog)
- openlog("netdata", LOG_PID, LOG_DAEMON);
+ // fork, switch user, create pid file, set process priority
+ if(become_daemon(dont_fork, user) == -1)
+ fatal("Cannot demonize myself.");
+
+ info("NetData started on pid %d", getpid());
+
+
+ // ------------------------------------------------------------------------
+ // get default pthread stack size
+
+ if(stacksize < wanted_stacksize) {
+ i = pthread_attr_setstacksize(&attr, wanted_stacksize);
+ if(i != 0)
+ fatal("pthread_attr_setstacksize() to %zu bytes, failed with code %d.", wanted_stacksize, i);
+ else
+ info("Successfully set pthread stacksize to %zu bytes", wanted_stacksize);
+ }
- info("NetData started on pid %d", getpid());
+ // ------------------------------------------------------------------------
+ // initialize the registry
+ registry_init();
- // ------------------------------------------------------------------------
- // get default pthread stack size
+ // ------------------------------------------------------------------------
+ // initialize health monitoring
- if(stacksize < wanted_stacksize) {
- i = pthread_attr_setstacksize(&attr, wanted_stacksize);
- if(i != 0)
- fatal("pthread_attr_setstacksize() to %zu bytes, failed with code %d.", wanted_stacksize, i);
- else
- info("Successfully set pthread stacksize to %zu bytes", wanted_stacksize);
- }
+ health_init();
- // --------------------------------------------------------------------
- // initialize the registry
+ if(check_config)
+ exit(1);
- registry_init();
+ // ------------------------------------------------------------------------
+ // spawn the threads
- // ------------------------------------------------------------------------
- // spawn the threads
+ web_server_threading_selection();
- for (i = 0; static_threads[i].name != NULL ; i++) {
- struct netdata_static_thread *st = &static_threads[i];
+ for (i = 0; static_threads[i].name != NULL ; i++) {
+ struct netdata_static_thread *st = &static_threads[i];
- if(st->enabled) {
- st->thread = malloc(sizeof(pthread_t));
- if(!st->thread)
- fatal("Cannot allocate pthread_t memory");
+ if(st->enabled) {
+ st->thread = mallocz(sizeof(pthread_t));
- info("Starting thread %s.", st->name);
+ info("Starting thread %s.", st->name);
- if(pthread_create(st->thread, &attr, st->start_routine, NULL))
- error("failed to create new thread for %s.", st->name);
+ if(pthread_create(st->thread, &attr, st->start_routine, NULL))
+ error("failed to create new thread for %s.", st->name);
- else if(pthread_detach(*st->thread))
- error("Cannot request detach of newly created %s thread.", st->name);
- }
- else info("Not starting thread %s.", st->name);
- }
+ else if(pthread_detach(*st->thread))
+ error("Cannot request detach of newly created %s thread.", st->name);
+ }
+ else info("Not starting thread %s.", st->name);
+ }
- // ------------------------------------------------------------------------
- // block signals while initializing threads.
- sigset_t sigset;
- sigfillset(&sigset);
+ // ------------------------------------------------------------------------
+ // block signals while initializing threads.
+ sigset_t sigset;
+ sigfillset(&sigset);
- if(pthread_sigmask(SIG_UNBLOCK, &sigset, NULL) == -1) {
- error("Could not unblock signals for threads");
- }
+ if(pthread_sigmask(SIG_UNBLOCK, &sigset, NULL) == -1) {
+ error("Could not unblock signals for threads");
+ }
- // Handle flags set in the signal handler.
- while(1) {
- pause();
- if(netdata_exit) {
- info("Exit main loop of netdata.");
- netdata_cleanup_and_exit(0);
- exit(0);
- }
- }
+ // Handle flags set in the signal handler.
+ while(1) {
+ pause();
+ if(netdata_exit) {
+ info("Exit main loop of netdata.");
+ netdata_cleanup_and_exit(0);
+ exit(0);
+ }
+ }
}
diff --git a/src/main.h b/src/main.h
index d9edda58e..646827fbd 100644
--- a/src/main.h
+++ b/src/main.h
@@ -1,12 +1,31 @@
#ifndef NETDATA_MAIN_H
#define NETDATA_MAIN_H 1
-#include <signal.h>
-
extern volatile sig_atomic_t netdata_exit;
+/**
+ * This struct contains information about command line options.
+ */
+struct option_def {
+ /** The option character */
+ const char val;
+ /** The name of the long option. */
+ const char *description;
+ /** Short descripton what the option does */
+ /** Name of the argument displayed in SYNOPSIS */
+ const char *arg_name;
+ /** Default value if not set */
+ const char *default_value;
+};
+
+/**
+ * List of command line options.
+ * This can be used to compute manpage, help messages, ect.
+ */
+extern struct option_def options[];
+
extern void kill_childs(void);
extern int killpid(pid_t pid, int signal);
-extern void netdata_cleanup_and_exit(int ret);
+extern void netdata_cleanup_and_exit(int ret) __attribute__ ((noreturn));
#endif /* NETDATA_MAIN_H */
diff --git a/src/plugin_checks.c b/src/plugin_checks.c
index 379fb9a84..007d6565f 100644
--- a/src/plugin_checks.c
+++ b/src/plugin_checks.c
@@ -1,97 +1,84 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <pthread.h>
-#include <sys/time.h>
-#include <sys/resource.h>
-#include <strings.h>
-#include <unistd.h>
-
#include "common.h"
-#include "appconfig.h"
-#include "log.h"
-#include "rrd.h"
-#include "plugin_checks.h"
void *checks_main(void *ptr)
{
- if(ptr) { ; }
+ if(ptr) { ; }
- info("CHECKS thread created with task id %d", gettid());
+ info("CHECKS thread created with task id %d", gettid());
- if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
- error("Cannot set pthread cancel type to DEFERRED.");
+ if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
+ error("Cannot set pthread cancel type to DEFERRED.");
- if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
- error("Cannot set pthread cancel state to ENABLE.");
+ if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
+ error("Cannot set pthread cancel state to ENABLE.");
- unsigned long long usec = 0, susec = rrd_update_every * 1000000ULL, loop_usec = 0, total_susec = 0;
- struct timeval now, last, loop;
+ unsigned long long usec = 0, susec = rrd_update_every * 1000000ULL, loop_usec = 0, total_susec = 0;
+ struct timeval now, last, loop;
- RRDSET *check1, *check2, *check3, *apps_cpu = NULL;
+ RRDSET *check1, *check2, *check3, *apps_cpu = NULL;
- check1 = rrdset_create("netdata", "check1", NULL, "netdata", NULL, "Caller gives microseconds", "a million !", 99999, rrd_update_every, RRDSET_TYPE_LINE);
- rrddim_add(check1, "absolute", NULL, -1, 1, RRDDIM_ABSOLUTE);
- rrddim_add(check1, "incremental", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ check1 = rrdset_create("netdata", "check1", NULL, "netdata", NULL, "Caller gives microseconds", "a million !", 99999, rrd_update_every, RRDSET_TYPE_LINE);
+ rrddim_add(check1, "absolute", NULL, -1, 1, RRDDIM_ABSOLUTE);
+ rrddim_add(check1, "incremental", NULL, 1, 1, RRDDIM_INCREMENTAL);
- check2 = rrdset_create("netdata", "check2", NULL, "netdata", NULL, "Netdata calcs microseconds", "a million !", 99999, rrd_update_every, RRDSET_TYPE_LINE);
- rrddim_add(check2, "absolute", NULL, -1, 1, RRDDIM_ABSOLUTE);
- rrddim_add(check2, "incremental", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ check2 = rrdset_create("netdata", "check2", NULL, "netdata", NULL, "Netdata calcs microseconds", "a million !", 99999, rrd_update_every, RRDSET_TYPE_LINE);
+ rrddim_add(check2, "absolute", NULL, -1, 1, RRDDIM_ABSOLUTE);
+ rrddim_add(check2, "incremental", NULL, 1, 1, RRDDIM_INCREMENTAL);
- check3 = rrdset_create("netdata", "checkdt", NULL, "netdata", NULL, "Clock difference", "microseconds diff", 99999, rrd_update_every, RRDSET_TYPE_LINE);
- rrddim_add(check3, "caller", NULL, 1, 1, RRDDIM_ABSOLUTE);
- rrddim_add(check3, "netdata", NULL, 1, 1, RRDDIM_ABSOLUTE);
- rrddim_add(check3, "apps.plugin", NULL, 1, 1, RRDDIM_ABSOLUTE);
+ check3 = rrdset_create("netdata", "checkdt", NULL, "netdata", NULL, "Clock difference", "microseconds diff", 99999, rrd_update_every, RRDSET_TYPE_LINE);
+ rrddim_add(check3, "caller", NULL, 1, 1, RRDDIM_ABSOLUTE);
+ rrddim_add(check3, "netdata", NULL, 1, 1, RRDDIM_ABSOLUTE);
+ rrddim_add(check3, "apps.plugin", NULL, 1, 1, RRDDIM_ABSOLUTE);
- gettimeofday(&last, NULL);
- while(1) {
- usleep(susec);
+ gettimeofday(&last, NULL);
+ while(1) {
+ usleep(susec);
- // find the time to sleep in order to wait exactly update_every seconds
- gettimeofday(&now, NULL);
- loop_usec = usecdiff(&now, &last);
- usec = loop_usec - susec;
- debug(D_PROCNETDEV_LOOP, "CHECK: last loop took %llu usec (worked for %llu, sleeped for %llu).", loop_usec, usec, susec);
+ // find the time to sleep in order to wait exactly update_every seconds
+ gettimeofday(&now, NULL);
+ loop_usec = usec_dt(&now, &last);
+ usec = loop_usec - susec;
+ debug(D_PROCNETDEV_LOOP, "CHECK: last loop took %llu usec (worked for %llu, sleeped for %llu).", loop_usec, usec, susec);
- if(usec < (rrd_update_every * 1000000ULL / 2ULL)) susec = (rrd_update_every * 1000000ULL) - usec;
- else susec = rrd_update_every * 1000000ULL / 2ULL;
+ if(usec < (rrd_update_every * 1000000ULL / 2ULL)) susec = (rrd_update_every * 1000000ULL) - usec;
+ else susec = rrd_update_every * 1000000ULL / 2ULL;
- // --------------------------------------------------------------------
- // Calculate loop time
+ // --------------------------------------------------------------------
+ // Calculate loop time
- last.tv_sec = now.tv_sec;
- last.tv_usec = now.tv_usec;
- total_susec += loop_usec;
+ last.tv_sec = now.tv_sec;
+ last.tv_usec = now.tv_usec;
+ total_susec += loop_usec;
- // --------------------------------------------------------------------
- // check chart 1
+ // --------------------------------------------------------------------
+ // check chart 1
- if(check1->counter_done) rrdset_next_usec(check1, loop_usec);
- rrddim_set(check1, "absolute", 1000000);
- rrddim_set(check1, "incremental", total_susec);
- rrdset_done(check1);
+ if(check1->counter_done) rrdset_next_usec(check1, loop_usec);
+ rrddim_set(check1, "absolute", 1000000);
+ rrddim_set(check1, "incremental", total_susec);
+ rrdset_done(check1);
- // --------------------------------------------------------------------
- // check chart 2
+ // --------------------------------------------------------------------
+ // check chart 2
- if(check2->counter_done) rrdset_next(check2);
- rrddim_set(check2, "absolute", 1000000);
- rrddim_set(check2, "incremental", total_susec);
- rrdset_done(check2);
+ if(check2->counter_done) rrdset_next(check2);
+ rrddim_set(check2, "absolute", 1000000);
+ rrddim_set(check2, "incremental", total_susec);
+ rrdset_done(check2);
- // --------------------------------------------------------------------
- // check chart 3
+ // --------------------------------------------------------------------
+ // check chart 3
- if(!apps_cpu) apps_cpu = rrdset_find("apps.cpu");
- if(check3->counter_done) rrdset_next_usec(check3, loop_usec);
- gettimeofday(&loop, NULL);
- rrddim_set(check3, "caller", (long long)usecdiff(&loop, &check1->last_collected_time));
- rrddim_set(check3, "netdata", (long long)usecdiff(&loop, &check2->last_collected_time));
- if(apps_cpu) rrddim_set(check3, "apps.plugin", (long long)usecdiff(&loop, &apps_cpu->last_collected_time));
- rrdset_done(check3);
- }
+ if(!apps_cpu) apps_cpu = rrdset_find("apps.cpu");
+ if(check3->counter_done) rrdset_next_usec(check3, loop_usec);
+ gettimeofday(&loop, NULL);
+ rrddim_set(check3, "caller", (long long) usec_dt(&loop, &check1->last_collected_time));
+ rrddim_set(check3, "netdata", (long long) usec_dt(&loop, &check2->last_collected_time));
+ if(apps_cpu) rrddim_set(check3, "apps.plugin", (long long) usec_dt(&loop, &apps_cpu->last_collected_time));
+ rrdset_done(check3);
+ }
- pthread_exit(NULL);
- return NULL;
+ pthread_exit(NULL);
+ return NULL;
}
diff --git a/src/plugin_idlejitter.c b/src/plugin_idlejitter.c
index 56c22a160..30c6d8708 100644
--- a/src/plugin_idlejitter.c
+++ b/src/plugin_idlejitter.c
@@ -1,68 +1,54 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <pthread.h>
-#include <sys/time.h>
-#include <sys/resource.h>
-#include <strings.h>
-#include <unistd.h>
-
-#include "global_statistics.h"
#include "common.h"
-#include "appconfig.h"
-#include "log.h"
-#include "rrd.h"
-#include "plugin_idlejitter.h"
#define CPU_IDLEJITTER_SLEEP_TIME_MS 20
void *cpuidlejitter_main(void *ptr)
{
- if(ptr) { ; }
+ if(ptr) { ; }
- info("CPU Idle Jitter thread created with task id %d", gettid());
+ info("CPU Idle Jitter thread created with task id %d", gettid());
- if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
- error("Cannot set pthread cancel type to DEFERRED.");
+ if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
+ error("Cannot set pthread cancel type to DEFERRED.");
- if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
- error("Cannot set pthread cancel state to ENABLE.");
+ if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
+ error("Cannot set pthread cancel state to ENABLE.");
- int sleep_ms = (int) config_get_number("plugin:idlejitter", "loop time in ms", CPU_IDLEJITTER_SLEEP_TIME_MS);
- if(sleep_ms <= 0) {
- config_set_number("plugin:idlejitter", "loop time in ms", CPU_IDLEJITTER_SLEEP_TIME_MS);
- sleep_ms = CPU_IDLEJITTER_SLEEP_TIME_MS;
- }
+ int sleep_ms = (int) config_get_number("plugin:idlejitter", "loop time in ms", CPU_IDLEJITTER_SLEEP_TIME_MS);
+ if(sleep_ms <= 0) {
+ config_set_number("plugin:idlejitter", "loop time in ms", CPU_IDLEJITTER_SLEEP_TIME_MS);
+ sleep_ms = CPU_IDLEJITTER_SLEEP_TIME_MS;
+ }
- RRDSET *st = rrdset_find("system.idlejitter");
- if(!st) {
- st = rrdset_create("system", "idlejitter", NULL, "processes", NULL, "CPU Idle Jitter", "microseconds lost/s", 9999, rrd_update_every, RRDSET_TYPE_LINE);
- rrddim_add(st, "jitter", NULL, 1, 1, RRDDIM_ABSOLUTE);
- }
+ RRDSET *st = rrdset_find("system.idlejitter");
+ if(!st) {
+ st = rrdset_create("system", "idlejitter", NULL, "processes", NULL, "CPU Idle Jitter", "microseconds lost/s", 9999, rrd_update_every, RRDSET_TYPE_LINE);
+ rrddim_add(st, "jitter", NULL, 1, 1, RRDDIM_ABSOLUTE);
+ }
- struct timeval before, after;
- unsigned long long counter;
- for(counter = 0; 1 ;counter++) {
- unsigned long long usec = 0, susec = 0;
+ struct timeval before, after;
+ unsigned long long counter;
+ for(counter = 0; 1 ;counter++) {
+ unsigned long long usec = 0, susec = 0;
- while(susec < (rrd_update_every * 1000000ULL)) {
+ while(susec < (rrd_update_every * 1000000ULL)) {
- gettimeofday(&before, NULL);
- usleep(sleep_ms * 1000);
- gettimeofday(&after, NULL);
+ gettimeofday(&before, NULL);
+ usleep(sleep_ms * 1000);
+ gettimeofday(&after, NULL);
- // calculate the time it took for a full loop
- usec = usecdiff(&after, &before);
- susec += usec;
- }
- usec -= (sleep_ms * 1000);
+ // calculate the time it took for a full loop
+ usec = usec_dt(&after, &before);
+ susec += usec;
+ }
+ usec -= (sleep_ms * 1000);
- if(counter) rrdset_next(st);
- rrddim_set(st, "jitter", usec);
- rrdset_done(st);
- }
+ if(counter) rrdset_next(st);
+ rrddim_set(st, "jitter", usec);
+ rrdset_done(st);
+ }
- pthread_exit(NULL);
- return NULL;
+ pthread_exit(NULL);
+ return NULL;
}
diff --git a/src/plugin_nfacct.c b/src/plugin_nfacct.c
index 6cde66e0c..b2396fac1 100644
--- a/src/plugin_nfacct.c
+++ b/src/plugin_nfacct.c
@@ -1,222 +1,202 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#ifdef INTERNAL_PLUGIN_NFACCT
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <dirent.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <time.h>
-#include <errno.h>
+#include "common.h"
+#ifdef INTERNAL_PLUGIN_NFACCT
#include <libmnl/libmnl.h>
#include <libnetfilter_acct/libnetfilter_acct.h>
-#include "main.h"
-#include "global_statistics.h"
-#include "common.h"
-#include "appconfig.h"
-#include "log.h"
-#include "rrd.h"
-#include "plugin_proc.h"
-
struct mynfacct {
- const char *name;
- uint64_t pkts;
- uint64_t bytes;
- struct nfacct *nfacct;
+ const char *name;
+ uint64_t pkts;
+ uint64_t bytes;
+ struct nfacct *nfacct;
};
struct nfacct_list {
- int size;
- int len;
- struct mynfacct data[];
+ int size;
+ int len;
+ struct mynfacct data[];
} *nfacct_list = NULL;
static int nfacct_callback(const struct nlmsghdr *nlh, void *data) {
- if(data) {};
-
- 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++;
-
- info("nfacct.plugin: increasing nfacct_list to size %d", size);
-
- nfacct_list = realloc(nfacct_list, sizeof(struct nfacct_list) + (sizeof(struct mynfacct) * size));
- if(!nfacct_list) {
- error("nfacct.plugin: cannot allocate nfacct_list.");
- return MNL_CB_OK;
- }
-
- 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;
- }
-
- nfacct_list->size = size;
- nfacct_list->len = len;
- }
-
- if(nfacct_nlmsg_parse_payload(nlh, nfacct_list->data[nfacct_list->len].nfacct) < 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);
-
- nfacct_list->len++;
- return MNL_CB_OK;
+ if(data) {};
+
+ 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++;
+
+ info("nfacct.plugin: increasing nfacct_list to size %d", size);
+
+ nfacct_list = realloc(nfacct_list, sizeof(struct nfacct_list) + (sizeof(struct mynfacct) * size));
+ if(!nfacct_list) {
+ error("nfacct.plugin: cannot allocate nfacct_list.");
+ return MNL_CB_OK;
+ }
+
+ 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;
+ }
+
+ nfacct_list->size = size;
+ nfacct_list->len = len;
+ }
+
+ if(nfacct_nlmsg_parse_payload(nlh, nfacct_list->data[nfacct_list->len].nfacct) < 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);
+
+ nfacct_list->len++;
+ return MNL_CB_OK;
}
void *nfacct_main(void *ptr) {
- if(ptr) { ; }
+ if(ptr) { ; }
- info("NFACCT thread created with task id %d", gettid());
+ info("NFACCT thread created with task id %d", gettid());
- if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
- error("nfacct.plugin: Cannot set pthread cancel type to DEFERRED.");
+ if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
+ error("nfacct.plugin: Cannot set pthread cancel type to DEFERRED.");
- if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
- error("nfacct.plugin: Cannot set pthread cancel state to ENABLE.");
+ if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
+ error("nfacct.plugin: Cannot set pthread cancel state to ENABLE.");
- char buf[MNL_SOCKET_BUFFER_SIZE];
- struct mnl_socket *nl = NULL;
- struct nlmsghdr *nlh = NULL;
- unsigned int seq = 0, portid = 0;
+ 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 = time(NULL) - 1;
- nl = mnl_socket_open(NETLINK_NETFILTER);
- if(!nl) {
- error("nfacct.plugin: mnl_socket_open() failed");
- pthread_exit(NULL);
- return NULL;
- }
+ nl = mnl_socket_open(NETLINK_NETFILTER);
+ if(!nl) {
+ error("nfacct.plugin: mnl_socket_open() failed");
+ pthread_exit(NULL);
+ return NULL;
+ }
- 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;
- }
- portid = mnl_socket_get_portid(nl);
+ 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;
+ }
+ portid = mnl_socket_get_portid(nl);
- // ------------------------------------------------------------------------
+ // ------------------------------------------------------------------------
- struct timeval last, now;
- unsigned long long usec = 0, susec = 0;
- RRDSET *st = NULL;
+ struct timeval last, now;
+ unsigned long long usec = 0, susec = 0;
+ RRDSET *st = NULL;
- gettimeofday(&last, NULL);
+ gettimeofday(&last, NULL);
- // ------------------------------------------------------------------------
+ // ------------------------------------------------------------------------
- while(1) {
- if(unlikely(netdata_exit)) break;
+ while(1) {
+ if(unlikely(netdata_exit)) break;
- seq++;
+ seq++;
- nlh = nfacct_nlmsg_build_hdr(buf, NFNL_MSG_ACCT_GET, NLM_F_DUMP, seq);
- if(!nlh) {
- mnl_socket_close(nl);
- error("nfacct.plugin: nfacct_nlmsg_build_hdr() failed");
- pthread_exit(NULL);
- return NULL;
- }
+ nlh = nfacct_nlmsg_build_hdr(buf, NFNL_MSG_ACCT_GET, NLM_F_DUMP, seq);
+ if(!nlh) {
+ mnl_socket_close(nl);
+ error("nfacct.plugin: nfacct_nlmsg_build_hdr() failed");
+ pthread_exit(NULL);
+ return NULL;
+ }
- if(mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
- error("nfacct.plugin: mnl_socket_send");
- pthread_exit(NULL);
- return NULL;
- }
+ if(mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
+ error("nfacct.plugin: mnl_socket_send");
+ pthread_exit(NULL);
+ return NULL;
+ }
- if(nfacct_list) nfacct_list->len = 0;
+ if(nfacct_list) nfacct_list->len = 0;
- int 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;
- }
+ int 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 == -1) {
- error("nfacct.plugin: error communicating with kernel.");
- pthread_exit(NULL);
- return NULL;
- }
+ if (ret == -1) {
+ error("nfacct.plugin: error communicating with kernel.");
+ pthread_exit(NULL);
+ return NULL;
+ }
- // --------------------------------------------------------------------
+ // --------------------------------------------------------------------
- gettimeofday(&now, NULL);
- usec = usecdiff(&now, &last) - susec;
- debug(D_NFACCT_LOOP, "nfacct.plugin: last loop took %llu usec (worked for %llu, sleeped for %llu).", usec + susec, usec, susec);
+ 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(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;
+ 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", 1006, rrd_update_every, RRDSET_TYPE_STACKED);
+ st = rrdset_find_bytype("netfilter", "nfacct_packets");
+ if(!st) {
+ st = rrdset_create("netfilter", "nfacct_packets", NULL, "nfacct", NULL, "Netfilter Accounting Packets", "packets/s", 1006, 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);
- }
- else rrdset_next(st);
+ for(i = 0; i < nfacct_list->len ; i++)
+ rrddim_add(st, nfacct_list->data[i].name, NULL, 1, rrd_update_every, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
- for(i = 0; i < nfacct_list->len ; i++) {
- RRDDIM *rd = rrddim_find(st, nfacct_list->data[i].name);
+ 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);
- }
+ 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);
+ }
- rrdset_done(st);
+ rrdset_done(st);
- // ----------------------------------------------------------------
+ // ----------------------------------------------------------------
- st = rrdset_find_bytype("netfilter", "nfacct_bytes");
- if(!st) {
- st = rrdset_create("netfilter", "nfacct_bytes", NULL, "nfacct", NULL, "Netfilter Accounting Bandwidth", "kilobytes/s", 1007, rrd_update_every, RRDSET_TYPE_STACKED);
+ st = rrdset_find_bytype("netfilter", "nfacct_bytes");
+ if(!st) {
+ st = rrdset_create("netfilter", "nfacct_bytes", NULL, "nfacct", NULL, "Netfilter Accounting Bandwidth", "kilobytes/s", 1007, 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);
- }
- else rrdset_next(st);
+ for(i = 0; i < nfacct_list->len ; i++)
+ rrddim_add(st, nfacct_list->data[i].name, NULL, 1, 1000 * rrd_update_every, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
- for(i = 0; i < nfacct_list->len ; i++) {
- RRDDIM *rd = rrddim_find(st, nfacct_list->data[i].name);
+ 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);
- }
+ 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);
+ }
- rrdset_done(st);
- }
+ rrdset_done(st);
+ }
- // --------------------------------------------------------------------
+ // --------------------------------------------------------------------
- usleep(susec);
+ usleep(susec);
- // copy current to last
- bcopy(&now, &last, sizeof(struct timeval));
- }
+ // copy current to last
+ bcopy(&now, &last, sizeof(struct timeval));
+ }
- mnl_socket_close(nl);
- pthread_exit(NULL);
- return NULL;
+ mnl_socket_close(nl);
+ pthread_exit(NULL);
+ return NULL;
}
#endif
diff --git a/src/plugin_proc.c b/src/plugin_proc.c
index a147d971f..a1bf314de 100644
--- a/src/plugin_proc.c
+++ b/src/plugin_proc.c
@@ -1,319 +1,226 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <pthread.h>
-#include <sys/time.h>
-#include <sys/resource.h>
-#include <strings.h>
-#include <unistd.h>
-
-#include "global_statistics.h"
#include "common.h"
-#include "appconfig.h"
-#include "log.h"
-#include "rrd.h"
-#include "plugin_proc.h"
-#include "main.h"
-#include "registry.h"
void *proc_main(void *ptr)
{
- if(ptr) { ; }
-
- info("PROC Plugin thread created with task id %d", gettid());
-
- if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
- error("Cannot set pthread cancel type to DEFERRED.");
-
- if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
- error("Cannot set pthread cancel state to ENABLE.");
-
- struct rusage me, thread;
-
- // disable (by default) various interface that are not needed
- config_get_boolean("plugin:proc:/proc/net/dev:lo", "enabled", 0);
- config_get_boolean("plugin:proc:/proc/net/dev:fireqos_monitor", "enabled", 0);
-
- // when ZERO, attempt to do it
- int vdo_proc_net_dev = !config_get_boolean("plugin:proc", "/proc/net/dev", 1);
- int vdo_proc_diskstats = !config_get_boolean("plugin:proc", "/proc/diskstats", 1);
- int vdo_proc_net_snmp = !config_get_boolean("plugin:proc", "/proc/net/snmp", 1);
- int vdo_proc_net_snmp6 = !config_get_boolean("plugin:proc", "/proc/net/snmp6", 1);
- int vdo_proc_net_netstat = !config_get_boolean("plugin:proc", "/proc/net/netstat", 1);
- int vdo_proc_net_stat_conntrack = !config_get_boolean("plugin:proc", "/proc/net/stat/conntrack", 1);
- int vdo_proc_net_ip_vs_stats = !config_get_boolean("plugin:proc", "/proc/net/ip_vs/stats", 1);
- int vdo_proc_net_stat_synproxy = !config_get_boolean("plugin:proc", "/proc/net/stat/synproxy", 1);
- int vdo_proc_stat = !config_get_boolean("plugin:proc", "/proc/stat", 1);
- int vdo_proc_meminfo = !config_get_boolean("plugin:proc", "/proc/meminfo", 1);
- int vdo_proc_vmstat = !config_get_boolean("plugin:proc", "/proc/vmstat", 1);
- int vdo_proc_net_rpc_nfsd = !config_get_boolean("plugin:proc", "/proc/net/rpc/nfsd", 1);
- int vdo_proc_sys_kernel_random_entropy_avail = !config_get_boolean("plugin:proc", "/proc/sys/kernel/random/entropy_avail", 1);
- int vdo_proc_interrupts = !config_get_boolean("plugin:proc", "/proc/interrupts", 1);
- int vdo_proc_softirqs = !config_get_boolean("plugin:proc", "/proc/softirqs", 1);
- int vdo_proc_loadavg = !config_get_boolean("plugin:proc", "/proc/loadavg", 1);
- int vdo_sys_kernel_mm_ksm = !config_get_boolean("plugin:proc", "/sys/kernel/mm/ksm", 1);
- int vdo_cpu_netdata = !config_get_boolean("plugin:proc", "netdata server resources", 1);
-
- // keep track of the time each module was called
- unsigned long long sutime_proc_net_dev = 0ULL;
- unsigned long long sutime_proc_diskstats = 0ULL;
- unsigned long long sutime_proc_net_snmp = 0ULL;
- unsigned long long sutime_proc_net_snmp6 = 0ULL;
- unsigned long long sutime_proc_net_netstat = 0ULL;
- unsigned long long sutime_proc_net_stat_conntrack = 0ULL;
- unsigned long long sutime_proc_net_ip_vs_stats = 0ULL;
- unsigned long long sutime_proc_net_stat_synproxy = 0ULL;
- unsigned long long sutime_proc_stat = 0ULL;
- unsigned long long sutime_proc_meminfo = 0ULL;
- unsigned long long sutime_proc_vmstat = 0ULL;
- unsigned long long sutime_proc_net_rpc_nfsd = 0ULL;
- unsigned long long sutime_proc_sys_kernel_random_entropy_avail = 0ULL;
- unsigned long long sutime_proc_interrupts = 0ULL;
- unsigned long long sutime_proc_softirqs = 0ULL;
- unsigned long long sutime_proc_loadavg = 0ULL;
- unsigned long long sutime_sys_kernel_mm_ksm = 0ULL;
-
- // the next time we will run - aligned properly
- unsigned long long sunext = (time(NULL) - (time(NULL) % rrd_update_every) + rrd_update_every) * 1000000ULL;
- unsigned long long sunow;
-
- RRDSET *stcpu = NULL, *stcpu_thread = NULL, *stclients = NULL, *streqs = NULL, *stbytes = NULL;
-
- for(;1;) {
- if(unlikely(netdata_exit)) break;
-
- // delay until it is our time to run
- while((sunow = timems()) < sunext)
- usleep((useconds_t)(sunext - sunow));
-
- // find the next time we need to run
- while(timems() > sunext)
- sunext += rrd_update_every * 1000000ULL;
-
- if(unlikely(netdata_exit)) break;
-
- // BEGIN -- the job to be done
-
- if(!vdo_sys_kernel_mm_ksm) {
- debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_sys_kernel_mm_ksm().");
-
- sunow = timems();
- vdo_sys_kernel_mm_ksm = do_sys_kernel_mm_ksm(rrd_update_every, (sutime_sys_kernel_mm_ksm > 0)?sunow - sutime_sys_kernel_mm_ksm:0ULL);
- sutime_sys_kernel_mm_ksm = sunow;
- }
- if(unlikely(netdata_exit)) break;
-
- if(!vdo_proc_loadavg) {
- debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_loadavg().");
- sunow = timems();
- vdo_proc_loadavg = do_proc_loadavg(rrd_update_every, (sutime_proc_loadavg > 0)?sunow - sutime_proc_loadavg:0ULL);
- sutime_proc_loadavg = sunow;
- }
- if(unlikely(netdata_exit)) break;
-
- if(!vdo_proc_interrupts) {
- debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_interrupts().");
- sunow = timems();
- vdo_proc_interrupts = do_proc_interrupts(rrd_update_every, (sutime_proc_interrupts > 0)?sunow - sutime_proc_interrupts:0ULL);
- sutime_proc_interrupts = sunow;
- }
- if(unlikely(netdata_exit)) break;
-
- if(!vdo_proc_softirqs) {
- debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_softirqs().");
- sunow = timems();
- vdo_proc_softirqs = do_proc_softirqs(rrd_update_every, (sutime_proc_softirqs > 0)?sunow - sutime_proc_softirqs:0ULL);
- sutime_proc_softirqs = sunow;
- }
- if(unlikely(netdata_exit)) break;
-
- if(!vdo_proc_sys_kernel_random_entropy_avail) {
- debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_sys_kernel_random_entropy_avail().");
- sunow = timems();
- vdo_proc_sys_kernel_random_entropy_avail = do_proc_sys_kernel_random_entropy_avail(rrd_update_every, (sutime_proc_sys_kernel_random_entropy_avail > 0)?sunow - sutime_proc_sys_kernel_random_entropy_avail:0ULL);
- sutime_proc_sys_kernel_random_entropy_avail = sunow;
- }
- if(unlikely(netdata_exit)) break;
-
- if(!vdo_proc_net_dev) {
- debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_net_dev().");
- sunow = timems();
- vdo_proc_net_dev = do_proc_net_dev(rrd_update_every, (sutime_proc_net_dev > 0)?sunow - sutime_proc_net_dev:0ULL);
- sutime_proc_net_dev = sunow;
- }
- if(unlikely(netdata_exit)) break;
-
- if(!vdo_proc_diskstats) {
- debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_diskstats().");
- sunow = timems();
- vdo_proc_diskstats = do_proc_diskstats(rrd_update_every, (sutime_proc_diskstats > 0)?sunow - sutime_proc_diskstats:0ULL);
- sutime_proc_diskstats = sunow;
- }
- if(unlikely(netdata_exit)) break;
-
- if(!vdo_proc_net_snmp) {
- debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_net_snmp().");
- sunow = timems();
- vdo_proc_net_snmp = do_proc_net_snmp(rrd_update_every, (sutime_proc_net_snmp > 0)?sunow - sutime_proc_net_snmp:0ULL);
- sutime_proc_net_snmp = sunow;
- }
- if(unlikely(netdata_exit)) break;
-
- if(!vdo_proc_net_snmp6) {
- debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_net_snmp6().");
- sunow = timems();
- vdo_proc_net_snmp6 = do_proc_net_snmp6(rrd_update_every, (sutime_proc_net_snmp6 > 0)?sunow - sutime_proc_net_snmp6:0ULL);
- sutime_proc_net_snmp6 = sunow;
- }
- if(unlikely(netdata_exit)) break;
-
- if(!vdo_proc_net_netstat) {
- debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_net_netstat().");
- sunow = timems();
- vdo_proc_net_netstat = do_proc_net_netstat(rrd_update_every, (sutime_proc_net_netstat > 0)?sunow - sutime_proc_net_netstat:0ULL);
- sutime_proc_net_netstat = sunow;
- }
- if(unlikely(netdata_exit)) break;
-
- if(!vdo_proc_net_stat_conntrack) {
- debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_net_stat_conntrack().");
- sunow = timems();
- vdo_proc_net_stat_conntrack = do_proc_net_stat_conntrack(rrd_update_every, (sutime_proc_net_stat_conntrack > 0)?sunow - sutime_proc_net_stat_conntrack:0ULL);
- sutime_proc_net_stat_conntrack = sunow;
- }
- if(unlikely(netdata_exit)) break;
-
- if(!vdo_proc_net_ip_vs_stats) {
- debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling vdo_proc_net_ip_vs_stats().");
- sunow = timems();
- vdo_proc_net_ip_vs_stats = do_proc_net_ip_vs_stats(rrd_update_every, (sutime_proc_net_ip_vs_stats > 0)?sunow - sutime_proc_net_ip_vs_stats:0ULL);
- sutime_proc_net_ip_vs_stats = sunow;
- }
- if(unlikely(netdata_exit)) break;
-
- if(!vdo_proc_net_stat_synproxy) {
- debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling vdo_proc_net_stat_synproxy().");
- sunow = timems();
- vdo_proc_net_stat_synproxy = do_proc_net_stat_synproxy(rrd_update_every, (sutime_proc_net_stat_synproxy > 0)?sunow - sutime_proc_net_stat_synproxy:0ULL);
- sutime_proc_net_stat_synproxy = sunow;
- }
- if(unlikely(netdata_exit)) break;
-
- if(!vdo_proc_stat) {
- debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_stat().");
- sunow = timems();
- vdo_proc_stat = do_proc_stat(rrd_update_every, (sutime_proc_stat > 0)?sunow - sutime_proc_stat:0ULL);
- sutime_proc_stat = sunow;
- }
- if(unlikely(netdata_exit)) break;
-
- if(!vdo_proc_meminfo) {
- debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling vdo_proc_meminfo().");
- sunow = timems();
- vdo_proc_meminfo = do_proc_meminfo(rrd_update_every, (sutime_proc_meminfo > 0)?sunow - sutime_proc_meminfo:0ULL);
- sutime_proc_meminfo = sunow;
- }
- if(unlikely(netdata_exit)) break;
-
- if(!vdo_proc_vmstat) {
- debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling vdo_proc_vmstat().");
- sunow = timems();
- vdo_proc_vmstat = do_proc_vmstat(rrd_update_every, (sutime_proc_vmstat > 0)?sunow - sutime_proc_vmstat:0ULL);
- sutime_proc_vmstat = sunow;
- }
- if(unlikely(netdata_exit)) break;
-
- if(!vdo_proc_net_rpc_nfsd) {
- debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_net_rpc_nfsd().");
- sunow = timems();
- vdo_proc_net_rpc_nfsd = do_proc_net_rpc_nfsd(rrd_update_every, (sutime_proc_net_rpc_nfsd > 0)?sunow - sutime_proc_net_rpc_nfsd:0ULL);
- sutime_proc_net_rpc_nfsd = sunow;
- }
- if(unlikely(netdata_exit)) break;
-
- // END -- the job is done
-
- // --------------------------------------------------------------------
-
- if(!vdo_cpu_netdata) {
- getrusage(RUSAGE_THREAD, &thread);
- getrusage(RUSAGE_SELF, &me);
-
- if(!stcpu_thread) stcpu_thread = rrdset_find("netdata.plugin_proc_cpu");
- if(!stcpu_thread) {
- stcpu_thread = rrdset_create("netdata", "plugin_proc_cpu", NULL, "proc.internal", NULL, "NetData Proc Plugin CPU usage", "milliseconds/s", 132000, rrd_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);
- }
- else rrdset_next(stcpu_thread);
-
- rrddim_set(stcpu_thread, "user" , thread.ru_utime.tv_sec * 1000000ULL + thread.ru_utime.tv_usec);
- rrddim_set(stcpu_thread, "system", thread.ru_stime.tv_sec * 1000000ULL + thread.ru_stime.tv_usec);
- rrdset_done(stcpu_thread);
-
- // ----------------------------------------------------------------
-
- if(!stcpu) stcpu = rrdset_find("netdata.server_cpu");
- if(!stcpu) {
- stcpu = rrdset_create("netdata", "server_cpu", NULL, "netdata", NULL, "NetData CPU usage", "milliseconds/s", 130000, rrd_update_every, RRDSET_TYPE_STACKED);
-
- rrddim_add(stcpu, "user", NULL, 1, 1000, RRDDIM_INCREMENTAL);
- rrddim_add(stcpu, "system", NULL, 1, 1000, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(stcpu);
-
- rrddim_set(stcpu, "user" , me.ru_utime.tv_sec * 1000000ULL + me.ru_utime.tv_usec);
- rrddim_set(stcpu, "system", me.ru_stime.tv_sec * 1000000ULL + me.ru_stime.tv_usec);
- rrdset_done(stcpu);
-
- // ----------------------------------------------------------------
-
- if(!stclients) stclients = rrdset_find("netdata.clients");
- if(!stclients) {
- stclients = rrdset_create("netdata", "clients", NULL, "netdata", NULL, "NetData Web Clients", "connected clients", 130100, rrd_update_every, RRDSET_TYPE_LINE);
-
- rrddim_add(stclients, "clients", NULL, 1, 1, RRDDIM_ABSOLUTE);
- }
- else rrdset_next(stclients);
-
- rrddim_set(stclients, "clients", global_statistics.connected_clients);
- rrdset_done(stclients);
-
- // ----------------------------------------------------------------
-
- if(!streqs) streqs = rrdset_find("netdata.requests");
- if(!streqs) {
- streqs = rrdset_create("netdata", "requests", NULL, "netdata", NULL, "NetData Web Requests", "requests/s", 130200, rrd_update_every, RRDSET_TYPE_LINE);
-
- rrddim_add(streqs, "requests", NULL, 1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(streqs);
-
- rrddim_set(streqs, "requests", global_statistics.web_requests);
- rrdset_done(streqs);
-
- // ----------------------------------------------------------------
-
- if(!stbytes) stbytes = rrdset_find("netdata.net");
- if(!stbytes) {
- stbytes = rrdset_create("netdata", "net", NULL, "netdata", NULL, "NetData Network Traffic", "kilobits/s", 130300, rrd_update_every, RRDSET_TYPE_AREA);
-
- rrddim_add(stbytes, "in", NULL, 8, 1024, RRDDIM_INCREMENTAL);
- rrddim_add(stbytes, "out", NULL, -8, 1024, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(stbytes);
-
- rrddim_set(stbytes, "in", global_statistics.bytes_received);
- rrddim_set(stbytes, "out", global_statistics.bytes_sent);
- rrdset_done(stbytes);
-
- // ----------------------------------------------------------------
-
- registry_statistics();
- }
- }
-
- pthread_exit(NULL);
- return NULL;
+ (void)ptr;
+
+ info("PROC Plugin thread created with task id %d", gettid());
+
+ if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
+ error("Cannot set pthread cancel type to DEFERRED.");
+
+ if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
+ error("Cannot set pthread cancel state to ENABLE.");
+
+ // disable (by default) various interface that are not needed
+ config_get_boolean("plugin:proc:/proc/net/dev:lo", "enabled", 0);
+ config_get_boolean("plugin:proc:/proc/net/dev:fireqos_monitor", "enabled", 0);
+
+ // when ZERO, attempt to do it
+ int vdo_proc_net_dev = !config_get_boolean("plugin:proc", "/proc/net/dev", 1);
+ int vdo_proc_diskstats = !config_get_boolean("plugin:proc", "/proc/diskstats", 1);
+ int vdo_proc_net_snmp = !config_get_boolean("plugin:proc", "/proc/net/snmp", 1);
+ int vdo_proc_net_snmp6 = !config_get_boolean("plugin:proc", "/proc/net/snmp6", 1);
+ int vdo_proc_net_netstat = !config_get_boolean("plugin:proc", "/proc/net/netstat", 1);
+ int vdo_proc_net_stat_conntrack = !config_get_boolean("plugin:proc", "/proc/net/stat/conntrack", 1);
+ int vdo_proc_net_ip_vs_stats = !config_get_boolean("plugin:proc", "/proc/net/ip_vs/stats", 1);
+ int vdo_proc_net_stat_synproxy = !config_get_boolean("plugin:proc", "/proc/net/stat/synproxy", 1);
+ int vdo_proc_stat = !config_get_boolean("plugin:proc", "/proc/stat", 1);
+ int vdo_proc_meminfo = !config_get_boolean("plugin:proc", "/proc/meminfo", 1);
+ int vdo_proc_vmstat = !config_get_boolean("plugin:proc", "/proc/vmstat", 1);
+ int vdo_proc_net_rpc_nfsd = !config_get_boolean("plugin:proc", "/proc/net/rpc/nfsd", 1);
+ int vdo_proc_sys_kernel_random_entropy_avail = !config_get_boolean("plugin:proc", "/proc/sys/kernel/random/entropy_avail", 1);
+ int vdo_proc_interrupts = !config_get_boolean("plugin:proc", "/proc/interrupts", 1);
+ int vdo_proc_softirqs = !config_get_boolean("plugin:proc", "/proc/softirqs", 1);
+ int vdo_proc_loadavg = !config_get_boolean("plugin:proc", "/proc/loadavg", 1);
+ int vdo_sys_kernel_mm_ksm = !config_get_boolean("plugin:proc", "/sys/kernel/mm/ksm", 1);
+ int vdo_cpu_netdata = !config_get_boolean("plugin:proc", "netdata server resources", 1);
+
+ // keep track of the time each module was called
+ unsigned long long sutime_proc_net_dev = 0ULL;
+ unsigned long long sutime_proc_diskstats = 0ULL;
+ unsigned long long sutime_proc_net_snmp = 0ULL;
+ unsigned long long sutime_proc_net_snmp6 = 0ULL;
+ unsigned long long sutime_proc_net_netstat = 0ULL;
+ unsigned long long sutime_proc_net_stat_conntrack = 0ULL;
+ unsigned long long sutime_proc_net_ip_vs_stats = 0ULL;
+ unsigned long long sutime_proc_net_stat_synproxy = 0ULL;
+ unsigned long long sutime_proc_stat = 0ULL;
+ unsigned long long sutime_proc_meminfo = 0ULL;
+ unsigned long long sutime_proc_vmstat = 0ULL;
+ unsigned long long sutime_proc_net_rpc_nfsd = 0ULL;
+ unsigned long long sutime_proc_sys_kernel_random_entropy_avail = 0ULL;
+ unsigned long long sutime_proc_interrupts = 0ULL;
+ unsigned long long sutime_proc_softirqs = 0ULL;
+ unsigned long long sutime_proc_loadavg = 0ULL;
+ unsigned long long sutime_sys_kernel_mm_ksm = 0ULL;
+
+ // the next time we will run - aligned properly
+ unsigned long long sunext = (time(NULL) - (time(NULL) % rrd_update_every) + rrd_update_every) * 1000000ULL;
+ unsigned long long sunow;
+
+ for(;1;) {
+ if(unlikely(netdata_exit)) break;
+
+ // delay until it is our time to run
+ while((sunow = time_usec()) < sunext)
+ sleep_usec(sunext - sunow);
+
+ // find the next time we need to run
+ while(time_usec() > sunext)
+ sunext += rrd_update_every * 1000000ULL;
+
+ if(unlikely(netdata_exit)) break;
+
+ // BEGIN -- the job to be done
+
+ if(!vdo_sys_kernel_mm_ksm) {
+ debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_sys_kernel_mm_ksm().");
+
+ sunow = time_usec();
+ vdo_sys_kernel_mm_ksm = do_sys_kernel_mm_ksm(rrd_update_every, (sutime_sys_kernel_mm_ksm > 0)?sunow - sutime_sys_kernel_mm_ksm:0ULL);
+ sutime_sys_kernel_mm_ksm = sunow;
+ }
+ if(unlikely(netdata_exit)) break;
+
+ if(!vdo_proc_loadavg) {
+ debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_loadavg().");
+ sunow = time_usec();
+ vdo_proc_loadavg = do_proc_loadavg(rrd_update_every, (sutime_proc_loadavg > 0)?sunow - sutime_proc_loadavg:0ULL);
+ sutime_proc_loadavg = sunow;
+ }
+ if(unlikely(netdata_exit)) break;
+
+ if(!vdo_proc_interrupts) {
+ debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_interrupts().");
+ sunow = time_usec();
+ vdo_proc_interrupts = do_proc_interrupts(rrd_update_every, (sutime_proc_interrupts > 0)?sunow - sutime_proc_interrupts:0ULL);
+ sutime_proc_interrupts = sunow;
+ }
+ if(unlikely(netdata_exit)) break;
+
+ if(!vdo_proc_softirqs) {
+ debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_softirqs().");
+ sunow = time_usec();
+ vdo_proc_softirqs = do_proc_softirqs(rrd_update_every, (sutime_proc_softirqs > 0)?sunow - sutime_proc_softirqs:0ULL);
+ sutime_proc_softirqs = sunow;
+ }
+ if(unlikely(netdata_exit)) break;
+
+ if(!vdo_proc_sys_kernel_random_entropy_avail) {
+ debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_sys_kernel_random_entropy_avail().");
+ sunow = time_usec();
+ vdo_proc_sys_kernel_random_entropy_avail = do_proc_sys_kernel_random_entropy_avail(rrd_update_every, (sutime_proc_sys_kernel_random_entropy_avail > 0)?sunow - sutime_proc_sys_kernel_random_entropy_avail:0ULL);
+ sutime_proc_sys_kernel_random_entropy_avail = sunow;
+ }
+ if(unlikely(netdata_exit)) break;
+
+ if(!vdo_proc_net_dev) {
+ debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_net_dev().");
+ sunow = time_usec();
+ vdo_proc_net_dev = do_proc_net_dev(rrd_update_every, (sutime_proc_net_dev > 0)?sunow - sutime_proc_net_dev:0ULL);
+ sutime_proc_net_dev = sunow;
+ }
+ if(unlikely(netdata_exit)) break;
+
+ if(!vdo_proc_diskstats) {
+ debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_diskstats().");
+ sunow = time_usec();
+ vdo_proc_diskstats = do_proc_diskstats(rrd_update_every, (sutime_proc_diskstats > 0)?sunow - sutime_proc_diskstats:0ULL);
+ sutime_proc_diskstats = sunow;
+ }
+ if(unlikely(netdata_exit)) break;
+
+ if(!vdo_proc_net_snmp) {
+ debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_net_snmp().");
+ sunow = time_usec();
+ vdo_proc_net_snmp = do_proc_net_snmp(rrd_update_every, (sutime_proc_net_snmp > 0)?sunow - sutime_proc_net_snmp:0ULL);
+ sutime_proc_net_snmp = sunow;
+ }
+ if(unlikely(netdata_exit)) break;
+
+ if(!vdo_proc_net_snmp6) {
+ debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_net_snmp6().");
+ sunow = time_usec();
+ vdo_proc_net_snmp6 = do_proc_net_snmp6(rrd_update_every, (sutime_proc_net_snmp6 > 0)?sunow - sutime_proc_net_snmp6:0ULL);
+ sutime_proc_net_snmp6 = sunow;
+ }
+ if(unlikely(netdata_exit)) break;
+
+ if(!vdo_proc_net_netstat) {
+ debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_net_netstat().");
+ sunow = time_usec();
+ vdo_proc_net_netstat = do_proc_net_netstat(rrd_update_every, (sutime_proc_net_netstat > 0)?sunow - sutime_proc_net_netstat:0ULL);
+ sutime_proc_net_netstat = sunow;
+ }
+ if(unlikely(netdata_exit)) break;
+
+ if(!vdo_proc_net_stat_conntrack) {
+ debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_net_stat_conntrack().");
+ sunow = time_usec();
+ vdo_proc_net_stat_conntrack = do_proc_net_stat_conntrack(rrd_update_every, (sutime_proc_net_stat_conntrack > 0)?sunow - sutime_proc_net_stat_conntrack:0ULL);
+ sutime_proc_net_stat_conntrack = sunow;
+ }
+ if(unlikely(netdata_exit)) break;
+
+ if(!vdo_proc_net_ip_vs_stats) {
+ debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling vdo_proc_net_ip_vs_stats().");
+ sunow = time_usec();
+ vdo_proc_net_ip_vs_stats = do_proc_net_ip_vs_stats(rrd_update_every, (sutime_proc_net_ip_vs_stats > 0)?sunow - sutime_proc_net_ip_vs_stats:0ULL);
+ sutime_proc_net_ip_vs_stats = sunow;
+ }
+ if(unlikely(netdata_exit)) break;
+
+ if(!vdo_proc_net_stat_synproxy) {
+ debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling vdo_proc_net_stat_synproxy().");
+ sunow = time_usec();
+ vdo_proc_net_stat_synproxy = do_proc_net_stat_synproxy(rrd_update_every, (sutime_proc_net_stat_synproxy > 0)?sunow - sutime_proc_net_stat_synproxy:0ULL);
+ sutime_proc_net_stat_synproxy = sunow;
+ }
+ if(unlikely(netdata_exit)) break;
+
+ if(!vdo_proc_stat) {
+ debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_stat().");
+ sunow = time_usec();
+ vdo_proc_stat = do_proc_stat(rrd_update_every, (sutime_proc_stat > 0)?sunow - sutime_proc_stat:0ULL);
+ sutime_proc_stat = sunow;
+ }
+ if(unlikely(netdata_exit)) break;
+
+ if(!vdo_proc_meminfo) {
+ debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling vdo_proc_meminfo().");
+ sunow = time_usec();
+ vdo_proc_meminfo = do_proc_meminfo(rrd_update_every, (sutime_proc_meminfo > 0)?sunow - sutime_proc_meminfo:0ULL);
+ sutime_proc_meminfo = sunow;
+ }
+ if(unlikely(netdata_exit)) break;
+
+ if(!vdo_proc_vmstat) {
+ debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling vdo_proc_vmstat().");
+ sunow = time_usec();
+ vdo_proc_vmstat = do_proc_vmstat(rrd_update_every, (sutime_proc_vmstat > 0)?sunow - sutime_proc_vmstat:0ULL);
+ sutime_proc_vmstat = sunow;
+ }
+ if(unlikely(netdata_exit)) break;
+
+ if(!vdo_proc_net_rpc_nfsd) {
+ debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_net_rpc_nfsd().");
+ sunow = time_usec();
+ vdo_proc_net_rpc_nfsd = do_proc_net_rpc_nfsd(rrd_update_every, (sutime_proc_net_rpc_nfsd > 0)?sunow - sutime_proc_net_rpc_nfsd:0ULL);
+ sutime_proc_net_rpc_nfsd = sunow;
+ }
+ if(unlikely(netdata_exit)) break;
+
+ // END -- the job is done
+
+ // --------------------------------------------------------------------
+
+ if(!vdo_cpu_netdata) {
+ global_statistics_charts();
+ registry_statistics();
+ }
+ }
+
+ pthread_exit(NULL);
+ return NULL;
}
diff --git a/src/plugin_tc.c b/src/plugin_tc.c
index 3d3e35217..408069dba 100644
--- a/src/plugin_tc.c
+++ b/src/plugin_tc.c
@@ -1,24 +1,7 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/resource.h>
-
-#include "avl.h"
-#include "log.h"
#include "common.h"
-#include "appconfig.h"
-#include "rrd.h"
-#include "popen.h"
-#include "plugin_tc.h"
-#include "main.h"
-#include "../config.h"
-#define RRD_TYPE_TC "tc"
-#define RRD_TYPE_TC_LEN strlen(RRD_TYPE_TC)
+#define RRD_TYPE_TC "tc"
+#define RRD_TYPE_TC_LEN strlen(RRD_TYPE_TC)
// ----------------------------------------------------------------------------
// /sbin/tc processor
@@ -27,54 +10,71 @@
#define TC_LINE_MAX 1024
struct tc_class {
- avl avl;
-
- char *id;
- uint32_t hash;
-
- char *name;
-
- char *leafid;
- uint32_t leaf_hash;
-
- char *parentid;
- uint32_t parent_hash;
+ avl avl;
+
+ char *id;
+ uint32_t hash;
+
+ char *name;
+
+ char *leafid;
+ uint32_t leaf_hash;
+
+ char *parentid;
+ uint32_t parent_hash;
+
+ char hasparent;
+ char isleaf;
+ unsigned long long bytes;
+ unsigned long long packets;
+ unsigned long long dropped;
+ unsigned long long overlimits;
+ unsigned long long requeues;
+ unsigned long long lended;
+ unsigned long long borrowed;
+ unsigned long long giants;
+ unsigned long long tokens;
+ unsigned long long ctokens;
+
+ RRDDIM *rd_bytes;
+ RRDDIM *rd_packets;
+ RRDDIM *rd_dropped;
+
+ char name_updated;
+ char updated; // updated bytes
+ int seen; // seen in the tc list (even without bytes)
+
+ struct tc_class *next;
+ struct tc_class *prev;
+};
- char hasparent;
- char isleaf;
- unsigned long long bytes;
- unsigned long long packets;
- unsigned long long dropped;
- unsigned long long overlimits;
- unsigned long long requeues;
- unsigned long long lended;
- unsigned long long borrowed;
- unsigned long long giants;
- unsigned long long tokens;
- unsigned long long ctokens;
+struct tc_device {
+ avl avl;
- char updated; // updated bytes
- char seen; // seen in the tc list (even without bytes)
+ char *id;
+ uint32_t hash;
- struct tc_class *next;
- struct tc_class *prev;
-};
+ char *name;
+ char *family;
-struct tc_device {
- avl avl;
+ char name_updated;
+ char family_updated;
- char *id;
- uint32_t hash;
+ char enabled;
+ char enabled_bytes;
+ char enabled_packets;
+ char enabled_dropped;
- char *name;
- char *family;
+ RRDSET *st_bytes;
+ RRDSET *st_packets;
+ RRDSET *st_dropped;
- avl_tree classes_index;
+ avl_tree classes_index;
- struct tc_class *classes;
+ struct tc_class *classes;
- struct tc_device *next;
- struct tc_device *prev;
+ struct tc_device *next;
+ struct tc_device *prev;
};
@@ -84,25 +84,25 @@ struct tc_device *tc_device_root = NULL;
// tc_device index
static int tc_device_compare(void* a, void* b) {
- if(((struct tc_device *)a)->hash < ((struct tc_device *)b)->hash) return -1;
- else if(((struct tc_device *)a)->hash > ((struct tc_device *)b)->hash) return 1;
- else return strcmp(((struct tc_device *)a)->id, ((struct tc_device *)b)->id);
+ if(((struct tc_device *)a)->hash < ((struct tc_device *)b)->hash) return -1;
+ else if(((struct tc_device *)a)->hash > ((struct tc_device *)b)->hash) return 1;
+ else return strcmp(((struct tc_device *)a)->id, ((struct tc_device *)b)->id);
}
avl_tree tc_device_root_index = {
- NULL,
- tc_device_compare
+ NULL,
+ tc_device_compare
};
#define tc_device_index_add(st) avl_insert(&tc_device_root_index, (avl *)(st))
#define tc_device_index_del(st) avl_remove(&tc_device_root_index, (avl *)(st))
static inline struct tc_device *tc_device_index_find(const char *id, uint32_t hash) {
- struct tc_device tmp;
- tmp.id = (char *)id;
- tmp.hash = (hash)?hash:simple_hash(tmp.id);
+ struct tc_device tmp;
+ tmp.id = (char *)id;
+ tmp.hash = (hash)?hash:simple_hash(tmp.id);
- return (struct tc_device *)avl_search(&(tc_device_root_index), (avl *)&tmp);
+ return (struct tc_device *)avl_search(&(tc_device_root_index), (avl *)&tmp);
}
@@ -110,621 +110,766 @@ static inline struct tc_device *tc_device_index_find(const char *id, uint32_t ha
// tc_class index
static int tc_class_compare(void* a, void* b) {
- if(((struct tc_class *)a)->hash < ((struct tc_class *)b)->hash) return -1;
- else if(((struct tc_class *)a)->hash > ((struct tc_class *)b)->hash) return 1;
- else return strcmp(((struct tc_class *)a)->id, ((struct tc_class *)b)->id);
+ if(((struct tc_class *)a)->hash < ((struct tc_class *)b)->hash) return -1;
+ else if(((struct tc_class *)a)->hash > ((struct tc_class *)b)->hash) return 1;
+ else return strcmp(((struct tc_class *)a)->id, ((struct tc_class *)b)->id);
}
#define tc_class_index_add(st, rd) avl_insert(&((st)->classes_index), (avl *)(rd))
#define tc_class_index_del(st, rd) avl_remove(&((st)->classes_index), (avl *)(rd))
static inline struct tc_class *tc_class_index_find(struct tc_device *st, const char *id, uint32_t hash) {
- struct tc_class tmp;
- tmp.id = (char *)id;
- tmp.hash = (hash)?hash:simple_hash(tmp.id);
+ struct tc_class tmp;
+ tmp.id = (char *)id;
+ tmp.hash = (hash)?hash:simple_hash(tmp.id);
- return (struct tc_class *)avl_search(&(st->classes_index), (avl *) &tmp);
+ return (struct tc_class *)avl_search(&(st->classes_index), (avl *) &tmp);
}
// ----------------------------------------------------------------------------
static inline void tc_class_free(struct tc_device *n, struct tc_class *c) {
- debug(D_TC_LOOP, "Removing from device '%s' class '%s', parentid '%s', leafid '%s', seen=%d", n->id, c->id, c->parentid?c->parentid:"", c->leafid?c->leafid:"", c->seen);
-
- if(c->next) c->next->prev = c->prev;
- if(c->prev) c->prev->next = c->next;
- if(n->classes == c) {
- if(c->next) n->classes = c->next;
- else n->classes = c->prev;
- }
-
- tc_class_index_del(n, c);
-
- if(c->id) free(c->id);
- if(c->name) free(c->name);
- if(c->leafid) free(c->leafid);
- if(c->parentid) free(c->parentid);
-
- free(c);
+ if(c == n->classes) {
+ if(c->next)
+ n->classes = c->next;
+ else
+ n->classes = c->prev;
+ }
+ if(c->next) c->next->prev = c->prev;
+ if(c->prev) c->prev->next = c->next;
+
+ debug(D_TC_LOOP, "Removing from device '%s' class '%s', parentid '%s', leafid '%s', seen=%d", n->id, c->id, c->parentid?c->parentid:"", c->leafid?c->leafid:"", c->seen);
+
+ tc_class_index_del(n, c);
+
+ freez(c->id);
+ freez(c->name);
+ freez(c->leafid);
+ freez(c->parentid);
+ freez(c);
}
static inline void tc_device_classes_cleanup(struct tc_device *d) {
- static int cleanup_every = 999;
-
- if(cleanup_every > 0) {
- cleanup_every = (int) -config_get_number("plugin:tc", "cleanup unused classes every", 60);
- if(cleanup_every > 0) cleanup_every = -cleanup_every;
- if(cleanup_every == 0) cleanup_every = -1;
- }
-
- struct tc_class *c = d->classes;
- while(c) {
- if(c->seen < cleanup_every) {
- struct tc_class *nc = c->next;
- tc_class_free(d, c);
- c = nc;
- }
- else c = c->next;
-
- if(c) {
- c->updated = 0;
- c->seen--;
- }
- }
+ static int cleanup_every = 999;
+
+ if(unlikely(cleanup_every > 0)) {
+ cleanup_every = (int) config_get_number("plugin:tc", "cleanup unused classes every", 60);
+ if(cleanup_every < 0) cleanup_every = -cleanup_every;
+ }
+
+ d->name_updated = 0;
+ d->family_updated = 0;
+
+ struct tc_class *c = d->classes;
+ while(c) {
+ if(unlikely(cleanup_every > 0 && c->seen >= cleanup_every)) {
+ struct tc_class *nc = c->next;
+ tc_class_free(d, c);
+ c = nc;
+ }
+ else {
+ c->updated = 0;
+ c->name_updated = 0;
+
+ c = c->next;
+ }
+ }
}
-static inline void tc_device_commit(struct tc_device *d)
-{
- static int enable_new_interfaces = -1;
-
- if(enable_new_interfaces == -1) enable_new_interfaces = config_get_boolean("plugin:tc", "enable new interfaces detected at runtime", 1);
-
- // we only need to add leaf classes
- struct tc_class *c, *x;
-
- // set all classes
- for(c = d->classes ; c ; c = c->next) {
- c->isleaf = 1;
- c->hasparent = 0;
- }
-
- // mark the classes as leafs and parents
- for(c = d->classes ; c ; c = c->next) {
- if(!c->updated) continue;
-
- for(x = d->classes ; x ; x = x->next) {
- if(!x->updated) continue;
-
- if(c == x) continue;
-
- if(x->parentid && (
- ( c->hash == x->parent_hash && strcmp(c->id, x->parentid) == 0) ||
- (c->leafid && c->leaf_hash == x->parent_hash && strcmp(c->leafid, x->parentid) == 0))) {
- // debug(D_TC_LOOP, "TC: In device '%s', class '%s' (leafid: '%s') has as leaf class '%s' (parentid: '%s').", d->name?d->name:d->id, c->name?c->name:c->id, c->leafid?c->leafid:c->id, x->name?x->name:x->id, x->parentid?x->parentid:x->id);
- c->isleaf = 0;
- x->hasparent = 1;
- }
- }
- }
-
- // debugging:
- /*
- for ( c = d->classes ; c ; c = c->next) {
- if(c->isleaf && c->hasparent) debug(D_TC_LOOP, "TC: Device %s, class %s, OK", d->name, c->id);
- else debug(D_TC_LOOP, "TC: Device %s, class %s, IGNORE (isleaf: %d, hasparent: %d, parent: %s)", d->name, c->id, c->isleaf, c->hasparent, c->parentid);
- }
- */
-
- // we need at least a class
- for(c = d->classes ; c ; c = c->next) {
- // debug(D_TC_LOOP, "TC: Device '%s', class '%s', isLeaf=%d, HasParent=%d, Seen=%d", d->name?d->name:d->id, c->name?c->name:c->id, c->isleaf, c->hasparent, c->seen);
- if(!c->updated) continue;
- if(c->isleaf && c->hasparent) break;
- }
- if(!c) {
- debug(D_TC_LOOP, "TC: Ignoring TC device '%s'. No leaf classes.", d->name?d->name:d->id);
- tc_device_classes_cleanup(d);
- return;
- }
-
- char var_name[CONFIG_MAX_NAME + 1];
- snprintfz(var_name, CONFIG_MAX_NAME, "qos for %s", d->id);
- if(config_get_boolean("plugin:tc", var_name, enable_new_interfaces)) {
- RRDSET *st = rrdset_find_bytype(RRD_TYPE_TC, d->id);
- if(!st) {
- debug(D_TC_LOOP, "TC: Creating new chart for device '%s'", d->name?d->name:d->id);
-
- st = rrdset_create(RRD_TYPE_TC, d->id, d->name?d->name:d->id, d->family?d->family:d->id, RRD_TYPE_TC ".qos", "Class Usage", "kilobits/s", 7000, rrd_update_every, RRDSET_TYPE_STACKED);
-
- for(c = d->classes ; c ; c = c->next) {
- if(!c->updated) continue;
-
- if(c->isleaf && c->hasparent)
- rrddim_add(st, c->id, c->name?c->name:c->id, 8, 1024, RRDDIM_INCREMENTAL);
- }
- }
- else {
- debug(D_TC_LOOP, "TC: Updating chart for device '%s'", d->name?d->name:d->id);
- rrdset_next_plugins(st);
-
- if(d->name && strcmp(d->id, d->name) != 0) rrdset_set_name(st, d->name);
- }
-
- for(c = d->classes ; c ; c = c->next) {
- if(!c->updated) continue;
-
- if(c->isleaf && c->hasparent) {
- RRDDIM *rd = rrddim_find(st, c->id);
-
- if(!rd) {
- debug(D_TC_LOOP, "TC: Adding to chart '%s', dimension '%s'", st->id, c->id, c->name);
-
- // new class, we have to add it
- rd = rrddim_add(st, c->id, c->name?c->name:c->id, 8, 1024, RRDDIM_INCREMENTAL);
- }
- else debug(D_TC_LOOP, "TC: Updating chart '%s', dimension '%s'", st->id, c->id);
-
- rrddim_set_by_pointer(st, rd, c->bytes);
-
- // if it has a name, different to the id
- if(c->name) {
- // update the rrd dimension with the new name
- debug(D_TC_LOOP, "TC: Setting chart '%s', dimension '%s' name to '%s'", st->id, rd->id, c->name);
- rrddim_set_name(st, rd, c->name);
-
- free(c->name);
- c->name = NULL;
- }
- }
- }
- rrdset_done(st);
- }
-
- tc_device_classes_cleanup(d);
+static inline void tc_device_commit(struct tc_device *d) {
+ static int enable_new_interfaces = -1, enable_bytes = -1, enable_packets = -1, enable_dropped = -1;
+
+ if(unlikely(enable_new_interfaces == -1)) {
+ enable_new_interfaces = config_get_boolean_ondemand("plugin:tc", "enable new interfaces detected at runtime", CONFIG_ONDEMAND_YES);
+ enable_bytes = config_get_boolean_ondemand("plugin:tc", "enable traffic charts for all interfaces", CONFIG_ONDEMAND_ONDEMAND);
+ enable_packets = config_get_boolean_ondemand("plugin:tc", "enable packets charts for all interfaces", CONFIG_ONDEMAND_ONDEMAND);
+ enable_dropped = config_get_boolean_ondemand("plugin:tc", "enable dropped charts for all interfaces", CONFIG_ONDEMAND_ONDEMAND);
+ }
+
+ // we only need to add leaf classes
+ struct tc_class *c, *x;
+ unsigned long long bytes_sum = 0, packets_sum = 0, dropped_sum = 0;
+ int active_classes = 0;
+
+ // set all classes
+ for(c = d->classes ; c ; c = c->next) {
+ c->isleaf = 1;
+ c->hasparent = 0;
+ }
+
+ // mark the classes as leafs and parents
+ for(c = d->classes ; c ; c = c->next) {
+ if(unlikely(!c->updated)) continue;
+
+ for(x = d->classes ; x ; x = x->next) {
+ if(unlikely(!x->updated)) continue;
+
+ if(unlikely(c == x)) continue;
+
+ if(x->parentid && (
+ ( c->hash == x->parent_hash && strcmp(c->id, x->parentid) == 0) ||
+ (c->leafid && c->leaf_hash == x->parent_hash && strcmp(c->leafid, x->parentid) == 0))) {
+ // debug(D_TC_LOOP, "TC: In device '%s', class '%s' (leafid: '%s') has as leaf class '%s' (parentid: '%s').", d->name?d->name:d->id, c->name?c->name:c->id, c->leafid?c->leafid:c->id, x->name?x->name:x->id, x->parentid?x->parentid:x->id);
+ c->isleaf = 0;
+ x->hasparent = 1;
+ }
+ }
+ }
+
+ // debugging:
+ /*
+ for ( c = d->classes ; c ; c = c->next) {
+ if(c->isleaf && c->hasparent) debug(D_TC_LOOP, "TC: Device %s, class %s, OK", d->name, c->id);
+ else debug(D_TC_LOOP, "TC: Device %s, class %s, IGNORE (isleaf: %d, hasparent: %d, parent: %s)", d->name, c->id, c->isleaf, c->hasparent, c->parentid);
+ }
+ */
+
+ // we need at least a class
+ for(c = d->classes ; c ; c = c->next) {
+ // debug(D_TC_LOOP, "TC: Device '%s', class '%s', isLeaf=%d, HasParent=%d, Seen=%d", d->name?d->name:d->id, c->name?c->name:c->id, c->isleaf, c->hasparent, c->seen);
+ if(!c->updated) continue;
+ if(c->isleaf && c->hasparent) {
+ active_classes++;
+ bytes_sum += c->bytes;
+ packets_sum += c->packets;
+ dropped_sum += c->dropped;
+ }
+ }
+
+ if(unlikely(!active_classes)) {
+ debug(D_TC_LOOP, "TC: Ignoring TC device '%s'. No leaf classes.", d->name?d->name:d->id);
+ tc_device_classes_cleanup(d);
+ return;
+ }
+
+ if(unlikely(d->enabled == -1)) {
+ char var_name[CONFIG_MAX_NAME + 1];
+ snprintfz(var_name, CONFIG_MAX_NAME, "qos for %s", d->id);
+ d->enabled = config_get_boolean_ondemand("plugin:tc", var_name, enable_new_interfaces);
+
+ snprintfz(var_name, CONFIG_MAX_NAME, "traffic chart for %s", d->id);
+ d->enabled_bytes = config_get_boolean_ondemand("plugin:tc", var_name, enable_bytes);
+
+ snprintfz(var_name, CONFIG_MAX_NAME, "packets chart for %s", d->id);
+ d->enabled_packets = config_get_boolean_ondemand("plugin:tc", var_name, enable_packets);
+
+ snprintfz(var_name, CONFIG_MAX_NAME, "dropped packets chart for %s", d->id);
+ d->enabled_dropped = config_get_boolean_ondemand("plugin:tc", var_name, enable_dropped);
+ }
+
+ if(likely(d->enabled)) {
+ // --------------------------------------------------------------------
+ // bytes
+
+ if(d->enabled_bytes == CONFIG_ONDEMAND_YES || (d->enabled_bytes == CONFIG_ONDEMAND_ONDEMAND && bytes_sum)) {
+ d->enabled_bytes = CONFIG_ONDEMAND_YES;
+
+ if(unlikely(!d->st_bytes)) {
+ d->st_bytes = rrdset_find_bytype(RRD_TYPE_TC, d->id);
+ if(unlikely(!d->st_bytes)) {
+ debug(D_TC_LOOP, "TC: Creating new chart for device '%s'", d->name?d->name:d->id);
+ d->st_bytes = rrdset_create(RRD_TYPE_TC, d->id, d->name?d->name:d->id, d->family?d->family:d->id, RRD_TYPE_TC ".qos", "Class Usage", "kilobits/s", 7000, rrd_update_every, RRDSET_TYPE_STACKED);
+ }
+ }
+ else {
+ debug(D_TC_LOOP, "TC: Updating chart for device '%s'", d->name?d->name:d->id);
+ rrdset_next_plugins(d->st_bytes);
+
+ if(unlikely(d->name_updated && d->name && strcmp(d->id, d->name) != 0)) {
+ rrdset_set_name(d->st_bytes, d->name);
+ d->name_updated = 0;
+ }
+
+ // FIXME
+ // update the family
+ }
+
+ for(c = d->classes ; c ; c = c->next) {
+ if(unlikely(!c->updated)) continue;
+
+ if(c->isleaf && c->hasparent) {
+ c->seen++;
+
+ if(unlikely(!c->rd_bytes)) {
+ c->rd_bytes = rrddim_find(d->st_bytes, c->id);
+ if(unlikely(!c->rd_bytes)) {
+ debug(D_TC_LOOP, "TC: Adding to chart '%s', dimension '%s' (name: '%s')", d->st_bytes->id, c->id, c->name);
+
+ // new class, we have to add it
+ c->rd_bytes = rrddim_add(d->st_bytes, c->id, c->name?c->name:c->id, 8, 1024, RRDDIM_INCREMENTAL);
+ }
+ else debug(D_TC_LOOP, "TC: Updating chart '%s', dimension '%s'", d->st_bytes->id, c->id);
+ }
+
+ rrddim_set_by_pointer(d->st_bytes, c->rd_bytes, c->bytes);
+
+ // if it has a name, different to the id
+ if(unlikely(c->name_updated && c->name && strcmp(c->id, c->name) != 0)) {
+ // update the rrd dimension with the new name
+ debug(D_TC_LOOP, "TC: Setting chart '%s', dimension '%s' name to '%s'", d->st_bytes->id, c->rd_bytes->id, c->name);
+ rrddim_set_name(d->st_bytes, c->rd_bytes, c->name);
+ }
+ }
+ }
+ rrdset_done(d->st_bytes);
+ }
+
+ // --------------------------------------------------------------------
+ // packets
+
+ if(d->enabled_packets == CONFIG_ONDEMAND_YES || (d->enabled_packets == CONFIG_ONDEMAND_ONDEMAND && packets_sum)) {
+ d->enabled_packets = CONFIG_ONDEMAND_YES;
+
+ if(unlikely(!d->st_packets)) {
+ char id[RRD_ID_LENGTH_MAX + 1];
+ char name[RRD_ID_LENGTH_MAX + 1];
+ snprintfz(id, RRD_ID_LENGTH_MAX, "%s_packets", d->id);
+ snprintfz(name, RRD_ID_LENGTH_MAX, "%s_packets", d->name?d->name:d->id);
+
+ d->st_packets = rrdset_find_bytype(RRD_TYPE_TC, id);
+ if(unlikely(!d->st_packets)) {
+ debug(D_TC_LOOP, "TC: Creating new _packets chart for device '%s'", d->name?d->name:d->id);
+ d->st_packets = rrdset_create(RRD_TYPE_TC, id, name, d->family?d->family:d->id, RRD_TYPE_TC ".qos_packets", "Class Packets", "packets/s", 7010, rrd_update_every, RRDSET_TYPE_STACKED);
+ }
+ }
+ else {
+ debug(D_TC_LOOP, "TC: Updating _packets chart for device '%s'", d->name?d->name:d->id);
+ rrdset_next_plugins(d->st_packets);
+
+ // FIXME
+ // update the family
+ }
+
+ for(c = d->classes ; c ; c = c->next) {
+ if(unlikely(!c->updated)) continue;
+
+ if(c->isleaf && c->hasparent) {
+ if(unlikely(!c->rd_packets)) {
+ c->rd_packets = rrddim_find(d->st_packets, c->id);
+ if(unlikely(!c->rd_packets)) {
+ debug(D_TC_LOOP, "TC: Adding to chart '%s', dimension '%s' (name: '%s')", d->st_packets->id, c->id, c->name);
+
+ // new class, we have to add it
+ c->rd_packets = rrddim_add(d->st_packets, c->id, c->name?c->name:c->id, 1, 1, RRDDIM_INCREMENTAL);
+ }
+ else debug(D_TC_LOOP, "TC: Updating chart '%s', dimension '%s'", d->st_packets->id, c->id);
+ }
+
+ rrddim_set_by_pointer(d->st_packets, c->rd_packets, c->packets);
+
+ // if it has a name, different to the id
+ if(unlikely(c->name_updated && c->name && strcmp(c->id, c->name) != 0)) {
+ // update the rrd dimension with the new name
+ debug(D_TC_LOOP, "TC: Setting chart '%s', dimension '%s' name to '%s'", d->st_packets->id, c->rd_packets->id, c->name);
+ rrddim_set_name(d->st_packets, c->rd_packets, c->name);
+ }
+ }
+ }
+ rrdset_done(d->st_packets);
+ }
+
+ // --------------------------------------------------------------------
+ // dropped
+
+ if(d->enabled_dropped == CONFIG_ONDEMAND_YES || (d->enabled_dropped == CONFIG_ONDEMAND_ONDEMAND && dropped_sum)) {
+ d->enabled_dropped = CONFIG_ONDEMAND_YES;
+
+ if(unlikely(!d->st_dropped)) {
+ char id[RRD_ID_LENGTH_MAX + 1];
+ char name[RRD_ID_LENGTH_MAX + 1];
+ snprintfz(id, RRD_ID_LENGTH_MAX, "%s_dropped", d->id);
+ snprintfz(name, RRD_ID_LENGTH_MAX, "%s_dropped", d->name?d->name:d->id);
+
+ d->st_dropped = rrdset_find_bytype(RRD_TYPE_TC, id);
+ if(unlikely(!d->st_dropped)) {
+ debug(D_TC_LOOP, "TC: Creating new _dropped chart for device '%s'", d->name?d->name:d->id);
+ d->st_dropped = rrdset_create(RRD_TYPE_TC, id, name, d->family?d->family:d->id, RRD_TYPE_TC ".qos_dropped", "Class Dropped Packets", "packets/s", 7020, rrd_update_every, RRDSET_TYPE_STACKED);
+ }
+ }
+ else {
+ debug(D_TC_LOOP, "TC: Updating _dropped chart for device '%s'", d->name?d->name:d->id);
+ rrdset_next_plugins(d->st_dropped);
+
+ // FIXME
+ // update the family
+ }
+
+ for(c = d->classes ; c ; c = c->next) {
+ if(unlikely(!c->updated)) continue;
+
+ if(c->isleaf && c->hasparent) {
+ if(unlikely(!c->rd_dropped)) {
+ c->rd_dropped = rrddim_find(d->st_dropped, c->id);
+ if(unlikely(!c->rd_dropped)) {
+ debug(D_TC_LOOP, "TC: Adding to chart '%s', dimension '%s' (name: '%s')", d->st_dropped->id, c->id, c->name);
+
+ // new class, we have to add it
+ c->rd_dropped = rrddim_add(d->st_dropped, c->id, c->name?c->name:c->id, 1, 1, RRDDIM_INCREMENTAL);
+ }
+ else debug(D_TC_LOOP, "TC: Updating chart '%s', dimension '%s'", d->st_dropped->id, c->id);
+ }
+
+ rrddim_set_by_pointer(d->st_dropped, c->rd_dropped, c->dropped);
+
+ // if it has a name, different to the id
+ if(unlikely(c->name_updated && c->name && strcmp(c->id, c->name) != 0)) {
+ // update the rrd dimension with the new name
+ debug(D_TC_LOOP, "TC: Setting chart '%s', dimension '%s' name to '%s'", d->st_dropped->id, c->rd_dropped->id, c->name);
+ rrddim_set_name(d->st_dropped, c->rd_dropped, c->name);
+ }
+ }
+ }
+ rrdset_done(d->st_dropped);
+ }
+ }
+
+ tc_device_classes_cleanup(d);
}
static inline void tc_device_set_class_name(struct tc_device *d, char *id, char *name)
{
- struct tc_class *c = tc_class_index_find(d, id, 0);
- if(c) {
- if(c->name) free(c->name);
- c->name = NULL;
-
- if(name && *name && strcmp(c->id, name) != 0) {
- debug(D_TC_LOOP, "TC: Setting device '%s', class '%s' name to '%s'", d->id, id, name);
- c->name = strdup(name);
- }
- }
+ struct tc_class *c = tc_class_index_find(d, id, 0);
+ if(likely(c)) {
+ freez(c->name);
+ c->name = NULL;
+
+ if(likely(name && *name && strcmp(c->id, name) != 0)) {
+ debug(D_TC_LOOP, "TC: Setting device '%s', class '%s' name to '%s'", d->id, id, name);
+ c->name = strdupz(name);
+ c->name_updated = 1;
+ }
+ }
}
static inline void tc_device_set_device_name(struct tc_device *d, char *name) {
- if(d->name) free(d->name);
- d->name = NULL;
-
- if(name && *name && strcmp(d->id, name) != 0) {
- debug(D_TC_LOOP, "TC: Setting device '%s' name to '%s'", d->id, name);
- d->name = strdup(name);
- }
+ freez(d->name);
+ d->name = NULL;
+
+ if(likely(name && *name && strcmp(d->id, name) != 0)) {
+ debug(D_TC_LOOP, "TC: Setting device '%s' name to '%s'", d->id, name);
+ d->name = strdupz(name);
+ d->name_updated = 1;
+ }
}
static inline void tc_device_set_device_family(struct tc_device *d, char *family) {
- if(d->family) free(d->family);
- d->family = NULL;
-
- if(family && *family && strcmp(d->id, family) != 0) {
- debug(D_TC_LOOP, "TC: Setting device '%s' family to '%s'", d->id, family);
- d->family = strdup(family);
- }
- // no need for null termination - it is already null
+ freez(d->family);
+ d->family = NULL;
+
+ if(likely(family && *family && strcmp(d->id, family) != 0)) {
+ debug(D_TC_LOOP, "TC: Setting device '%s' family to '%s'", d->id, family);
+ d->family = strdupz(family);
+ d->family_updated = 1;
+ }
+ // no need for null termination - it is already null
}
static inline struct tc_device *tc_device_create(char *id)
{
- struct tc_device *d = tc_device_index_find(id, 0);
-
- if(!d) {
- debug(D_TC_LOOP, "TC: Creating device '%s'", id);
-
- d = calloc(1, sizeof(struct tc_device));
- if(!d) {
- fatal("Cannot allocate memory for tc_device %s", id);
- return NULL;
- }
-
- d->id = strdup(id);
- d->hash = simple_hash(d->id);
-
- avl_init(&d->classes_index, tc_class_compare);
- tc_device_index_add(d);
-
- if(!tc_device_root) {
- tc_device_root = d;
- }
- else {
- d->next = tc_device_root;
- tc_device_root->prev = d;
- tc_device_root = d;
- }
- }
-
- return(d);
+ struct tc_device *d = tc_device_index_find(id, 0);
+
+ if(!d) {
+ debug(D_TC_LOOP, "TC: Creating device '%s'", id);
+
+ d = callocz(1, sizeof(struct tc_device));
+
+ d->id = strdupz(id);
+ d->hash = simple_hash(d->id);
+ d->enabled = -1;
+
+ avl_init(&d->classes_index, tc_class_compare);
+ tc_device_index_add(d);
+
+ if(!tc_device_root) {
+ tc_device_root = d;
+ }
+ else {
+ d->next = tc_device_root;
+ tc_device_root->prev = d;
+ tc_device_root = d;
+ }
+ }
+
+ return(d);
}
static inline struct tc_class *tc_class_add(struct tc_device *n, char *id, char *parentid, char *leafid)
{
- struct tc_class *c = tc_class_index_find(n, id, 0);
+ struct tc_class *c = tc_class_index_find(n, id, 0);
- if(!c) {
- debug(D_TC_LOOP, "TC: Creating in device '%s', class id '%s', parentid '%s', leafid '%s'", n->id, id, parentid?parentid:"", leafid?leafid:"");
+ if(!c) {
+ debug(D_TC_LOOP, "TC: Creating in device '%s', class id '%s', parentid '%s', leafid '%s'", n->id, id, parentid?parentid:"", leafid?leafid:"");
- c = calloc(1, sizeof(struct tc_class));
- if(!c) {
- fatal("Cannot allocate memory for tc class");
- return NULL;
- }
+ c = callocz(1, sizeof(struct tc_class));
- if(n->classes) n->classes->prev = c;
- c->next = n->classes;
- n->classes = c;
+ if(n->classes) n->classes->prev = c;
+ c->next = n->classes;
+ n->classes = c;
- c->id = strdup(id);
- if(!c->id) {
- free(c);
- return NULL;
- }
- c->hash = simple_hash(c->id);
+ c->id = strdupz(id);
+ c->hash = simple_hash(c->id);
- if(parentid && *parentid) {
- c->parentid = strdup(parentid);
- c->parent_hash = simple_hash(c->parentid);
- }
+ if(parentid && *parentid) {
+ c->parentid = strdupz(parentid);
+ c->parent_hash = simple_hash(c->parentid);
+ }
- if(leafid && *leafid) {
- c->leafid = strdup(leafid);
- c->leaf_hash = simple_hash(c->leafid);
- }
+ if(leafid && *leafid) {
+ c->leafid = strdupz(leafid);
+ c->leaf_hash = simple_hash(c->leafid);
+ }
- tc_class_index_add(n, c);
- }
+ tc_class_index_add(n, c);
+ }
- c->seen = 1;
+ c->seen = 1;
- return(c);
+ return(c);
}
static inline void tc_device_free(struct tc_device *n)
{
- if(n->next) n->next->prev = n->prev;
- if(n->prev) n->prev->next = n->next;
- if(tc_device_root == n) {
- if(n->next) tc_device_root = n->next;
- else tc_device_root = n->prev;
- }
+ if(n->next) n->next->prev = n->prev;
+ if(n->prev) n->prev->next = n->next;
+ if(tc_device_root == n) {
+ if(n->next) tc_device_root = n->next;
+ else tc_device_root = n->prev;
+ }
- tc_device_index_del(n);
+ tc_device_index_del(n);
- while(n->classes) tc_class_free(n, n->classes);
+ while(n->classes) tc_class_free(n, n->classes);
- if(n->id) free(n->id);
- if(n->name) free(n->name);
- if(n->family) free(n->family);
-
- free(n);
+ freez(n->id);
+ freez(n->name);
+ freez(n->family);
+ freez(n);
}
static inline void tc_device_free_all()
{
- while(tc_device_root)
- tc_device_free(tc_device_root);
+ while(tc_device_root)
+ tc_device_free(tc_device_root);
}
#define MAX_WORDS 20
static inline int tc_space(char c) {
- switch(c) {
- case ' ':
- case '\t':
- case '\r':
- case '\n':
- return 1;
-
- default:
- return 0;
- }
+ switch(c) {
+ case ' ':
+ case '\t':
+ case '\r':
+ case '\n':
+ return 1;
+
+ default:
+ return 0;
+ }
}
static inline void tc_split_words(char *str, char **words, int max_words) {
- char *s = str;
- int i = 0;
+ char *s = str;
+ int i = 0;
- // skip all white space
- while(tc_space(*s)) s++;
+ // skip all white space
+ while(tc_space(*s)) s++;
- // store the first word
- words[i++] = s;
+ // store the first word
+ words[i++] = s;
- // while we have something
- while(*s) {
- // if it is a space
- if(tc_space(*s)) {
+ // while we have something
+ while(*s) {
+ // if it is a space
+ if(unlikely(tc_space(*s))) {
- // terminate the word
- *s++ = '\0';
+ // terminate the word
+ *s++ = '\0';
- // skip all white space
- while(tc_space(*s)) s++;
+ // skip all white space
+ while(tc_space(*s)) s++;
- // if we reached the end, stop
- if(!*s) break;
+ // if we reached the end, stop
+ if(!*s) break;
- // store the next word
- if(i < max_words) words[i++] = s;
- else break;
- }
- else s++;
- }
+ // store the next word
+ if(i < max_words) words[i++] = s;
+ else break;
+ }
+ else s++;
+ }
- // terminate the words
- while(i < max_words) words[i++] = NULL;
+ // terminate the words
+ while(i < max_words) words[i++] = NULL;
}
pid_t tc_child_pid = 0;
-void *tc_main(void *ptr)
-{
- if(ptr) { ; }
+void *tc_main(void *ptr) {
+ (void)ptr;
- info("TC thread created with task id %d", gettid());
+ info("TC thread created with task id %d", gettid());
- if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
- error("Cannot set pthread cancel type to DEFERRED.");
+ if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
+ error("Cannot set pthread cancel type to DEFERRED.");
- if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
- error("Cannot set pthread cancel state to ENABLE.");
+ if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
+ error("Cannot set pthread cancel state to ENABLE.");
- struct rusage thread;
- RRDSET *stcpu = NULL, *sttime = NULL;
+ struct rusage thread;
+ RRDSET *stcpu = NULL, *sttime = NULL;
- char buffer[TC_LINE_MAX+1] = "";
- char *words[MAX_WORDS] = { NULL };
+ char buffer[TC_LINE_MAX+1] = "";
+ char *words[MAX_WORDS] = { NULL };
- uint32_t BEGIN_HASH = simple_hash("BEGIN");
- uint32_t END_HASH = simple_hash("END");
- uint32_t CLASS_HASH = simple_hash("class");
- uint32_t SENT_HASH = simple_hash("Sent");
- uint32_t LENDED_HASH = simple_hash("lended:");
- uint32_t TOKENS_HASH = simple_hash("tokens:");
- uint32_t SETDEVICENAME_HASH = simple_hash("SETDEVICENAME");
- uint32_t SETDEVICEGROUP_HASH = simple_hash("SETDEVICEGROUP");
- uint32_t SETCLASSNAME_HASH = simple_hash("SETCLASSNAME");
- uint32_t WORKTIME_HASH = simple_hash("WORKTIME");
+ uint32_t BEGIN_HASH = simple_hash("BEGIN");
+ uint32_t END_HASH = simple_hash("END");
+ uint32_t CLASS_HASH = simple_hash("class");
+ uint32_t SENT_HASH = simple_hash("Sent");
+ uint32_t LENDED_HASH = simple_hash("lended:");
+ uint32_t TOKENS_HASH = simple_hash("tokens:");
+ uint32_t SETDEVICENAME_HASH = simple_hash("SETDEVICENAME");
+ uint32_t SETDEVICEGROUP_HASH = simple_hash("SETDEVICEGROUP");
+ uint32_t SETCLASSNAME_HASH = simple_hash("SETCLASSNAME");
+ uint32_t WORKTIME_HASH = simple_hash("WORKTIME");
#ifdef DETACH_PLUGINS_FROM_NETDATA
- uint32_t MYPID_HASH = simple_hash("MYPID");
+ uint32_t MYPID_HASH = simple_hash("MYPID");
#endif
- uint32_t first_hash;
-
- for(;1;) {
- if(unlikely(netdata_exit)) break;
-
- FILE *fp;
- struct tc_device *device = NULL;
- struct tc_class *class = NULL;
-
- snprintfz(buffer, TC_LINE_MAX, "exec %s %d", config_get("plugin:tc", "script to run to get tc values", PLUGINS_DIR "/tc-qos-helper.sh"), rrd_update_every);
- debug(D_TC_LOOP, "executing '%s'", buffer);
- // fp = popen(buffer, "r");
- fp = mypopen(buffer, &tc_child_pid);
- if(!fp) {
- error("TC: Cannot popen(\"%s\", \"r\").", buffer);
- pthread_exit(NULL);
- return NULL;
- }
-
- while(fgets(buffer, TC_LINE_MAX, fp) != NULL) {
- if(unlikely(netdata_exit)) break;
-
- buffer[TC_LINE_MAX] = '\0';
- // debug(D_TC_LOOP, "TC: read '%s'", buffer);
-
- tc_split_words(buffer, words, MAX_WORDS);
- if(!words[0] || !*words[0]) {
- // debug(D_TC_LOOP, "empty line");
- continue;
- }
- // else debug(D_TC_LOOP, "First word is '%s'", words[0]);
-
- first_hash = simple_hash(words[0]);
-
- if(device && first_hash == CLASS_HASH && strcmp(words[0], "class") == 0) {
- // debug(D_TC_LOOP, "CLASS line on class id='%s', parent='%s', parentid='%s', leaf='%s', leafid='%s'", words[2], words[3], words[4], words[5], words[6]);
-
- // clear the last class
- class = NULL;
-
- // words[1] : class type
- // words[2] : N:XX
- // words[3] : parent or root
- if(words[1] && words[2] && words[3] && (strcmp(words[3], "parent") == 0 || strcmp(words[3], "root") == 0)) {
- //char *type = words[1]; // the class: htb, fq_codel, etc
-
- // we are only interested for HTB classes
- //if(strcmp(type, "htb") != 0) continue;
-
- char *id = words[2]; // the class major:minor
- char *parent = words[3]; // 'parent' or 'root'
- char *parentid = words[4]; // the parent's id
- char *leaf = words[5]; // 'leaf'
- char *leafid = words[6]; // leafid
-
- if(strcmp(parent, "root") == 0) {
- parentid = NULL;
- leafid = NULL;
- }
- else if(!leaf || strcmp(leaf, "leaf") != 0)
- leafid = NULL;
-
- char leafbuf[20 + 1] = "";
- if(leafid && leafid[strlen(leafid) - 1] == ':') {
- strncpyz(leafbuf, leafid, 20 - 1);
- strcat(leafbuf, "1");
- leafid = leafbuf;
- }
-
- class = tc_class_add(device, id, parentid, leafid);
- }
- }
- else if(first_hash == END_HASH && strcmp(words[0], "END") == 0) {
- // debug(D_TC_LOOP, "END line");
-
- if(device) {
- if(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL) != 0)
- error("Cannot set pthread cancel state to DISABLE.");
-
- tc_device_commit(device);
- // tc_device_free(device);
- device = NULL;
- class = NULL;
-
- if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
- error("Cannot set pthread cancel state to ENABLE.");
- }
- }
- else if(first_hash == BEGIN_HASH && strcmp(words[0], "BEGIN") == 0) {
- // debug(D_TC_LOOP, "BEGIN line on device '%s'", words[1]);
-
- if(device) {
- // tc_device_free(device);
- device = NULL;
- class = NULL;
- }
-
- if(words[1] && *words[1]) {
- device = tc_device_create(words[1]);
- class = NULL;
- }
- }
- else if(device && class && first_hash == SENT_HASH && strcmp(words[0], "Sent") == 0) {
- // debug(D_TC_LOOP, "SENT line '%s'", words[1]);
- if(words[1] && *words[1]) {
- class->bytes = strtoull(words[1], NULL, 10);
- class->updated = 1;
- }
-
- if(words[3] && *words[3])
- class->packets = strtoull(words[3], NULL, 10);
-
- if(words[6] && *words[6])
- class->dropped = strtoull(words[6], NULL, 10);
-
- if(words[8] && *words[8])
- class->overlimits = strtoull(words[8], NULL, 10);
-
- if(words[10] && *words[10])
- class->requeues = strtoull(words[8], NULL, 10);
- }
- else if(device && class && class->updated && first_hash == LENDED_HASH && strcmp(words[0], "lended:") == 0) {
- // debug(D_TC_LOOP, "LENDED line '%s'", words[1]);
- if(words[1] && *words[1])
- class->lended = strtoull(words[1], NULL, 10);
-
- if(words[3] && *words[3])
- class->borrowed = strtoull(words[3], NULL, 10);
-
- if(words[5] && *words[5])
- class->giants = strtoull(words[5], NULL, 10);
- }
- else if(device && class && class->updated && first_hash == TOKENS_HASH && strcmp(words[0], "tokens:") == 0) {
- // debug(D_TC_LOOP, "TOKENS line '%s'", words[1]);
- if(words[1] && *words[1])
- class->tokens = strtoull(words[1], NULL, 10);
-
- if(words[3] && *words[3])
- class->ctokens = strtoull(words[3], NULL, 10);
- }
- else if(device && first_hash == SETDEVICENAME_HASH && strcmp(words[0], "SETDEVICENAME") == 0) {
- // debug(D_TC_LOOP, "SETDEVICENAME line '%s'", words[1]);
- if(words[1] && *words[1]) tc_device_set_device_name(device, words[1]);
- }
- else if(device && first_hash == SETDEVICEGROUP_HASH && strcmp(words[0], "SETDEVICEGROUP") == 0) {
- // debug(D_TC_LOOP, "SETDEVICEGROUP line '%s'", words[1]);
- if(words[1] && *words[1]) tc_device_set_device_family(device, words[1]);
- }
- else if(device && first_hash == SETCLASSNAME_HASH && strcmp(words[0], "SETCLASSNAME") == 0) {
- // debug(D_TC_LOOP, "SETCLASSNAME line '%s' '%s'", words[1], words[2]);
- char *id = words[1];
- char *path = words[2];
- if(id && *id && path && *path) tc_device_set_class_name(device, id, path);
- }
- else if(first_hash == WORKTIME_HASH && strcmp(words[0], "WORKTIME") == 0) {
- // debug(D_TC_LOOP, "WORKTIME line '%s' '%s'", words[1], words[2]);
- getrusage(RUSAGE_THREAD, &thread);
-
- if(!stcpu) stcpu = rrdset_find("netdata.plugin_tc_cpu");
- if(!stcpu) {
- stcpu = rrdset_create("netdata", "plugin_tc_cpu", NULL, "tc.helper", NULL, "NetData TC CPU usage", "milliseconds/s", 135000, rrd_update_every, RRDSET_TYPE_STACKED);
- rrddim_add(stcpu, "user", NULL, 1, 1000, RRDDIM_INCREMENTAL);
- rrddim_add(stcpu, "system", NULL, 1, 1000, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(stcpu);
-
- rrddim_set(stcpu, "user" , thread.ru_utime.tv_sec * 1000000ULL + thread.ru_utime.tv_usec);
- rrddim_set(stcpu, "system", thread.ru_stime.tv_sec * 1000000ULL + thread.ru_stime.tv_usec);
- rrdset_done(stcpu);
-
- if(!sttime) stcpu = rrdset_find("netdata.plugin_tc_time");
- if(!sttime) {
- sttime = rrdset_create("netdata", "plugin_tc_time", NULL, "tc.helper", NULL, "NetData TC script execution", "milliseconds/run", 135001, rrd_update_every, RRDSET_TYPE_AREA);
- rrddim_add(sttime, "run_time", "run time", 1, 1, RRDDIM_ABSOLUTE);
- }
- else rrdset_next(sttime);
-
- rrddim_set(sttime, "run_time", atoll(words[1]));
- rrdset_done(sttime);
-
- }
+ uint32_t first_hash;
+
+ snprintfz(buffer, TC_LINE_MAX, "%s/tc-qos-helper.sh", config_get("plugins", "plugins directory", PLUGINS_DIR));
+ char *tc_script = config_get("plugin:tc", "script to run to get tc values", buffer);
+
+ for(;1;) {
+ if(unlikely(netdata_exit)) break;
+
+ FILE *fp;
+ struct tc_device *device = NULL;
+ struct tc_class *class = NULL;
+
+ snprintfz(buffer, TC_LINE_MAX, "exec %s %d", tc_script, rrd_update_every);
+ debug(D_TC_LOOP, "executing '%s'", buffer);
+
+ fp = mypopen(buffer, &tc_child_pid);
+ if(unlikely(!fp)) {
+ error("TC: Cannot popen(\"%s\", \"r\").", buffer);
+ pthread_exit(NULL);
+ return NULL;
+ }
+
+ while(fgets(buffer, TC_LINE_MAX, fp) != NULL) {
+ if(unlikely(netdata_exit)) break;
+
+ buffer[TC_LINE_MAX] = '\0';
+ // debug(D_TC_LOOP, "TC: read '%s'", buffer);
+
+ tc_split_words(buffer, words, MAX_WORDS);
+
+ if(unlikely(!words[0] || !*words[0])) {
+ // debug(D_TC_LOOP, "empty line");
+ continue;
+ }
+ // else debug(D_TC_LOOP, "First word is '%s'", words[0]);
+
+ first_hash = simple_hash(words[0]);
+
+ if(unlikely(device && first_hash == CLASS_HASH && strcmp(words[0], "class") == 0)) {
+ // debug(D_TC_LOOP, "CLASS line on class id='%s', parent='%s', parentid='%s', leaf='%s', leafid='%s'", words[2], words[3], words[4], words[5], words[6]);
+
+ // words[1] : class type
+ // words[2] : N:XX
+ // words[3] : parent or root
+ if(likely(words[1] && words[2] && words[3] && (strcmp(words[3], "parent") == 0 || strcmp(words[3], "root") == 0))) {
+ //char *type = words[1]; // the class: htb, fq_codel, etc
+
+ // we are only interested for HTB classes
+ //if(strcmp(type, "htb") != 0) continue;
+
+ char *id = words[2]; // the class major:minor
+ char *parent = words[3]; // 'parent' or 'root'
+ char *parentid = words[4]; // the parent's id
+ char *leaf = words[5]; // 'leaf'
+ char *leafid = words[6]; // leafid
+
+ if(strcmp(parent, "root") == 0) {
+ parentid = NULL;
+ leafid = NULL;
+ }
+ else if(!leaf || strcmp(leaf, "leaf") != 0)
+ leafid = NULL;
+
+ char leafbuf[20 + 1] = "";
+ if(leafid && leafid[strlen(leafid) - 1] == ':') {
+ strncpyz(leafbuf, leafid, 20 - 1);
+ strcat(leafbuf, "1");
+ leafid = leafbuf;
+ }
+
+ class = tc_class_add(device, id, parentid, leafid);
+ }
+ else {
+ // clear the last class
+ class = NULL;
+ }
+ }
+ else if(unlikely(first_hash == END_HASH && strcmp(words[0], "END") == 0)) {
+ // debug(D_TC_LOOP, "END line");
+
+ if(likely(device)) {
+ if(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL) != 0)
+ error("Cannot set pthread cancel state to DISABLE.");
+
+ tc_device_commit(device);
+ // tc_device_free(device);
+
+ if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
+ error("Cannot set pthread cancel state to ENABLE.");
+ }
+
+ device = NULL;
+ class = NULL;
+ }
+ else if(unlikely(first_hash == BEGIN_HASH && strcmp(words[0], "BEGIN") == 0)) {
+ // debug(D_TC_LOOP, "BEGIN line on device '%s'", words[1]);
+
+ if(likely(words[1] && *words[1])) {
+ device = tc_device_create(words[1]);
+ }
+ else {
+ // tc_device_free(device);
+ device = NULL;
+ }
+
+ class = NULL;
+ }
+ else if(unlikely(device && class && first_hash == SENT_HASH && strcmp(words[0], "Sent") == 0)) {
+ // debug(D_TC_LOOP, "SENT line '%s'", words[1]);
+ if(likely(words[1] && *words[1])) {
+ class->bytes = strtoull(words[1], NULL, 10);
+ class->updated = 1;
+ }
+ else {
+ class->updated = 0;
+ }
+
+ if(likely(words[3] && *words[3]))
+ class->packets = strtoull(words[3], NULL, 10);
+
+ if(likely(words[6] && *words[6]))
+ class->dropped = strtoull(words[6], NULL, 10);
+
+ if(likely(words[8] && *words[8]))
+ class->overlimits = strtoull(words[8], NULL, 10);
+
+ if(likely(words[10] && *words[10]))
+ class->requeues = strtoull(words[8], NULL, 10);
+ }
+ else if(unlikely(device && class && class->updated && first_hash == LENDED_HASH && strcmp(words[0], "lended:") == 0)) {
+ // debug(D_TC_LOOP, "LENDED line '%s'", words[1]);
+ if(likely(words[1] && *words[1]))
+ class->lended = strtoull(words[1], NULL, 10);
+
+ if(likely(words[3] && *words[3]))
+ class->borrowed = strtoull(words[3], NULL, 10);
+
+ if(likely(words[5] && *words[5]))
+ class->giants = strtoull(words[5], NULL, 10);
+ }
+ else if(unlikely(device && class && class->updated && first_hash == TOKENS_HASH && strcmp(words[0], "tokens:") == 0)) {
+ // debug(D_TC_LOOP, "TOKENS line '%s'", words[1]);
+ if(likely(words[1] && *words[1]))
+ class->tokens = strtoull(words[1], NULL, 10);
+
+ if(likely(words[3] && *words[3]))
+ class->ctokens = strtoull(words[3], NULL, 10);
+ }
+ else if(unlikely(device && first_hash == SETDEVICENAME_HASH && strcmp(words[0], "SETDEVICENAME") == 0)) {
+ // debug(D_TC_LOOP, "SETDEVICENAME line '%s'", words[1]);
+ if(likely(words[1] && *words[1]))
+ tc_device_set_device_name(device, words[1]);
+ }
+ else if(unlikely(device && first_hash == SETDEVICEGROUP_HASH && strcmp(words[0], "SETDEVICEGROUP") == 0)) {
+ // debug(D_TC_LOOP, "SETDEVICEGROUP line '%s'", words[1]);
+ if(likely(words[1] && *words[1]))
+ tc_device_set_device_family(device, words[1]);
+ }
+ else if(unlikely(device && first_hash == SETCLASSNAME_HASH && strcmp(words[0], "SETCLASSNAME") == 0)) {
+ // debug(D_TC_LOOP, "SETCLASSNAME line '%s' '%s'", words[1], words[2]);
+ char *id = words[1];
+ char *path = words[2];
+ if(likely(id && *id && path && *path))
+ tc_device_set_class_name(device, id, path);
+ }
+ else if(unlikely(first_hash == WORKTIME_HASH && strcmp(words[0], "WORKTIME") == 0)) {
+ // debug(D_TC_LOOP, "WORKTIME line '%s' '%s'", words[1], words[2]);
+ getrusage(RUSAGE_THREAD, &thread);
+
+ if(unlikely(!stcpu)) stcpu = rrdset_find("netdata.plugin_tc_cpu");
+ if(unlikely(!stcpu)) {
+ stcpu = rrdset_create("netdata", "plugin_tc_cpu", NULL, "tc.helper", NULL, "NetData TC CPU usage", "milliseconds/s", 135000, rrd_update_every, RRDSET_TYPE_STACKED);
+ rrddim_add(stcpu, "user", NULL, 1, 1000, RRDDIM_INCREMENTAL);
+ rrddim_add(stcpu, "system", NULL, 1, 1000, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(stcpu);
+
+ rrddim_set(stcpu, "user" , thread.ru_utime.tv_sec * 1000000ULL + thread.ru_utime.tv_usec);
+ rrddim_set(stcpu, "system", thread.ru_stime.tv_sec * 1000000ULL + thread.ru_stime.tv_usec);
+ rrdset_done(stcpu);
+
+ if(unlikely(!sttime)) stcpu = rrdset_find("netdata.plugin_tc_time");
+ if(unlikely(!sttime)) {
+ sttime = rrdset_create("netdata", "plugin_tc_time", NULL, "tc.helper", NULL, "NetData TC script execution", "milliseconds/run", 135001, rrd_update_every, RRDSET_TYPE_AREA);
+ rrddim_add(sttime, "run_time", "run time", 1, 1, RRDDIM_ABSOLUTE);
+ }
+ else rrdset_next(sttime);
+
+ rrddim_set(sttime, "run_time", atoll(words[1]));
+ rrdset_done(sttime);
+
+ }
#ifdef DETACH_PLUGINS_FROM_NETDATA
- else if(first_hash == MYPID_HASH && (strcmp(words[0], "MYPID") == 0)) {
- // debug(D_TC_LOOP, "MYPID line '%s'", words[1]);
- char *id = words[1];
- pid_t pid = atol(id);
+ else if(unlikely(first_hash == MYPID_HASH && (strcmp(words[0], "MYPID") == 0))) {
+ // debug(D_TC_LOOP, "MYPID line '%s'", words[1]);
+ char *id = words[1];
+ pid_t pid = atol(id);
- if(pid) tc_child_pid = pid;
+ if(likely(pid)) tc_child_pid = pid;
- debug(D_TC_LOOP, "TC: Child PID is %d.", tc_child_pid);
- }
+ debug(D_TC_LOOP, "TC: Child PID is %d.", tc_child_pid);
+ }
#endif
- //else {
- // debug(D_TC_LOOP, "IGNORED line");
- //}
- }
- // fgets() failed or loop broke
- int code = mypclose(fp, tc_child_pid);
- tc_child_pid = 0;
-
- if(device) {
- // tc_device_free(device);
- device = NULL;
- class = NULL;
- }
-
- if(netdata_exit) {
- tc_device_free_all();
- pthread_exit(NULL);
- return NULL;
- }
-
- if(code == 1 || code == 127) {
- // 1 = DISABLE
- // 127 = cannot even run it
- error("TC: tc-qos-helper.sh exited with code %d. Disabling it.", code);
-
- tc_device_free_all();
- pthread_exit(NULL);
- return NULL;
- }
-
- sleep((unsigned int) rrd_update_every);
- }
-
- pthread_exit(NULL);
- return NULL;
+ //else {
+ // debug(D_TC_LOOP, "IGNORED line");
+ //}
+ }
+
+ // fgets() failed or loop broke
+ int code = mypclose(fp, tc_child_pid);
+ tc_child_pid = 0;
+
+ if(unlikely(device)) {
+ // tc_device_free(device);
+ device = NULL;
+ class = NULL;
+ }
+
+ if(unlikely(netdata_exit)) {
+ tc_device_free_all();
+ pthread_exit(NULL);
+ return NULL;
+ }
+
+ if(code == 1 || code == 127) {
+ // 1 = DISABLE
+ // 127 = cannot even run it
+ error("TC: tc-qos-helper.sh exited with code %d. Disabling it.", code);
+
+ tc_device_free_all();
+ pthread_exit(NULL);
+ return NULL;
+ }
+
+ sleep((unsigned int) rrd_update_every);
+ }
+
+ pthread_exit(NULL);
+ return NULL;
}
diff --git a/src/plugins_d.c b/src/plugins_d.c
index 0ccbd36e4..627cc90e5 100644
--- a/src/plugins_d.c
+++ b/src/plugins_d.c
@@ -1,531 +1,547 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <sys/types.h>
-#include <dirent.h>
-#include <pthread.h>
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <signal.h>
-
-#include "main.h"
#include "common.h"
-#include "appconfig.h"
-#include "log.h"
-#include "rrd.h"
-#include "popen.h"
-#include "plugins_d.h"
-#include "../config.h"
struct plugind *pluginsd_root = NULL;
#define MAX_WORDS 20
static inline int pluginsd_space(char c) {
- switch(c) {
- case ' ':
- case '\t':
- case '\r':
- case '\n':
- case '=':
- return 1;
-
- default:
- return 0;
- }
+ switch(c) {
+ case ' ':
+ case '\t':
+ case '\r':
+ case '\n':
+ case '=':
+ return 1;
+
+ default:
+ return 0;
+ }
}
static int pluginsd_split_words(char *str, char **words, int max_words) {
- char *s = str, quote = 0;
- int i = 0, j;
-
- // skip all white space
- while(unlikely(pluginsd_space(*s))) s++;
-
- // check for quote
- if(unlikely(*s == '\'' || *s == '"')) {
- quote = *s; // remember the quote
- s++; // skip the quote
- }
-
- // store the first word
- words[i++] = s;
-
- // while we have something
- while(likely(*s)) {
- // if it is escape
- if(unlikely(*s == '\\' && s[1])) {
- s += 2;
- continue;
- }
-
- // if it is quote
- else if(unlikely(*s == quote)) {
- quote = 0;
- *s = ' ';
- continue;
- }
-
- // if it is a space
- else if(unlikely(quote == 0 && pluginsd_space(*s))) {
-
- // terminate the word
- *s++ = '\0';
-
- // skip all white space
- while(likely(pluginsd_space(*s))) s++;
-
- // check for quote
- if(unlikely(*s == '\'' || *s == '"')) {
- quote = *s; // remember the quote
- s++; // skip the quote
- }
-
- // if we reached the end, stop
- if(unlikely(!*s)) break;
-
- // store the next word
- if(likely(i < max_words)) words[i++] = s;
- else break;
- }
-
- // anything else
- else s++;
- }
-
- // terminate the words
- j = i;
- while(likely(j < max_words)) words[j++] = NULL;
-
- return i;
+ char *s = str, quote = 0;
+ int i = 0, j;
+
+ // skip all white space
+ while(unlikely(pluginsd_space(*s))) s++;
+
+ // check for quote
+ if(unlikely(*s == '\'' || *s == '"')) {
+ quote = *s; // remember the quote
+ s++; // skip the quote
+ }
+
+ // store the first word
+ words[i++] = s;
+
+ // while we have something
+ while(likely(*s)) {
+ // if it is escape
+ if(unlikely(*s == '\\' && s[1])) {
+ s += 2;
+ continue;
+ }
+
+ // if it is quote
+ else if(unlikely(*s == quote)) {
+ quote = 0;
+ *s = ' ';
+ continue;
+ }
+
+ // if it is a space
+ else if(unlikely(quote == 0 && pluginsd_space(*s))) {
+
+ // terminate the word
+ *s++ = '\0';
+
+ // skip all white space
+ while(likely(pluginsd_space(*s))) s++;
+
+ // check for quote
+ if(unlikely(*s == '\'' || *s == '"')) {
+ quote = *s; // remember the quote
+ s++; // skip the quote
+ }
+
+ // if we reached the end, stop
+ if(unlikely(!*s)) break;
+
+ // store the next word
+ if(likely(i < max_words)) words[i++] = s;
+ else break;
+ }
+
+ // anything else
+ else s++;
+ }
+
+ // terminate the words
+ j = i;
+ while(likely(j < max_words)) words[j++] = NULL;
+
+ return i;
}
void *pluginsd_worker_thread(void *arg)
{
- struct plugind *cd = (struct plugind *)arg;
- char line[PLUGINSD_LINE_MAX + 1];
+ struct plugind *cd = (struct plugind *)arg;
+ char line[PLUGINSD_LINE_MAX + 1];
#ifdef DETACH_PLUGINS_FROM_NETDATA
- unsigned long long usec = 0, susec = 0;
- struct timeval last = {0, 0} , now = {0, 0};
+ unsigned long long usec = 0, susec = 0;
+ struct timeval last = {0, 0} , now = {0, 0};
#endif
- char *words[MAX_WORDS] = { NULL };
- uint32_t SET_HASH = simple_hash("SET");
- uint32_t BEGIN_HASH = simple_hash("BEGIN");
- uint32_t END_HASH = simple_hash("END");
- uint32_t FLUSH_HASH = simple_hash("FLUSH");
- uint32_t CHART_HASH = simple_hash("CHART");
- uint32_t DIMENSION_HASH = simple_hash("DIMENSION");
- uint32_t DISABLE_HASH = simple_hash("DISABLE");
+ char *words[MAX_WORDS] = { NULL };
+ uint32_t SET_HASH = simple_hash("SET");
+ uint32_t BEGIN_HASH = simple_hash("BEGIN");
+ uint32_t END_HASH = simple_hash("END");
+ uint32_t FLUSH_HASH = simple_hash("FLUSH");
+ uint32_t CHART_HASH = simple_hash("CHART");
+ uint32_t DIMENSION_HASH = simple_hash("DIMENSION");
+ uint32_t DISABLE_HASH = simple_hash("DISABLE");
#ifdef DETACH_PLUGINS_FROM_NETDATA
- uint32_t MYPID_HASH = simple_hash("MYPID");
- uint32_t STOPPING_WAKE_ME_UP_PLEASE_HASH = simple_hash("STOPPING_WAKE_ME_UP_PLEASE");
+ uint32_t MYPID_HASH = simple_hash("MYPID");
+ uint32_t STOPPING_WAKE_ME_UP_PLEASE_HASH = simple_hash("STOPPING_WAKE_ME_UP_PLEASE");
#endif
- while(likely(1)) {
- if(unlikely(netdata_exit)) break;
-
- FILE *fp = mypopen(cd->cmd, &cd->pid);
- if(unlikely(!fp)) {
- error("Cannot popen(\"%s\", \"r\").", cd->cmd);
- break;
- }
-
- info("PLUGINSD: '%s' running on pid %d", cd->fullfilename, cd->pid);
-
- RRDSET *st = NULL;
- unsigned long long count = 0;
- char *s;
- uint32_t hash;
-
- while(likely(fgets(line, PLUGINSD_LINE_MAX, fp) != NULL)) {
- if(unlikely(netdata_exit)) break;
-
- line[PLUGINSD_LINE_MAX] = '\0';
-
- // debug(D_PLUGINSD, "PLUGINSD: %s: %s", cd->filename, line);
-
- int w = pluginsd_split_words(line, words, MAX_WORDS);
- s = words[0];
- if(unlikely(!s || !*s || !w)) {
- // debug(D_PLUGINSD, "PLUGINSD: empty line");
- continue;
- }
-
- // debug(D_PLUGINSD, "PLUGINSD: words 0='%s' 1='%s' 2='%s' 3='%s' 4='%s' 5='%s' 6='%s' 7='%s' 8='%s' 9='%s'", words[0], words[1], words[2], words[3], words[4], words[5], words[6], words[7], words[8], words[9]);
-
- hash = simple_hash(s);
-
- if(likely(hash == SET_HASH && !strcmp(s, "SET"))) {
- char *dimension = words[1];
- char *value = words[2];
-
- if(unlikely(!dimension || !*dimension)) {
- error("PLUGINSD: '%s' is requesting a SET on chart '%s', without a dimension. Disabling it.", cd->fullfilename, st->id);
- cd->enabled = 0;
- killpid(cd->pid, SIGTERM);
- break;
- }
-
- if(unlikely(!value || !*value)) value = NULL;
-
- if(unlikely(!st)) {
- error("PLUGINSD: '%s' is requesting a SET on dimension %s with value %s, without a BEGIN. Disabling it.", cd->fullfilename, dimension, value?value:"<nothing>");
- cd->enabled = 0;
- killpid(cd->pid, SIGTERM);
- break;
- }
-
- if(unlikely(st->debug)) debug(D_PLUGINSD, "PLUGINSD: '%s' is setting dimension %s/%s to %s", cd->fullfilename, st->id, dimension, value?value:"<nothing>");
-
- if(value) rrddim_set(st, dimension, strtoll(value, NULL, 0));
-
- count++;
- }
- else if(likely(hash == BEGIN_HASH && !strcmp(s, "BEGIN"))) {
- char *id = words[1];
- char *microseconds_txt = words[2];
-
- if(unlikely(!id)) {
- error("PLUGINSD: '%s' is requesting a BEGIN without a chart id. Disabling it.", cd->fullfilename);
- cd->enabled = 0;
- killpid(cd->pid, SIGTERM);
- break;
- }
-
- st = rrdset_find(id);
- if(unlikely(!st)) {
- error("PLUGINSD: '%s' is requesting a BEGIN on chart '%s', which does not exist. Disabling it.", cd->fullfilename, id);
- cd->enabled = 0;
- killpid(cd->pid, SIGTERM);
- break;
- }
-
- if(likely(st->counter_done)) {
- unsigned long long microseconds = 0;
- if(microseconds_txt && *microseconds_txt) microseconds = strtoull(microseconds_txt, NULL, 10);
- if(microseconds) rrdset_next_usec(st, microseconds);
- else rrdset_next_plugins(st);
- }
- }
- else if(likely(hash == END_HASH && !strcmp(s, "END"))) {
- if(unlikely(!st)) {
- error("PLUGINSD: '%s' is requesting an END, without a BEGIN. Disabling it.", cd->fullfilename);
- cd->enabled = 0;
- killpid(cd->pid, SIGTERM);
- break;
- }
-
- if(unlikely(st->debug)) debug(D_PLUGINSD, "PLUGINSD: '%s' is requesting an END on chart %s", cd->fullfilename, st->id);
-
- rrdset_done(st);
- st = NULL;
- }
- else if(likely(hash == FLUSH_HASH && !strcmp(s, "FLUSH"))) {
- debug(D_PLUGINSD, "PLUGINSD: '%s' is requesting a FLUSH", cd->fullfilename);
- st = NULL;
- }
- else if(likely(hash == CHART_HASH && !strcmp(s, "CHART"))) {
- int noname = 0;
- st = NULL;
-
- if((words[1]) != NULL && (words[2]) != NULL && strcmp(words[1], words[2]) == 0)
- noname = 1;
-
- char *type = words[1];
- char *id = NULL;
- if(likely(type)) {
- id = strchr(type, '.');
- if(likely(id)) { *id = '\0'; id++; }
- }
- char *name = words[2];
- char *title = words[3];
- char *units = words[4];
- char *family = words[5];
- char *context = words[6];
- char *chart = words[7];
- char *priority_s = words[8];
- char *update_every_s = words[9];
-
- if(unlikely(!type || !*type || !id || !*id)) {
- error("PLUGINSD: '%s' is requesting a CHART, without a type.id. Disabling it.", cd->fullfilename);
- cd->enabled = 0;
- killpid(cd->pid, SIGTERM);
- break;
- }
-
- int priority = 1000;
- if(likely(priority_s)) priority = atoi(priority_s);
-
- int update_every = cd->update_every;
- if(likely(update_every_s)) update_every = atoi(update_every_s);
- if(unlikely(!update_every)) update_every = cd->update_every;
-
- int chart_type = RRDSET_TYPE_LINE;
- if(unlikely(chart)) chart_type = rrdset_type_id(chart);
-
- if(unlikely(noname || !name || !*name || strcasecmp(name, "NULL") == 0 || strcasecmp(name, "(NULL)") == 0)) name = NULL;
- if(unlikely(!family || !*family)) family = NULL;
- if(unlikely(!context || !*context)) context = NULL;
-
- st = rrdset_find_bytype(type, id);
- if(unlikely(!st)) {
- debug(D_PLUGINSD, "PLUGINSD: Creating chart type='%s', id='%s', name='%s', family='%s', context='%s', chart='%s', priority=%d, update_every=%d"
- , type, id
- , name?name:""
- , family?family:""
- , context?context:""
- , rrdset_type_name(chart_type)
- , priority
- , update_every
- );
-
- st = rrdset_create(type, id, name, family, context, title, units, priority, update_every, chart_type);
- cd->update_every = update_every;
- }
- else debug(D_PLUGINSD, "PLUGINSD: Chart '%s' already exists. Not adding it again.", st->id);
- }
- else if(likely(hash == DIMENSION_HASH && !strcmp(s, "DIMENSION"))) {
- char *id = words[1];
- char *name = words[2];
- char *algorithm = words[3];
- char *multiplier_s = words[4];
- char *divisor_s = words[5];
- char *options = words[6];
-
- if(unlikely(!id || !*id)) {
- error("PLUGINSD: '%s' is requesting a DIMENSION, without an id. Disabling it.", cd->fullfilename);
- cd->enabled = 0;
- killpid(cd->pid, SIGTERM);
- break;
- }
-
- if(unlikely(!st)) {
- error("PLUGINSD: '%s' is requesting a DIMENSION, without a CHART. Disabling it.", cd->fullfilename);
- cd->enabled = 0;
- killpid(cd->pid, SIGTERM);
- break;
- }
-
- long multiplier = 1;
- if(multiplier_s && *multiplier_s) multiplier = strtol(multiplier_s, NULL, 0);
- if(unlikely(!multiplier)) multiplier = 1;
-
- long divisor = 1;
- if(likely(divisor_s && *divisor_s)) divisor = strtol(divisor_s, NULL, 0);
- if(unlikely(!divisor)) divisor = 1;
-
- if(unlikely(!algorithm || !*algorithm)) algorithm = "absolute";
-
- if(unlikely(st->debug)) debug(D_PLUGINSD, "PLUGINSD: Creating dimension in chart %s, id='%s', name='%s', algorithm='%s', multiplier=%ld, divisor=%ld, hidden='%s'"
- , st->id
- , id
- , name?name:""
- , rrddim_algorithm_name(rrddim_algorithm_id(algorithm))
- , multiplier
- , divisor
- , options?options:""
- );
-
- RRDDIM *rd = rrddim_find(st, id);
- if(unlikely(!rd)) {
- rd = rrddim_add(st, id, name, multiplier, divisor, rrddim_algorithm_id(algorithm));
- rd->flags = 0x00000000;
- if(options && *options) {
- if(strstr(options, "hidden") != NULL) rd->flags |= RRDDIM_FLAG_HIDDEN;
- if(strstr(options, "noreset") != NULL) rd->flags |= RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS;
- if(strstr(options, "nooverflow") != NULL) rd->flags |= RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS;
- }
- }
- else if(unlikely(st->debug)) debug(D_PLUGINSD, "PLUGINSD: dimension %s/%s already exists. Not adding it again.", st->id, id);
- }
- else if(unlikely(hash == DISABLE_HASH && !strcmp(s, "DISABLE"))) {
- error("PLUGINSD: '%s' called DISABLE. Disabling it.", cd->fullfilename);
- cd->enabled = 0;
- killpid(cd->pid, SIGTERM);
- break;
- }
+ size_t count = 0;
+
+ while(likely(1)) {
+ if(unlikely(netdata_exit)) break;
+
+ FILE *fp = mypopen(cd->cmd, &cd->pid);
+ if(unlikely(!fp)) {
+ error("Cannot popen(\"%s\", \"r\").", cd->cmd);
+ break;
+ }
+
+ info("PLUGINSD: '%s' running on pid %d", cd->fullfilename, cd->pid);
+
+ RRDSET *st = NULL;
+ char *s;
+ uint32_t hash;
+
+ while(likely(fgets(line, PLUGINSD_LINE_MAX, fp) != NULL)) {
+ if(unlikely(netdata_exit)) break;
+
+ line[PLUGINSD_LINE_MAX] = '\0';
+
+ // debug(D_PLUGINSD, "PLUGINSD: %s: %s", cd->filename, line);
+
+ int w = pluginsd_split_words(line, words, MAX_WORDS);
+ s = words[0];
+ if(unlikely(!s || !*s || !w)) {
+ // debug(D_PLUGINSD, "PLUGINSD: empty line");
+ continue;
+ }
+
+ // debug(D_PLUGINSD, "PLUGINSD: words 0='%s' 1='%s' 2='%s' 3='%s' 4='%s' 5='%s' 6='%s' 7='%s' 8='%s' 9='%s'", words[0], words[1], words[2], words[3], words[4], words[5], words[6], words[7], words[8], words[9]);
+
+ hash = simple_hash(s);
+
+ if(likely(hash == SET_HASH && !strcmp(s, "SET"))) {
+ char *dimension = words[1];
+ char *value = words[2];
+
+ if(unlikely(!dimension || !*dimension)) {
+ error("PLUGINSD: '%s' is requesting a SET on chart '%s', without a dimension. Disabling it.", cd->fullfilename, st->id);
+ cd->enabled = 0;
+ killpid(cd->pid, SIGTERM);
+ break;
+ }
+
+ if(unlikely(!value || !*value)) value = NULL;
+
+ if(unlikely(!st)) {
+ error("PLUGINSD: '%s' is requesting a SET on dimension %s with value %s, without a BEGIN. Disabling it.", cd->fullfilename, dimension, value?value:"<nothing>");
+ cd->enabled = 0;
+ killpid(cd->pid, SIGTERM);
+ break;
+ }
+
+ if(unlikely(st->debug)) debug(D_PLUGINSD, "PLUGINSD: '%s' is setting dimension %s/%s to %s", cd->fullfilename, st->id, dimension, value?value:"<nothing>");
+
+ if(value) rrddim_set(st, dimension, strtoll(value, NULL, 0));
+ }
+ else if(likely(hash == BEGIN_HASH && !strcmp(s, "BEGIN"))) {
+ char *id = words[1];
+ char *microseconds_txt = words[2];
+
+ if(unlikely(!id)) {
+ error("PLUGINSD: '%s' is requesting a BEGIN without a chart id. Disabling it.", cd->fullfilename);
+ cd->enabled = 0;
+ killpid(cd->pid, SIGTERM);
+ break;
+ }
+
+ st = rrdset_find(id);
+ if(unlikely(!st)) {
+ error("PLUGINSD: '%s' is requesting a BEGIN on chart '%s', which does not exist. Disabling it.", cd->fullfilename, id);
+ cd->enabled = 0;
+ killpid(cd->pid, SIGTERM);
+ break;
+ }
+
+ if(likely(st->counter_done)) {
+ unsigned long long microseconds = 0;
+ if(microseconds_txt && *microseconds_txt) microseconds = strtoull(microseconds_txt, NULL, 10);
+ if(microseconds) rrdset_next_usec(st, microseconds);
+ else rrdset_next_plugins(st);
+ }
+ }
+ else if(likely(hash == END_HASH && !strcmp(s, "END"))) {
+ if(unlikely(!st)) {
+ error("PLUGINSD: '%s' is requesting an END, without a BEGIN. Disabling it.", cd->fullfilename);
+ cd->enabled = 0;
+ killpid(cd->pid, SIGTERM);
+ break;
+ }
+
+ if(unlikely(st->debug)) debug(D_PLUGINSD, "PLUGINSD: '%s' is requesting an END on chart %s", cd->fullfilename, st->id);
+
+ rrdset_done(st);
+ st = NULL;
+
+ count++;
+ }
+ else if(likely(hash == FLUSH_HASH && !strcmp(s, "FLUSH"))) {
+ debug(D_PLUGINSD, "PLUGINSD: '%s' is requesting a FLUSH", cd->fullfilename);
+ st = NULL;
+ }
+ else if(likely(hash == CHART_HASH && !strcmp(s, "CHART"))) {
+ int noname = 0;
+ st = NULL;
+
+ if((words[1]) != NULL && (words[2]) != NULL && strcmp(words[1], words[2]) == 0)
+ noname = 1;
+
+ char *type = words[1];
+ char *id = NULL;
+ if(likely(type)) {
+ id = strchr(type, '.');
+ if(likely(id)) { *id = '\0'; id++; }
+ }
+ char *name = words[2];
+ char *title = words[3];
+ char *units = words[4];
+ char *family = words[5];
+ char *context = words[6];
+ char *chart = words[7];
+ char *priority_s = words[8];
+ char *update_every_s = words[9];
+
+ if(unlikely(!type || !*type || !id || !*id)) {
+ error("PLUGINSD: '%s' is requesting a CHART, without a type.id. Disabling it.", cd->fullfilename);
+ cd->enabled = 0;
+ killpid(cd->pid, SIGTERM);
+ break;
+ }
+
+ int priority = 1000;
+ if(likely(priority_s)) priority = atoi(priority_s);
+
+ int update_every = cd->update_every;
+ if(likely(update_every_s)) update_every = atoi(update_every_s);
+ if(unlikely(!update_every)) update_every = cd->update_every;
+
+ int chart_type = RRDSET_TYPE_LINE;
+ if(unlikely(chart)) chart_type = rrdset_type_id(chart);
+
+ if(unlikely(noname || !name || !*name || strcasecmp(name, "NULL") == 0 || strcasecmp(name, "(NULL)") == 0)) name = NULL;
+ if(unlikely(!family || !*family)) family = NULL;
+ if(unlikely(!context || !*context)) context = NULL;
+
+ st = rrdset_find_bytype(type, id);
+ if(unlikely(!st)) {
+ debug(D_PLUGINSD, "PLUGINSD: Creating chart type='%s', id='%s', name='%s', family='%s', context='%s', chart='%s', priority=%d, update_every=%d"
+ , type, id
+ , name?name:""
+ , family?family:""
+ , context?context:""
+ , rrdset_type_name(chart_type)
+ , priority
+ , update_every
+ );
+
+ st = rrdset_create(type, id, name, family, context, title, units, priority, update_every, chart_type);
+ cd->update_every = update_every;
+ }
+ else debug(D_PLUGINSD, "PLUGINSD: Chart '%s' already exists. Not adding it again.", st->id);
+ }
+ else if(likely(hash == DIMENSION_HASH && !strcmp(s, "DIMENSION"))) {
+ char *id = words[1];
+ char *name = words[2];
+ char *algorithm = words[3];
+ char *multiplier_s = words[4];
+ char *divisor_s = words[5];
+ char *options = words[6];
+
+ if(unlikely(!id || !*id)) {
+ error("PLUGINSD: '%s' is requesting a DIMENSION, without an id. Disabling it.", cd->fullfilename);
+ cd->enabled = 0;
+ killpid(cd->pid, SIGTERM);
+ break;
+ }
+
+ if(unlikely(!st)) {
+ error("PLUGINSD: '%s' is requesting a DIMENSION, without a CHART. Disabling it.", cd->fullfilename);
+ cd->enabled = 0;
+ killpid(cd->pid, SIGTERM);
+ break;
+ }
+
+ long multiplier = 1;
+ if(multiplier_s && *multiplier_s) multiplier = strtol(multiplier_s, NULL, 0);
+ if(unlikely(!multiplier)) multiplier = 1;
+
+ long divisor = 1;
+ if(likely(divisor_s && *divisor_s)) divisor = strtol(divisor_s, NULL, 0);
+ if(unlikely(!divisor)) divisor = 1;
+
+ if(unlikely(!algorithm || !*algorithm)) algorithm = "absolute";
+
+ if(unlikely(st->debug)) debug(D_PLUGINSD, "PLUGINSD: Creating dimension in chart %s, id='%s', name='%s', algorithm='%s', multiplier=%ld, divisor=%ld, hidden='%s'"
+ , st->id
+ , id
+ , name?name:""
+ , rrddim_algorithm_name(rrddim_algorithm_id(algorithm))
+ , multiplier
+ , divisor
+ , options?options:""
+ );
+
+ RRDDIM *rd = rrddim_find(st, id);
+ if(unlikely(!rd)) {
+ rd = rrddim_add(st, id, name, multiplier, divisor, rrddim_algorithm_id(algorithm));
+ rd->flags = 0x00000000;
+ if(options && *options) {
+ if(strstr(options, "hidden") != NULL) rd->flags |= RRDDIM_FLAG_HIDDEN;
+ if(strstr(options, "noreset") != NULL) rd->flags |= RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS;
+ if(strstr(options, "nooverflow") != NULL) rd->flags |= RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS;
+ }
+ }
+ else if(unlikely(st->debug)) debug(D_PLUGINSD, "PLUGINSD: dimension %s/%s already exists. Not adding it again.", st->id, id);
+ }
+ else if(unlikely(hash == DISABLE_HASH && !strcmp(s, "DISABLE"))) {
+ error("PLUGINSD: '%s' called DISABLE. Disabling it.", cd->fullfilename);
+ cd->enabled = 0;
+ killpid(cd->pid, SIGTERM);
+ break;
+ }
#ifdef DETACH_PLUGINS_FROM_NETDATA
- else if(likely(hash == MYPID_HASH && !strcmp(s, "MYPID"))) {
- char *pid_s = words[1];
- pid_t pid = strtod(pid_s, NULL, 0);
-
- if(likely(pid)) cd->pid = pid;
- debug(D_PLUGINSD, "PLUGINSD: %s is on pid %d", cd->id, cd->pid);
- }
- else if(likely(hash == STOPPING_WAKE_ME_UP_PLEASE_HASH && !strcmp(s, "STOPPING_WAKE_ME_UP_PLEASE"))) {
- error("PLUGINSD: '%s' (pid %d) called STOPPING_WAKE_ME_UP_PLEASE.", cd->fullfilename, cd->pid);
-
- gettimeofday(&now, NULL);
- if(unlikely(!usec && !susec)) {
- // our first run
- susec = cd->rrd_update_every * 1000000ULL;
- }
- else {
- // second+ run
- usec = usecdiff(&now, &last) - susec;
- error("PLUGINSD: %s last loop took %llu usec (worked for %llu, sleeped for %llu).\n", cd->fullfilename, usec + susec, usec, susec);
- if(unlikely(usec < (rrd_update_every * 1000000ULL / 2ULL))) susec = (rrd_update_every * 1000000ULL) - usec;
- else susec = rrd_update_every * 1000000ULL / 2ULL;
- }
-
- error("PLUGINSD: %s sleeping for %llu. Will kill with SIGCONT pid %d to wake it up.\n", cd->fullfilename, susec, cd->pid);
- usleep(susec);
- killpid(cd->pid, SIGCONT);
- bcopy(&now, &last, sizeof(struct timeval));
- break;
- }
+ else if(likely(hash == MYPID_HASH && !strcmp(s, "MYPID"))) {
+ char *pid_s = words[1];
+ pid_t pid = strtod(pid_s, NULL, 0);
+
+ if(likely(pid)) cd->pid = pid;
+ debug(D_PLUGINSD, "PLUGINSD: %s is on pid %d", cd->id, cd->pid);
+ }
+ else if(likely(hash == STOPPING_WAKE_ME_UP_PLEASE_HASH && !strcmp(s, "STOPPING_WAKE_ME_UP_PLEASE"))) {
+ error("PLUGINSD: '%s' (pid %d) called STOPPING_WAKE_ME_UP_PLEASE.", cd->fullfilename, cd->pid);
+
+ gettimeofday(&now, NULL);
+ if(unlikely(!usec && !susec)) {
+ // our first run
+ susec = cd->rrd_update_every * 1000000ULL;
+ }
+ else {
+ // second+ run
+ usec = usec_dt(&now, &last) - susec;
+ error("PLUGINSD: %s last loop took %llu usec (worked for %llu, sleeped for %llu).\n", cd->fullfilename, usec + susec, usec, susec);
+ if(unlikely(usec < (rrd_update_every * 1000000ULL / 2ULL))) susec = (rrd_update_every * 1000000ULL) - usec;
+ else susec = rrd_update_every * 1000000ULL / 2ULL;
+ }
+
+ error("PLUGINSD: %s sleeping for %llu. Will kill with SIGCONT pid %d to wake it up.\n", cd->fullfilename, susec, cd->pid);
+ usleep(susec);
+ killpid(cd->pid, SIGCONT);
+ bcopy(&now, &last, sizeof(struct timeval));
+ break;
+ }
#endif
- else {
- error("PLUGINSD: '%s' is sending command '%s' which is not known by netdata. Disabling it.", cd->fullfilename, s);
- cd->enabled = 0;
- killpid(cd->pid, SIGTERM);
- break;
- }
- }
-
- info("PLUGINSD: '%s' on pid %d stopped.", cd->fullfilename, cd->pid);
-
- // fgets() failed or loop broke
- int code = mypclose(fp, cd->pid);
- if(code == 1 || code == 127) {
- // 1 = DISABLE
- // 127 = cannot even run it
- error("PLUGINSD: '%s' (pid %d) exited with code %d. Disabling it.", cd->fullfilename, cd->pid, code);
- cd->enabled = 0;
- }
-
- if(netdata_exit) {
- cd->pid = 0;
- cd->enabled = 0;
- cd->obsolete = 1;
- pthread_exit(NULL);
- return NULL;
- }
-
- if(unlikely(!count && cd->enabled)) {
- error("PLUGINSD: '%s' (pid %d) does not generate useful output. Waiting a bit before starting it again.", cd->fullfilename, cd->pid);
- sleep((unsigned int) (cd->update_every * 10));
- }
-
- cd->pid = 0;
- if(likely(cd->enabled)) sleep((unsigned int) cd->update_every);
- else break;
- }
-
- cd->obsolete = 1;
- pthread_exit(NULL);
- return NULL;
+ else {
+ error("PLUGINSD: '%s' is sending command '%s' which is not known by netdata. Disabling it.", cd->fullfilename, s);
+ cd->enabled = 0;
+ killpid(cd->pid, SIGTERM);
+ break;
+ }
+ }
+ if(likely(count)) {
+ cd->successful_collections += count;
+ cd->serial_failures = 0;
+ }
+ else
+ cd->serial_failures++;
+
+ info("PLUGINSD: '%s' on pid %d stopped after %zu successful data collections (ENDs).", cd->fullfilename, cd->pid, count);
+
+ // get the return code
+ int code = mypclose(fp, cd->pid);
+
+ if(netdata_exit) {
+ cd->pid = 0;
+ cd->enabled = 0;
+ cd->obsolete = 1;
+ pthread_exit(NULL);
+ return NULL;
+ }
+
+ if(code != 0) {
+ // the plugin reports failure
+
+ if(likely(!cd->successful_collections)) {
+ // nothing collected - disable it
+ error("PLUGINSD: '%s' exited with error code %d. Disabling it.", cd->fullfilename, code);
+ cd->enabled = 0;
+ }
+ else {
+ // we have collected something
+
+ if(likely(cd->serial_failures <= 10)) {
+ error("PLUGINSD: '%s' exited with error code %d, but has given useful output in the past (%zu times). Waiting a bit before starting it again.", cd->fullfilename, code, cd->successful_collections);
+ sleep((unsigned int) (cd->update_every * 10));
+ }
+ else {
+ error("PLUGINSD: '%s' exited with error code %d, but has given useful output in the past (%zu times). We tried %zu times to restart it, but it failed to generate data. Disabling it.", cd->fullfilename, code, cd->successful_collections, cd->serial_failures);
+ cd->enabled = 0;
+ }
+ }
+ }
+ else {
+ // the plugin reports success
+
+ if(unlikely(!cd->successful_collections)) {
+ // we have collected nothing so far
+
+ if(likely(cd->serial_failures <= 10)) {
+ error("PLUGINSD: '%s' (pid %d) does not generate useful output but it reports success (exits with 0). Waiting a bit before starting it again.", cd->fullfilename, cd->pid);
+ sleep((unsigned int) (cd->update_every * 10));
+ }
+ else {
+ error("PLUGINSD: '%s' (pid %d) does not generate useful output, although it reports success (exits with 0), but we have tried %zu times to collect something. Disabling it.", cd->fullfilename, cd->pid, cd->serial_failures);
+ cd->enabled = 0;
+ }
+ }
+ else
+ sleep((unsigned int) cd->update_every);
+ }
+ cd->pid = 0;
+
+ if(unlikely(!cd->enabled))
+ break;
+ }
+
+ cd->obsolete = 1;
+ pthread_exit(NULL);
+ return NULL;
}
-void *pluginsd_main(void *ptr)
-{
- if(ptr) { ; }
-
- info("PLUGINS.D thread created with task id %d", gettid());
-
- if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
- error("Cannot set pthread cancel type to DEFERRED.");
-
- if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
- error("Cannot set pthread cancel state to ENABLE.");
-
- char *dir_name = config_get("plugins", "plugins directory", PLUGINS_DIR);
- int automatic_run = config_get_boolean("plugins", "enable running new plugins", 1);
- int scan_frequency = (int) config_get_number("plugins", "check for new plugins every", 60);
- DIR *dir = NULL;
- struct dirent *file = NULL;
- struct plugind *cd;
-
- // enable the apps plugin by default
- // config_get_boolean("plugins", "apps", 1);
-
- if(scan_frequency < 1) scan_frequency = 1;
-
- while(likely(1)) {
- if(unlikely(netdata_exit)) break;
-
- dir = opendir(dir_name);
- if(unlikely(!dir)) {
- error("Cannot open directory '%s'.", dir_name);
- pthread_exit(NULL);
- return NULL;
- }
-
- while(likely((file = readdir(dir)))) {
- if(unlikely(netdata_exit)) break;
-
- debug(D_PLUGINSD, "PLUGINSD: Examining file '%s'", file->d_name);
-
- if(unlikely(strcmp(file->d_name, ".") == 0 || strcmp(file->d_name, "..") == 0)) continue;
-
- int len = (int) strlen(file->d_name);
- if(unlikely(len <= (int)PLUGINSD_FILE_SUFFIX_LEN)) continue;
- if(unlikely(strcmp(PLUGINSD_FILE_SUFFIX, &file->d_name[len - (int)PLUGINSD_FILE_SUFFIX_LEN]) != 0)) {
- debug(D_PLUGINSD, "PLUGINSD: File '%s' does not end in '%s'.", file->d_name, PLUGINSD_FILE_SUFFIX);
- continue;
- }
-
- char pluginname[CONFIG_MAX_NAME + 1];
- snprintfz(pluginname, CONFIG_MAX_NAME, "%.*s", (int)(len - PLUGINSD_FILE_SUFFIX_LEN), file->d_name);
- int enabled = config_get_boolean("plugins", pluginname, automatic_run);
-
- if(unlikely(!enabled)) {
- debug(D_PLUGINSD, "PLUGINSD: plugin '%s' is not enabled", file->d_name);
- continue;
- }
-
- // check if it runs already
- for(cd = pluginsd_root ; likely(cd) ; cd = cd->next) {
- if(unlikely(strcmp(cd->filename, file->d_name) == 0)) break;
- }
- if(likely(cd && !cd->obsolete)) {
- debug(D_PLUGINSD, "PLUGINSD: plugin '%s' is already running", cd->filename);
- continue;
- }
-
- // it is not running
- // allocate a new one, or use the obsolete one
- if(unlikely(!cd)) {
- cd = calloc(sizeof(struct plugind), 1);
- if(unlikely(!cd)) fatal("Cannot allocate memory for plugin.");
-
- snprintfz(cd->id, CONFIG_MAX_NAME, "plugin:%s", pluginname);
-
- strncpyz(cd->filename, file->d_name, FILENAME_MAX);
- snprintfz(cd->fullfilename, FILENAME_MAX, "%s/%s", dir_name, cd->filename);
-
- cd->enabled = enabled;
- cd->update_every = (int) config_get_number(cd->id, "update every", rrd_update_every);
- cd->started_t = time(NULL);
-
- char *def = "";
- snprintfz(cd->cmd, PLUGINSD_CMD_MAX, "exec %s %d %s", cd->fullfilename, cd->update_every, config_get(cd->id, "command options", def));
-
- // link it
- if(likely(pluginsd_root)) cd->next = pluginsd_root;
- pluginsd_root = cd;
- }
- cd->obsolete = 0;
-
- if(unlikely(!cd->enabled)) continue;
-
- // spawn a new thread for it
- if(unlikely(pthread_create(&cd->thread, NULL, pluginsd_worker_thread, cd) != 0)) {
- error("PLUGINSD: failed to create new thread for plugin '%s'.", cd->filename);
- cd->obsolete = 1;
- }
- else if(unlikely(pthread_detach(cd->thread) != 0))
- error("PLUGINSD: Cannot request detach of newly created thread for plugin '%s'.", cd->filename);
- }
-
- closedir(dir);
- sleep((unsigned int) scan_frequency);
- }
-
- pthread_exit(NULL);
- return NULL;
+void *pluginsd_main(void *ptr) {
+ (void)ptr;
+
+ info("PLUGINS.D thread created with task id %d", gettid());
+
+ if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
+ error("Cannot set pthread cancel type to DEFERRED.");
+
+ if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
+ error("Cannot set pthread cancel state to ENABLE.");
+
+ char *dir_name = config_get("plugins", "plugins directory", PLUGINS_DIR);
+ int automatic_run = config_get_boolean("plugins", "enable running new plugins", 1);
+ int scan_frequency = (int) config_get_number("plugins", "check for new plugins every", 60);
+ DIR *dir = NULL;
+ struct dirent *file = NULL;
+ struct plugind *cd;
+
+ // enable the apps plugin by default
+ // config_get_boolean("plugins", "apps", 1);
+
+ if(scan_frequency < 1) scan_frequency = 1;
+
+ while(likely(1)) {
+ if(unlikely(netdata_exit)) break;
+
+ dir = opendir(dir_name);
+ if(unlikely(!dir)) {
+ error("Cannot open directory '%s'.", dir_name);
+ pthread_exit(NULL);
+ return NULL;
+ }
+
+ while(likely((file = readdir(dir)))) {
+ if(unlikely(netdata_exit)) break;
+
+ debug(D_PLUGINSD, "PLUGINSD: Examining file '%s'", file->d_name);
+
+ if(unlikely(strcmp(file->d_name, ".") == 0 || strcmp(file->d_name, "..") == 0)) continue;
+
+ int len = (int) strlen(file->d_name);
+ if(unlikely(len <= (int)PLUGINSD_FILE_SUFFIX_LEN)) continue;
+ if(unlikely(strcmp(PLUGINSD_FILE_SUFFIX, &file->d_name[len - (int)PLUGINSD_FILE_SUFFIX_LEN]) != 0)) {
+ debug(D_PLUGINSD, "PLUGINSD: File '%s' does not end in '%s'.", file->d_name, PLUGINSD_FILE_SUFFIX);
+ continue;
+ }
+
+ char pluginname[CONFIG_MAX_NAME + 1];
+ snprintfz(pluginname, CONFIG_MAX_NAME, "%.*s", (int)(len - PLUGINSD_FILE_SUFFIX_LEN), file->d_name);
+ int enabled = config_get_boolean("plugins", pluginname, automatic_run);
+
+ if(unlikely(!enabled)) {
+ debug(D_PLUGINSD, "PLUGINSD: plugin '%s' is not enabled", file->d_name);
+ continue;
+ }
+
+ // check if it runs already
+ for(cd = pluginsd_root ; likely(cd) ; cd = cd->next) {
+ if(unlikely(strcmp(cd->filename, file->d_name) == 0)) break;
+ }
+ if(likely(cd && !cd->obsolete)) {
+ debug(D_PLUGINSD, "PLUGINSD: plugin '%s' is already running", cd->filename);
+ continue;
+ }
+
+ // it is not running
+ // allocate a new one, or use the obsolete one
+ if(unlikely(!cd)) {
+ cd = callocz(sizeof(struct plugind), 1);
+
+ snprintfz(cd->id, CONFIG_MAX_NAME, "plugin:%s", pluginname);
+
+ strncpyz(cd->filename, file->d_name, FILENAME_MAX);
+ snprintfz(cd->fullfilename, FILENAME_MAX, "%s/%s", dir_name, cd->filename);
+
+ cd->enabled = enabled;
+ cd->update_every = (int) config_get_number(cd->id, "update every", rrd_update_every);
+ cd->started_t = time(NULL);
+
+ char *def = "";
+ snprintfz(cd->cmd, PLUGINSD_CMD_MAX, "exec %s %d %s", cd->fullfilename, cd->update_every, config_get(cd->id, "command options", def));
+
+ // link it
+ if(likely(pluginsd_root)) cd->next = pluginsd_root;
+ pluginsd_root = cd;
+ }
+ cd->obsolete = 0;
+
+ if(unlikely(!cd->enabled)) continue;
+
+ // spawn a new thread for it
+ if(unlikely(pthread_create(&cd->thread, NULL, pluginsd_worker_thread, cd) != 0)) {
+ error("PLUGINSD: failed to create new thread for plugin '%s'.", cd->filename);
+ cd->obsolete = 1;
+ }
+ else if(unlikely(pthread_detach(cd->thread) != 0))
+ error("PLUGINSD: Cannot request detach of newly created thread for plugin '%s'.", cd->filename);
+ }
+
+ closedir(dir);
+ sleep((unsigned int) scan_frequency);
+ }
+
+ pthread_exit(NULL);
+ return NULL;
}
diff --git a/src/plugins_d.h b/src/plugins_d.h
index 11e89e0ac..6f1fbd6e1 100644
--- a/src/plugins_d.h
+++ b/src/plugins_d.h
@@ -1,7 +1,3 @@
-#include <sys/types.h>
-#include <unistd.h>
-
-
#ifndef NETDATA_PLUGINS_D_H
#define NETDATA_PLUGINS_D_H 1
@@ -11,22 +7,28 @@
#define PLUGINSD_LINE_MAX 1024
struct plugind {
- char id[CONFIG_MAX_NAME+1]; // config node id
+ char id[CONFIG_MAX_NAME+1]; // config node id
+
+ char filename[FILENAME_MAX+1]; // just the filename
+ char fullfilename[FILENAME_MAX+1]; // with path
+ char cmd[PLUGINSD_CMD_MAX+1]; // the command that is executes
+
+ pid_t pid;
+ pthread_t thread;
- char filename[FILENAME_MAX+1]; // just the filename
- char fullfilename[FILENAME_MAX+1]; // with path
- char cmd[PLUGINSD_CMD_MAX+1]; // the command that is executes
+ size_t successful_collections; // the number of times we have seen
+ // values collected from this plugin
- pid_t pid;
- pthread_t thread;
+ size_t serial_failures; // the number of times the plugin started
+ // without collecting values
- int update_every;
- int obsolete;
- int enabled;
+ int update_every; // the plugin default data collection frequency
+ int obsolete; // do not touch this structure after setting this to 1
+ int enabled; // if this is enabled or not
- time_t started_t;
+ time_t started_t;
- struct plugind *next;
+ struct plugind *next;
};
extern struct plugind *pluginsd_root;
diff --git a/src/popen.c b/src/popen.c
index 06f27c0b7..193efc0f3 100644
--- a/src/popen.c
+++ b/src/popen.c
@@ -1,54 +1,43 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <signal.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-
-#include "log.h"
-#include "popen.h"
#include "common.h"
/*
struct mypopen {
- pid_t pid;
- FILE *fp;
- struct mypopen *next;
- struct mypopen *prev;
+ pid_t pid;
+ FILE *fp;
+ struct mypopen *next;
+ struct mypopen *prev;
};
static struct mypopen *mypopen_root = NULL;
static void mypopen_add(FILE *fp, pid_t *pid) {
- struct mypopen *mp = malloc(sizeof(struct mypopen));
- if(!mp) {
- fatal("Cannot allocate %zu bytes", sizeof(struct mypopen))
- return;
- }
-
- mp->fp = fp;
- mp->pid = pid;
- mp->next = popen_root;
- mp->prev = NULL;
- if(mypopen_root) mypopen_root->prev = mp;
- mypopen_root = mp;
+ struct mypopen *mp = malloc(sizeof(struct mypopen));
+ if(!mp) {
+ fatal("Cannot allocate %zu bytes", sizeof(struct mypopen))
+ return;
+ }
+
+ mp->fp = fp;
+ mp->pid = pid;
+ mp->next = popen_root;
+ mp->prev = NULL;
+ if(mypopen_root) mypopen_root->prev = mp;
+ mypopen_root = mp;
}
static void mypopen_del(FILE *fp) {
- struct mypopen *mp;
-
- for(mp = mypopen_root; mp; mp = mp->next)
- if(mp->fd == fp) break;
-
- if(!mp) error("Cannot find mypopen() file pointer in open childs.");
- else {
- if(mp->next) mp->next->prev = mp->prev;
- if(mp->prev) mp->prev->next = mp->next;
- if(mypopen_root == mp) mypopen_root = mp->next;
- free(mp);
- }
+ struct mypopen *mp;
+
+ for(mp = mypopen_root; mp; mp = mp->next)
+ if(mp->fd == fp) break;
+
+ if(!mp) error("Cannot find mypopen() file pointer in open childs.");
+ else {
+ if(mp->next) mp->next->prev = mp->prev;
+ if(mp->prev) mp->prev->next = mp->next;
+ if(mypopen_root == mp) mypopen_root = mp->next;
+ free(mp);
+ }
}
*/
#define PIPE_READ 0
@@ -56,133 +45,152 @@ static void mypopen_del(FILE *fp) {
FILE *mypopen(const char *command, pid_t *pidptr)
{
- int pipefd[2];
-
- if(pipe(pipefd) == -1) return NULL;
-
- int pid = fork();
- if(pid == -1) {
- close(pipefd[PIPE_READ]);
- close(pipefd[PIPE_WRITE]);
- return NULL;
- }
- if(pid != 0) {
- // the parent
- *pidptr = pid;
- close(pipefd[PIPE_WRITE]);
- FILE *fp = fdopen(pipefd[PIPE_READ], "r");
- /*mypopen_add(fp, pid);*/
- return(fp);
- }
- // the child
-
- // close all files
- int i;
- for(i = (int) (sysconf(_SC_OPEN_MAX) - 1); i > 0; i--)
- if(i != STDIN_FILENO && i != STDERR_FILENO && i != pipefd[PIPE_WRITE]) close(i);
-
- // move the pipe to stdout
- if(pipefd[PIPE_WRITE] != STDOUT_FILENO) {
- dup2(pipefd[PIPE_WRITE], STDOUT_FILENO);
- close(pipefd[PIPE_WRITE]);
- }
+ int pipefd[2];
+
+ if(pipe(pipefd) == -1) return NULL;
+
+ int pid = fork();
+ if(pid == -1) {
+ close(pipefd[PIPE_READ]);
+ close(pipefd[PIPE_WRITE]);
+ return NULL;
+ }
+ if(pid != 0) {
+ // the parent
+ *pidptr = pid;
+ close(pipefd[PIPE_WRITE]);
+ FILE *fp = fdopen(pipefd[PIPE_READ], "r");
+ /*mypopen_add(fp, pid);*/
+ return(fp);
+ }
+ // the child
+
+ // close all files
+ int i;
+ for(i = (int) (sysconf(_SC_OPEN_MAX) - 1); i > 0; i--)
+ if(i != STDIN_FILENO && i != STDERR_FILENO && i != pipefd[PIPE_WRITE]) close(i);
+
+ // move the pipe to stdout
+ if(pipefd[PIPE_WRITE] != STDOUT_FILENO) {
+ dup2(pipefd[PIPE_WRITE], STDOUT_FILENO);
+ close(pipefd[PIPE_WRITE]);
+ }
#ifdef DETACH_PLUGINS_FROM_NETDATA
- // this was an attempt to detach the child and use the suspend mode charts.d
- // unfortunatelly it does not work as expected.
+ // this was an attempt to detach the child and use the suspend mode charts.d
+ // unfortunatelly it does not work as expected.
+
+ // fork again to become session leader
+ pid = fork();
+ if(pid == -1)
+ error("pre-execution of command '%s' on pid %d: Cannot fork 2nd time.", command, getpid());
- // fork again to become session leader
- pid = fork();
- if(pid == -1) fprintf(stderr, "Cannot fork again on pid %d\n", getpid());
- if(pid != 0) {
- // the parent
- exit(0);
- }
+ if(pid != 0) {
+ // the parent
+ exit(0);
+ }
- // set a new process group id for just this child
- if( setpgid(0, 0) != 0 )
- error("Cannot set a new process group for pid %d (%s)", getpid(), strerror(errno));
+ // set a new process group id for just this child
+ if( setpgid(0, 0) != 0 )
+ error("pre-execution of command '%s' on pid %d: Cannot set a new process group.", command, getpid());
- if( getpgid(0) != getpid() )
- error("Process group set is incorrect. Expected %d, found %d", getpid(), getpgid(0));
+ if( getpgid(0) != getpid() )
+ error("pre-execution of command '%s' on pid %d: Cannot set a new process group. Process group set is incorrect. Expected %d, found %d", command, getpid(), getpid(), getpgid(0));
- if( setsid() != 0 )
- error("Cannot set session id for pid %d (%s)", getpid(), strerror(errno));
+ if( setsid() != 0 )
+ error("pre-execution of command '%s' on pid %d: Cannot set session id.", command, getpid());
- fprintf(stdout, "MYPID %d\n", getpid());
- fflush(NULL);
+ fprintf(stdout, "MYPID %d\n", getpid());
+ fflush(NULL);
#endif
- // reset all signals
- {
- sigset_t sigset;
- sigfillset(&sigset);
-
- if(pthread_sigmask(SIG_UNBLOCK, &sigset, NULL) == -1) {
- error("Could not block signals for threads");
- }
- // We only need to reset ignored signals.
- // Signals with signal handlers are reset by default.
- struct sigaction sa;
- sigemptyset(&sa.sa_mask);
- sa.sa_handler = SIG_DFL;
- sa.sa_flags = 0;
- if(sigaction(SIGPIPE, &sa, NULL) == -1) {
- error("Failed to change signal handler for SIGTERM");
- }
- }
-
-
- info("executing command: '%s' on pid %d.", command, getpid());
- execl("/bin/sh", "sh", "-c", command, NULL);
- exit(1);
+ // reset all signals
+ {
+ sigset_t sigset;
+ sigfillset(&sigset);
+
+ if(pthread_sigmask(SIG_UNBLOCK, &sigset, NULL) == -1)
+ error("pre-execution of command '%s' on pid %d: could not unblock signals for threads.", command, getpid());
+
+ // We only need to reset ignored signals.
+ // Signals with signal handlers are reset by default.
+ struct sigaction sa;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_handler = SIG_DFL;
+ sa.sa_flags = 0;
+
+ if(sigaction(SIGINT, &sa, NULL) == -1)
+ error("pre-execution of command '%s' on pid %d: failed to set default signal handler for SIGINT.", command, getpid());
+
+ if(sigaction(SIGTERM, &sa, NULL) == -1)
+ error("pre-execution of command '%s' on pid %d: failed to set default signal handler for SIGTERM.", command, getpid());
+
+ if(sigaction(SIGPIPE, &sa, NULL) == -1)
+ error("pre-execution of command '%s' on pid %d: failed to set default signal handler for SIGPIPE.", command, getpid());
+
+ if(sigaction(SIGHUP, &sa, NULL) == -1)
+ error("pre-execution of command '%s' on pid %d: failed to set default signal handler for SIGHUP.", command, getpid());
+
+ if(sigaction(SIGUSR1, &sa, NULL) == -1)
+ error("pre-execution of command '%s' on pid %d: failed to set default signal handler for SIGUSR1.", command, getpid());
+
+ if(sigaction(SIGUSR2, &sa, NULL) == -1)
+ error("pre-execution of command '%s' on pid %d: failed to set default signal handler for SIGUSR2.", command, getpid());
+ }
+
+ info("executing command: '%s' on pid %d.", command, getpid());
+ execl("/bin/sh", "sh", "-c", command, NULL);
+ exit(1);
}
int mypclose(FILE *fp, pid_t pid) {
- debug(D_EXIT, "Request to mypclose() on pid %d", pid);
-
- /*mypopen_del(fp);*/
- fclose(fp);
-
- siginfo_t info;
- if(waitid(P_PID, (id_t) pid, &info, WEXITED) != -1) {
- switch(info.si_code) {
- case CLD_EXITED:
- error("pid %d exited with code %d.", info.si_pid, info.si_status);
- return(info.si_status);
- break;
-
- case CLD_KILLED:
- error("pid %d killed by signal %d.", info.si_pid, info.si_status);
- return(-1);
- break;
-
- case CLD_DUMPED:
- error("pid %d core dumped by signal %d.", info.si_pid, info.si_status);
- return(-2);
- break;
-
- case CLD_STOPPED:
- error("pid %d stopped by signal %d.", info.si_pid, info.si_status);
- return(0);
- break;
-
- case CLD_TRAPPED:
- error("pid %d trapped by signal %d.", info.si_pid, info.si_status);
- return(-4);
- break;
-
- case CLD_CONTINUED:
- error("pid %d continued by signal %d.", info.si_pid, info.si_status);
- return(0);
- break;
-
- default:
- error("pid %d gave us a SIGCHLD with code %d and status %d.", info.si_pid, info.si_code, info.si_status);
- return(-5);
- break;
- }
- }
- else error("Cannot waitid() for pid %d", pid);
- return 0;
+ debug(D_EXIT, "Request to mypclose() on pid %d", pid);
+
+ /*mypopen_del(fp);*/
+ fclose(fp);
+
+ siginfo_t info;
+ if(waitid(P_PID, (id_t) pid, &info, WEXITED) != -1) {
+ switch(info.si_code) {
+ case CLD_EXITED:
+ if(info.si_status)
+ error("child pid %d exited with code %d.", info.si_pid, info.si_status);
+ return(info.si_status);
+ break;
+
+ case CLD_KILLED:
+ error("child pid %d killed by signal %d.", info.si_pid, info.si_status);
+ return(-1);
+ break;
+
+ case CLD_DUMPED:
+ error("child pid %d core dumped by signal %d.", info.si_pid, info.si_status);
+ return(-2);
+ break;
+
+ case CLD_STOPPED:
+ error("child pid %d stopped by signal %d.", info.si_pid, info.si_status);
+ return(0);
+ break;
+
+ case CLD_TRAPPED:
+ error("child pid %d trapped by signal %d.", info.si_pid, info.si_status);
+ return(-4);
+ break;
+
+ case CLD_CONTINUED:
+ error("child pid %d continued by signal %d.", info.si_pid, info.si_status);
+ return(0);
+ break;
+
+ default:
+ error("child pid %d gave us a SIGCHLD with code %d and status %d.", info.si_pid, info.si_code, info.si_status);
+ return(-5);
+ break;
+ }
+ }
+ else
+ error("Cannot waitid() for pid %d", pid);
+
+ return 0;
}
diff --git a/src/popen.h b/src/popen.h
index 10680f0c8..90845e1fb 100644
--- a/src/popen.h
+++ b/src/popen.h
@@ -1,7 +1,3 @@
-#include <stdio.h>
-#include <unistd.h>
-#include <sys/types.h>
-
#ifndef NETDATA_POPEN_H
#define NETDATA_POPEN_H 1
diff --git a/src/proc_diskstats.c b/src/proc_diskstats.c
index c62a1351c..459d5a133 100644
--- a/src/proc_diskstats.c
+++ b/src/proc_diskstats.c
@@ -1,578 +1,700 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-
#include "common.h"
-#include "log.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "rrd.h"
-#include "plugin_proc.h"
-
-#include "proc_self_mountinfo.h"
#define RRD_TYPE_DISK "disk"
-struct disk {
- unsigned long major;
- unsigned long minor;
- int partition_id; // -1 = this is not a partition
- char *family;
- struct disk *next;
+#define DISK_TYPE_PHYSICAL 1
+#define DISK_TYPE_PARTITION 2
+#define DISK_TYPE_CONTAINER 3
+
+static struct disk {
+ char *disk; // the name of the disk (sda, sdb, etc)
+ unsigned long major;
+ unsigned long minor;
+ int sector_size;
+ int type;
+ char *mount_point;
+
+ // disk options caching
+ int configured;
+ int do_io;
+ int do_ops;
+ int do_mops;
+ int do_iotime;
+ int do_qops;
+ int do_util;
+ int do_backlog;
+ int do_space;
+ int do_inodes;
+
+ struct disk *next;
} *disk_root = NULL;
-struct disk *get_disk(unsigned long major, unsigned long minor) {
- static char path_find_block_device_partition[FILENAME_MAX + 1] = "";
- static struct mountinfo *mountinfo_root = NULL;
- struct disk *d;
-
- // search for it in our RAM list.
- // this is sequential, but since we just walk through
- // and the number of disks / partitions in a system
- // should not be that many, it should be acceptable
- for(d = disk_root; d ; d = d->next)
- if(unlikely(d->major == major && d->minor == minor))
- break;
-
- // if we found it, return it
- if(likely(d))
- return d;
-
- if(unlikely(!path_find_block_device_partition[0])) {
- char filename[FILENAME_MAX + 1];
- snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/sys/dev/block/%lu:%lu/partition");
- snprintfz(path_find_block_device_partition, FILENAME_MAX, "%s", config_get("plugin:proc:/proc/diskstats", "path to get block device partition", filename));
- }
-
- // not found
- // create a new disk structure
- d = (struct disk *)malloc(sizeof(struct disk));
- if(!d) fatal("Cannot allocate memory for struct disk in proc_diskstats.");
-
- d->major = major;
- d->minor = minor;
- d->partition_id = -1;
- d->next = NULL;
-
- // append it to the list
- if(!disk_root)
- disk_root = d;
- else {
- struct disk *last;
- for(last = disk_root; last->next ;last = last->next);
- last->next = d;
- }
-
- // find if it is a partition
- // by reading /sys/dev/block/MAJOR:MINOR/partition
- char buffer[FILENAME_MAX + 1];
- snprintfz(buffer, FILENAME_MAX, path_find_block_device_partition, major, minor);
-
- int fd = open(buffer, O_RDONLY, 0666);
- if(likely(fd != -1)) {
- // we opened it
- int bytes = read(fd, buffer, FILENAME_MAX);
- close(fd);
-
- if(bytes > 0)
- d->partition_id = strtoul(buffer, NULL, 10);
- }
- // if the /partition file does not exist, it is a disk, not a partition
-
- // ------------------------------------------------------------------------
- // check if we can find its mount point
-
- // mountinfo_find() can be called with NULL mountinfo_root
- struct mountinfo *mi = mountinfo_find(mountinfo_root, d->major, d->minor);
- if(unlikely(!mi)) {
- // mountinfo_free() can be called with NULL mountinfo_root
- mountinfo_free(mountinfo_root);
-
- // re-read mountinfo in case something changed
- mountinfo_root = mountinfo_read();
-
- // search again for this disk
- mi = mountinfo_find(mountinfo_root, d->major, d->minor);
- }
-
- if(mi)
- d->family = strdup(mi->mount_point);
- // no need to check for NULL
- else
- d->family = NULL;
-
- return d;
+static struct mountinfo *disk_mountinfo_root = NULL;
+
+static struct disk *get_disk(unsigned long major, unsigned long minor, char *disk) {
+ static char path_to_get_hw_sector_size[FILENAME_MAX + 1] = "";
+ static char path_to_get_hw_sector_size_partitions[FILENAME_MAX + 1] = "";
+ static char path_find_block_device[FILENAME_MAX + 1] = "";
+ struct disk *d;
+
+ // search for it in our RAM list.
+ // this is sequential, but since we just walk through
+ // and the number of disks / partitions in a system
+ // should not be that many, it should be acceptable
+ for(d = disk_root; d ; d = d->next)
+ if(unlikely(d->major == major && d->minor == minor))
+ break;
+
+ // if we found it, return it
+ if(likely(d))
+ return d;
+
+ // not found
+ // create a new disk structure
+ d = (struct disk *)mallocz(sizeof(struct disk));
+
+ d->disk = strdupz(disk);
+ d->major = major;
+ d->minor = minor;
+ d->type = DISK_TYPE_PHYSICAL; // Default type. Changed later if not correct.
+ d->configured = 0;
+ d->sector_size = 512; // the default, will be changed below
+ d->next = NULL;
+
+ // append it to the list
+ if(!disk_root)
+ disk_root = d;
+ else {
+ struct disk *last;
+ for(last = disk_root; last->next ;last = last->next);
+ last->next = d;
+ }
+
+ // ------------------------------------------------------------------------
+ // find the type of the device
+
+ char buffer[FILENAME_MAX + 1];
+
+ // get the default path for finding info about the block device
+ if(unlikely(!path_find_block_device[0])) {
+ snprintfz(buffer, FILENAME_MAX, "%s%s", global_host_prefix, "/sys/dev/block/%lu:%lu/%s");
+ snprintfz(path_find_block_device, FILENAME_MAX, "%s", config_get("plugin:proc:/proc/diskstats", "path to get block device infos", buffer));
+ }
+
+ // find if it is a partition
+ // by checking if /sys/dev/block/MAJOR:MINOR/partition is readable.
+ snprintfz(buffer, FILENAME_MAX, path_find_block_device, major, minor, "partition");
+ if(access(buffer, R_OK) == 0) {
+ d->type = DISK_TYPE_PARTITION;
+ } else {
+ // find if it is a container
+ // by checking if /sys/dev/block/MAJOR:MINOR/slaves has entries
+ snprintfz(buffer, FILENAME_MAX, path_find_block_device, major, minor, "slaves/");
+ DIR *dirp = opendir(buffer);
+ if (dirp != NULL) {
+ struct dirent *dp;
+ while( (dp = readdir(dirp)) ) {
+ // . and .. are also files in empty folders.
+ if(strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) {
+ continue;
+ }
+
+ d->type = DISK_TYPE_CONTAINER;
+
+ // Stop the loop after we found one file.
+ break;
+ }
+ if(closedir(dirp) == -1)
+ error("Unable to close dir %s", buffer);
+ }
+ }
+
+ // ------------------------------------------------------------------------
+ // check if we can find its mount point
+
+ // mountinfo_find() can be called with NULL disk_mountinfo_root
+ struct mountinfo *mi = mountinfo_find(disk_mountinfo_root, d->major, d->minor);
+ if(unlikely(!mi)) {
+ // mountinfo_free() can be called with NULL disk_mountinfo_root
+ mountinfo_free(disk_mountinfo_root);
+
+ // re-read mountinfo in case something changed
+ disk_mountinfo_root = mountinfo_read();
+
+ // search again for this disk
+ mi = mountinfo_find(disk_mountinfo_root, d->major, d->minor);
+ }
+
+ if(mi)
+ d->mount_point = strdupz(mi->mount_point);
+ // no need to check for NULL
+ else
+ d->mount_point = NULL;
+
+ // ------------------------------------------------------------------------
+ // find the disk sector size
+
+ if(!path_to_get_hw_sector_size[0]) {
+ snprintfz(buffer, FILENAME_MAX, "%s%s", global_host_prefix, "/sys/block/%s/queue/hw_sector_size");
+ snprintfz(path_to_get_hw_sector_size, FILENAME_MAX, "%s", config_get("plugin:proc:/proc/diskstats", "path to get h/w sector size", buffer));
+ }
+ if(!path_to_get_hw_sector_size_partitions[0]) {
+ snprintfz(buffer, FILENAME_MAX, "%s%s", global_host_prefix, "/sys/dev/block/%lu:%lu/subsystem/%s/../queue/hw_sector_size");
+ snprintfz(path_to_get_hw_sector_size_partitions, FILENAME_MAX, "%s", config_get("plugin:proc:/proc/diskstats", "path to get h/w sector size for partitions", buffer));
+ }
+
+ {
+ char tf[FILENAME_MAX + 1], *t;
+ strncpyz(tf, d->disk, FILENAME_MAX);
+
+ // replace all / with !
+ for(t = tf; *t ;t++)
+ if(*t == '/') *t = '!';
+
+ if(d->type == DISK_TYPE_PARTITION)
+ snprintfz(buffer, FILENAME_MAX, path_to_get_hw_sector_size_partitions, d->major, d->minor, tf);
+ else
+ snprintfz(buffer, FILENAME_MAX, path_to_get_hw_sector_size, tf);
+
+ FILE *fpss = fopen(buffer, "r");
+ if(fpss) {
+ char buffer2[1024 + 1];
+ char *tmp = fgets(buffer2, 1024, fpss);
+
+ if(tmp) {
+ d->sector_size = atoi(tmp);
+ if(d->sector_size <= 0) {
+ error("Invalid sector size %d for device %s in %s. Assuming 512.", d->sector_size, d->disk, buffer);
+ d->sector_size = 512;
+ }
+ }
+ else error("Cannot read data for sector size for device %s from %s. Assuming 512.", d->disk, buffer);
+
+ fclose(fpss);
+ }
+ else error("Cannot read sector size for device %s from %s. Assuming 512.", d->disk, buffer);
+ }
+
+ return d;
+}
+
+static inline int select_positive_option(int option1, int option2) {
+ if(option1 == CONFIG_ONDEMAND_YES || option2 == CONFIG_ONDEMAND_YES)
+ return CONFIG_ONDEMAND_YES;
+ else if(option1 == CONFIG_ONDEMAND_ONDEMAND || option2 == CONFIG_ONDEMAND_ONDEMAND)
+ return CONFIG_ONDEMAND_ONDEMAND;
+
+ return CONFIG_ONDEMAND_NO;
}
int do_proc_diskstats(int update_every, unsigned long long dt) {
- static procfile *ff = NULL;
- static char path_to_get_hw_sector_size[FILENAME_MAX + 1] = "";
- static int enable_new_disks = -1;
- static int do_io = -1, do_ops = -1, do_mops = -1, do_iotime = -1, do_qops = -1, do_util = -1, do_backlog = -1;
-
- if(enable_new_disks == -1) enable_new_disks = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "enable new disks detected at runtime", CONFIG_ONDEMAND_ONDEMAND);
-
- if(do_io == -1) do_io = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "bandwidth for all disks", CONFIG_ONDEMAND_ONDEMAND);
- if(do_ops == -1) do_ops = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "operations for all disks", CONFIG_ONDEMAND_ONDEMAND);
- if(do_mops == -1) do_mops = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "merged operations for all disks", CONFIG_ONDEMAND_ONDEMAND);
- if(do_iotime == -1) do_iotime = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "i/o time for all disks", CONFIG_ONDEMAND_ONDEMAND);
- if(do_qops == -1) do_qops = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "queued operations for all disks", CONFIG_ONDEMAND_ONDEMAND);
- if(do_util == -1) do_util = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "utilization percentage for all disks", CONFIG_ONDEMAND_ONDEMAND);
- if(do_backlog == -1)do_backlog = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "backlog for all disks", CONFIG_ONDEMAND_ONDEMAND);
-
- if(!ff) {
- char filename[FILENAME_MAX + 1];
- snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/diskstats");
- ff = procfile_open(config_get("plugin:proc:/proc/diskstats", "filename to monitor", filename), " \t", PROCFILE_FLAG_DEFAULT);
- }
- if(!ff) return 1;
-
- if(!path_to_get_hw_sector_size[0]) {
- char filename[FILENAME_MAX + 1];
- snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/sys/block/%s/queue/hw_sector_size");
- snprintfz(path_to_get_hw_sector_size, FILENAME_MAX, "%s", config_get("plugin:proc:/proc/diskstats", "path to get h/w sector size", filename));
- }
-
- ff = procfile_readall(ff);
- if(!ff) return 0; // we return 0, so that we will retry to open it next time
-
- uint32_t lines = procfile_lines(ff), l;
- uint32_t words;
-
- for(l = 0; l < lines ;l++) {
- char *disk;
- unsigned long long major = 0, minor = 0,
- reads = 0, mreads = 0, readsectors = 0, readms = 0,
- writes = 0, mwrites = 0, writesectors = 0, writems = 0,
- queued_ios = 0, busy_ms = 0, backlog_ms = 0;
-
- unsigned long long last_reads = 0, last_readsectors = 0, last_readms = 0,
- last_writes = 0, last_writesectors = 0, last_writems = 0,
- last_busy_ms = 0;
-
- words = procfile_linewords(ff, l);
- if(words < 14) continue;
-
- major = strtoull(procfile_lineword(ff, l, 0), NULL, 10);
- minor = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
- disk = procfile_lineword(ff, l, 2);
-
- // # of reads completed # of writes completed
- // This is the total number of reads or writes completed successfully.
- reads = strtoull(procfile_lineword(ff, l, 3), NULL, 10); // rd_ios
- writes = strtoull(procfile_lineword(ff, l, 7), NULL, 10); // wr_ios
-
- // # of reads merged # of writes merged
- // Reads and writes which are adjacent to each other may be merged for
- // efficiency. Thus two 4K reads may become one 8K read before it is
- // ultimately handed to the disk, and so it will be counted (and queued)
- mreads = strtoull(procfile_lineword(ff, l, 4), NULL, 10); // rd_merges_or_rd_sec
- mwrites = strtoull(procfile_lineword(ff, l, 8), NULL, 10); // wr_merges
-
- // # of sectors read # of sectors written
- // This is the total number of sectors read or written successfully.
- readsectors = strtoull(procfile_lineword(ff, l, 5), NULL, 10); // rd_sec_or_wr_ios
- writesectors = strtoull(procfile_lineword(ff, l, 9), NULL, 10); // wr_sec
-
- // # of milliseconds spent reading # of milliseconds spent writing
- // This is the total number of milliseconds spent by all reads or writes (as
- // measured from __make_request() to end_that_request_last()).
- readms = strtoull(procfile_lineword(ff, l, 6), NULL, 10); // rd_ticks_or_wr_sec
- writems = strtoull(procfile_lineword(ff, l, 10), NULL, 10); // wr_ticks
-
- // # of I/Os currently in progress
- // The only field that should go to zero. Incremented as requests are
- // given to appropriate struct request_queue and decremented as they finish.
- queued_ios = strtoull(procfile_lineword(ff, l, 11), NULL, 10); // ios_pgr
-
- // # of milliseconds spent doing I/Os
- // This field increases so long as field queued_ios is nonzero.
- busy_ms = strtoull(procfile_lineword(ff, l, 12), NULL, 10); // tot_ticks
-
- // weighted # of milliseconds spent doing I/Os
- // This field is incremented at each I/O start, I/O completion, I/O
- // merge, or read of these stats by the number of I/Os in progress
- // (field queued_ios) times the number of milliseconds spent doing I/O since the
- // last update of this field. This can provide an easy measure of both
- // I/O completion time and the backlog that may be accumulating.
- backlog_ms = strtoull(procfile_lineword(ff, l, 13), NULL, 10); // rq_ticks
-
- int def_enabled = 0;
-
- // remove slashes from disk names
- char *s;
- for(s = disk; *s ;s++) if(*s == '/') *s = '_';
-
- struct disk *d = get_disk(major, minor);
- if(d->partition_id == -1)
- def_enabled = enable_new_disks;
- else
- def_enabled = 0;
-
- char *family = d->family;
- if(!family) family = disk;
-
-/*
- switch(major) {
- case 9: // MDs
- case 43: // network block
- case 144: // nfs
- case 145: // nfs
- case 146: // nfs
- case 199: // veritas
- case 201: // veritas
- case 251: // dm
- case 253: // virtio
- def_enabled = enable_new_disks;
- break;
-
- case 48: // RAID
- case 49: // RAID
- case 50: // RAID
- case 51: // RAID
- case 52: // RAID
- case 53: // RAID
- case 54: // RAID
- case 55: // RAID
- case 112: // RAID
- case 136: // RAID
- case 137: // RAID
- case 138: // RAID
- case 139: // RAID
- case 140: // RAID
- case 141: // RAID
- case 142: // RAID
- case 143: // RAID
- case 179: // MMC
- case 180: // USB
- if(minor % 8) def_enabled = 0; // partitions
- else def_enabled = enable_new_disks;
- break;
-
- case 8: // scsi disks
- case 65: // scsi disks
- case 66: // scsi disks
- case 67: // scsi disks
- case 68: // scsi disks
- case 69: // scsi disks
- case 70: // scsi disks
- case 71: // scsi disks
- case 72: // scsi disks
- case 73: // scsi disks
- case 74: // scsi disks
- case 75: // scsi disks
- case 76: // scsi disks
- case 77: // scsi disks
- case 78: // scsi disks
- case 79: // scsi disks
- case 80: // i2o
- case 81: // i2o
- case 82: // i2o
- case 83: // i2o
- case 84: // i2o
- case 85: // i2o
- case 86: // i2o
- case 87: // i2o
- case 101: // hyperdisk
- case 102: // compressed
- case 104: // scsi
- case 105: // scsi
- case 106: // scsi
- case 107: // scsi
- case 108: // scsi
- case 109: // scsi
- case 110: // scsi
- case 111: // scsi
- case 114: // bios raid
- case 116: // ram board
- case 128: // scsi
- case 129: // scsi
- case 130: // scsi
- case 131: // scsi
- case 132: // scsi
- case 133: // scsi
- case 134: // scsi
- case 135: // scsi
- case 153: // raid
- case 202: // xen
- case 254: // virtio3
- case 256: // flash
- case 257: // flash
- case 259: // nvme0n1 issue #119
- if(minor % 16) def_enabled = 0; // partitions
- else def_enabled = enable_new_disks;
- break;
-
- case 160: // raid
- case 161: // raid
- if(minor % 32) def_enabled = 0; // partitions
- else def_enabled = enable_new_disks;
- break;
-
- case 3: // ide
- case 13: // 8bit ide
- case 22: // ide
- case 33: // ide
- case 34: // ide
- case 56: // ide
- case 57: // ide
- case 88: // ide
- case 89: // ide
- case 90: // ide
- case 91: // ide
- if(minor % 64) def_enabled = 0; // partitions
- else def_enabled = enable_new_disks;
- break;
-
- case 252: // zram
- def_enabled = 0;
- break;
-
- default:
- def_enabled = 0;
- break;
- }
-*/
-
- int ddo_io = do_io, ddo_ops = do_ops, ddo_mops = do_mops, ddo_iotime = do_iotime, ddo_qops = do_qops, ddo_util = do_util, ddo_backlog = do_backlog;
-
- // check which charts are enabled for this disk
- {
- char var_name[4096 + 1];
- snprintfz(var_name, 4096, "plugin:proc:/proc/diskstats:%s", disk);
- def_enabled = config_get_boolean_ondemand(var_name, "enabled", def_enabled);
- if(def_enabled == CONFIG_ONDEMAND_NO) continue;
- if(def_enabled == CONFIG_ONDEMAND_ONDEMAND && !reads && !writes) continue;
-
-
- ddo_io = config_get_boolean_ondemand(var_name, "bandwidth", ddo_io);
- ddo_ops = config_get_boolean_ondemand(var_name, "operations", ddo_ops);
- ddo_mops = config_get_boolean_ondemand(var_name, "merged operations", ddo_mops);
- ddo_iotime = config_get_boolean_ondemand(var_name, "i/o time", ddo_iotime);
- ddo_qops = config_get_boolean_ondemand(var_name, "queued operations", ddo_qops);
- ddo_util = config_get_boolean_ondemand(var_name, "utilization percentage", ddo_util);
- ddo_backlog = config_get_boolean_ondemand(var_name, "backlog", ddo_backlog);
-
- // by default, do not add charts that do not have values
- if(ddo_io == CONFIG_ONDEMAND_ONDEMAND && !reads && !writes) ddo_io = 0;
- if(ddo_mops == CONFIG_ONDEMAND_ONDEMAND && mreads == 0 && mwrites == 0) ddo_mops = 0;
- if(ddo_iotime == CONFIG_ONDEMAND_ONDEMAND && readms == 0 && writems == 0) ddo_iotime = 0;
- if(ddo_util == CONFIG_ONDEMAND_ONDEMAND && busy_ms == 0) ddo_util = 0;
- if(ddo_backlog == CONFIG_ONDEMAND_ONDEMAND && backlog_ms == 0) ddo_backlog = 0;
- if(ddo_qops == CONFIG_ONDEMAND_ONDEMAND && backlog_ms == 0) ddo_qops = 0;
-
- // for absolute values, we need to switch the setting to 'yes'
- // to allow it refresh from now on
- if(ddo_qops == CONFIG_ONDEMAND_ONDEMAND) config_set(var_name, "queued operations", "yes");
- }
-
- RRDSET *st;
-
- // --------------------------------------------------------------------
-
- int sector_size = 512;
- if(ddo_io) {
- st = rrdset_find_bytype(RRD_TYPE_DISK, disk);
- if(!st) {
- char tf[FILENAME_MAX + 1], *t;
- char ssfilename[FILENAME_MAX + 1];
-
- strncpyz(tf, disk, FILENAME_MAX);
-
- // replace all / with !
- while((t = strchr(tf, '/'))) *t = '!';
-
- snprintfz(ssfilename, FILENAME_MAX, path_to_get_hw_sector_size, tf);
- FILE *fpss = fopen(ssfilename, "r");
- if(fpss) {
- char ssbuffer[1025];
- char *tmp = fgets(ssbuffer, 1024, fpss);
-
- if(tmp) {
- sector_size = atoi(tmp);
- if(sector_size <= 0) {
- error("Invalid sector size %d for device %s in %s. Assuming 512.", sector_size, disk, ssfilename);
- sector_size = 512;
- }
- }
- else error("Cannot read data for sector size for device %s from %s. Assuming 512.", disk, ssfilename);
-
- fclose(fpss);
- }
- else error("Cannot read sector size for device %s from %s. Assuming 512.", disk, ssfilename);
-
- st = rrdset_create(RRD_TYPE_DISK, disk, NULL, family, "disk.io", "Disk I/O Bandwidth", "kilobytes/s", 2000, update_every, RRDSET_TYPE_AREA);
-
- rrddim_add(st, "reads", NULL, sector_size, 1024, RRDDIM_INCREMENTAL);
- rrddim_add(st, "writes", NULL, sector_size * -1, 1024, RRDDIM_INCREMENTAL);
- }
- else rrdset_next_usec(st, dt);
-
- last_readsectors = rrddim_set(st, "reads", readsectors);
- last_writesectors = rrddim_set(st, "writes", writesectors);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(ddo_ops) {
- st = rrdset_find_bytype("disk_ops", disk);
- if(!st) {
- st = rrdset_create("disk_ops", disk, NULL, family, "disk.ops", "Disk Completed I/O Operations", "operations/s", 2001, update_every, RRDSET_TYPE_LINE);
- st->isdetail = 1;
-
- rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next_usec(st, dt);
-
- last_reads = rrddim_set(st, "reads", reads);
- last_writes = rrddim_set(st, "writes", writes);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(ddo_qops) {
- st = rrdset_find_bytype("disk_qops", disk);
- if(!st) {
- st = rrdset_create("disk_qops", disk, NULL, family, "disk.qops", "Disk Current I/O Operations", "operations", 2002, update_every, RRDSET_TYPE_LINE);
- st->isdetail = 1;
-
- rrddim_add(st, "operations", NULL, 1, 1, RRDDIM_ABSOLUTE);
- }
- else rrdset_next_usec(st, dt);
-
- rrddim_set(st, "operations", queued_ios);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(ddo_backlog) {
- st = rrdset_find_bytype("disk_backlog", disk);
- if(!st) {
- st = rrdset_create("disk_backlog", disk, NULL, family, "disk.backlog", "Disk Backlog", "backlog (ms)", 2003, update_every, RRDSET_TYPE_AREA);
- st->isdetail = 1;
-
- rrddim_add(st, "backlog", NULL, 1, 10, RRDDIM_INCREMENTAL);
- }
- else rrdset_next_usec(st, dt);
-
- rrddim_set(st, "backlog", backlog_ms);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(ddo_util) {
- st = rrdset_find_bytype("disk_util", disk);
- if(!st) {
- st = rrdset_create("disk_util", disk, NULL, family, "disk.util", "Disk Utilization Time", "% of time working", 2004, update_every, RRDSET_TYPE_AREA);
- st->isdetail = 1;
-
- rrddim_add(st, "utilization", NULL, 1, 10, RRDDIM_INCREMENTAL);
- }
- else rrdset_next_usec(st, dt);
-
- last_busy_ms = rrddim_set(st, "utilization", busy_ms);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(ddo_mops) {
- st = rrdset_find_bytype("disk_mops", disk);
- if(!st) {
- st = rrdset_create("disk_mops", disk, NULL, family, "disk.mops", "Disk Merged Operations", "merged operations/s", 2021, update_every, RRDSET_TYPE_LINE);
- st->isdetail = 1;
-
- rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next_usec(st, dt);
-
- rrddim_set(st, "reads", mreads);
- rrddim_set(st, "writes", mwrites);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(ddo_iotime) {
- st = rrdset_find_bytype("disk_iotime", disk);
- if(!st) {
- st = rrdset_create("disk_iotime", disk, NULL, family, "disk.iotime", "Disk Total I/O Time", "milliseconds/s", 2022, update_every, RRDSET_TYPE_LINE);
- st->isdetail = 1;
-
- rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next_usec(st, dt);
-
- last_readms = rrddim_set(st, "reads", readms);
- last_writems = rrddim_set(st, "writes", writems);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
- // calculate differential charts
- // only if this is not the first time we run
-
- if(dt) {
- if(ddo_iotime && ddo_ops) {
- st = rrdset_find_bytype("disk_await", disk);
- if(!st) {
- st = rrdset_create("disk_await", disk, NULL, family, "disk.await", "Average Completed I/O Operation Time", "ms per operation", 2005, update_every, RRDSET_TYPE_LINE);
- st->isdetail = 1;
-
- rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_ABSOLUTE);
- rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_ABSOLUTE);
- }
- else rrdset_next_usec(st, dt);
-
- rrddim_set(st, "reads", (reads - last_reads) ? (readms - last_readms) / (reads - last_reads) : 0);
- rrddim_set(st, "writes", (writes - last_writes) ? (writems - last_writems) / (writes - last_writes) : 0);
- rrdset_done(st);
- }
-
- if(ddo_io && ddo_ops) {
- st = rrdset_find_bytype("disk_avgsz", disk);
- if(!st) {
- st = rrdset_create("disk_avgsz", disk, NULL, family, "disk.avgsz", "Average Completed I/O Operation Bandwidth", "kilobytes per operation", 2006, update_every, RRDSET_TYPE_AREA);
- st->isdetail = 1;
-
- rrddim_add(st, "reads", NULL, sector_size, 1024, RRDDIM_ABSOLUTE);
- rrddim_add(st, "writes", NULL, -sector_size, 1024, RRDDIM_ABSOLUTE);
- }
- else rrdset_next_usec(st, dt);
-
- rrddim_set(st, "reads", (reads - last_reads) ? (readsectors - last_readsectors) / (reads - last_reads) : 0);
- rrddim_set(st, "writes", (writes - last_writes) ? (writesectors - last_writesectors) / (writes - last_writes) : 0);
- rrdset_done(st);
- }
-
- if(ddo_util && ddo_ops) {
- st = rrdset_find_bytype("disk_svctm", disk);
- if(!st) {
- st = rrdset_create("disk_svctm", disk, NULL, family, "disk.svctm", "Average Service Time", "ms per operation", 2007, update_every, RRDSET_TYPE_LINE);
- st->isdetail = 1;
-
- rrddim_add(st, "svctm", NULL, 1, 1, RRDDIM_ABSOLUTE);
- }
- else rrdset_next_usec(st, dt);
-
- rrddim_set(st, "svctm", ((reads - last_reads) + (writes - last_writes)) ? (busy_ms - last_busy_ms) / ((reads - last_reads) + (writes - last_writes)) : 0);
- rrdset_done(st);
- }
- }
- }
-
- return 0;
+ static procfile *ff = NULL;
+ static struct statvfs buff_statvfs;
+ static struct stat buff_stat;
+ static int global_enable_new_disks_detected_at_runtime = CONFIG_ONDEMAND_YES,
+ global_enable_performance_for_physical_disks = CONFIG_ONDEMAND_ONDEMAND,
+ global_enable_performance_for_virtual_disks = CONFIG_ONDEMAND_NO,
+ global_enable_performance_for_partitions = CONFIG_ONDEMAND_NO,
+ global_enable_performance_for_mountpoints = CONFIG_ONDEMAND_NO,
+ global_enable_performance_for_virtual_mountpoints = CONFIG_ONDEMAND_ONDEMAND,
+ global_enable_space_for_mountpoints = CONFIG_ONDEMAND_ONDEMAND,
+ global_do_io = CONFIG_ONDEMAND_ONDEMAND,
+ global_do_ops = CONFIG_ONDEMAND_ONDEMAND,
+ global_do_mops = CONFIG_ONDEMAND_ONDEMAND,
+ global_do_iotime = CONFIG_ONDEMAND_ONDEMAND,
+ global_do_qops = CONFIG_ONDEMAND_ONDEMAND,
+ global_do_util = CONFIG_ONDEMAND_ONDEMAND,
+ global_do_backlog = CONFIG_ONDEMAND_ONDEMAND,
+ global_do_space = CONFIG_ONDEMAND_ONDEMAND,
+ global_do_inodes = CONFIG_ONDEMAND_ONDEMAND,
+ globals_initialized = 0;
+
+ if(unlikely(!globals_initialized)) {
+ global_enable_new_disks_detected_at_runtime = config_get_boolean("plugin:proc:/proc/diskstats", "enable new disks detected at runtime", global_enable_new_disks_detected_at_runtime);
+
+ global_enable_performance_for_physical_disks = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "performance metrics for physical disks", global_enable_performance_for_physical_disks);
+ global_enable_performance_for_virtual_disks = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "performance metrics for virtual disks", global_enable_performance_for_virtual_disks);
+ global_enable_performance_for_partitions = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "performance metrics for partitions", global_enable_performance_for_partitions);
+ global_enable_performance_for_mountpoints = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "performance metrics for mounted filesystems", global_enable_performance_for_mountpoints);
+ global_enable_performance_for_virtual_mountpoints = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "performance metrics for mounted virtual disks", global_enable_performance_for_virtual_mountpoints);
+ global_enable_space_for_mountpoints = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "space metrics for mounted filesystems", global_enable_space_for_mountpoints);
+
+ global_do_io = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "bandwidth for all disks", global_do_io);
+ global_do_ops = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "operations for all disks", global_do_ops);
+ global_do_mops = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "merged operations for all disks", global_do_mops);
+ global_do_iotime = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "i/o time for all disks", global_do_iotime);
+ global_do_qops = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "queued operations for all disks", global_do_qops);
+ global_do_util = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "utilization percentage for all disks", global_do_util);
+ global_do_backlog = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "backlog for all disks", global_do_backlog);
+ global_do_space = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "space usage for all disks", global_do_space);
+ global_do_inodes = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "inodes usage for all disks", global_do_inodes);
+
+ globals_initialized = 1;
+ }
+
+ if(!ff) {
+ char filename[FILENAME_MAX + 1];
+ snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/diskstats");
+ ff = procfile_open(config_get("plugin:proc:/proc/diskstats", "filename to monitor", filename), " \t", PROCFILE_FLAG_DEFAULT);
+ }
+ if(!ff) return 1;
+
+ ff = procfile_readall(ff);
+ if(!ff) return 0; // we return 0, so that we will retry to open it next time
+
+ uint32_t lines = procfile_lines(ff), l;
+ uint32_t words;
+
+ for(l = 0; l < lines ;l++) {
+ // --------------------------------------------------------------------------
+ // Read parameters
+
+ char *disk;
+ unsigned long long major = 0, minor = 0,
+ reads = 0, mreads = 0, readsectors = 0, readms = 0,
+ writes = 0, mwrites = 0, writesectors = 0, writems = 0,
+ queued_ios = 0, busy_ms = 0, backlog_ms = 0,
+ space_avail = 0, space_avail_root = 0, space_used = 0,
+ inodes_avail = 0, inodes_avail_root = 0, inodes_used = 0;
+
+ unsigned long long last_reads = 0, last_readsectors = 0, last_readms = 0,
+ last_writes = 0, last_writesectors = 0, last_writems = 0,
+ last_busy_ms = 0;
+
+ words = procfile_linewords(ff, l);
+ if(words < 14) continue;
+
+ major = strtoull(procfile_lineword(ff, l, 0), NULL, 10);
+ minor = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
+ disk = procfile_lineword(ff, l, 2);
+
+ // # of reads completed # of writes completed
+ // This is the total number of reads or writes completed successfully.
+ reads = strtoull(procfile_lineword(ff, l, 3), NULL, 10); // rd_ios
+ writes = strtoull(procfile_lineword(ff, l, 7), NULL, 10); // wr_ios
+
+ // # of reads merged # of writes merged
+ // Reads and writes which are adjacent to each other may be merged for
+ // efficiency. Thus two 4K reads may become one 8K read before it is
+ // ultimately handed to the disk, and so it will be counted (and queued)
+ mreads = strtoull(procfile_lineword(ff, l, 4), NULL, 10); // rd_merges_or_rd_sec
+ mwrites = strtoull(procfile_lineword(ff, l, 8), NULL, 10); // wr_merges
+
+ // # of sectors read # of sectors written
+ // This is the total number of sectors read or written successfully.
+ readsectors = strtoull(procfile_lineword(ff, l, 5), NULL, 10); // rd_sec_or_wr_ios
+ writesectors = strtoull(procfile_lineword(ff, l, 9), NULL, 10); // wr_sec
+
+ // # of milliseconds spent reading # of milliseconds spent writing
+ // This is the total number of milliseconds spent by all reads or writes (as
+ // measured from __make_request() to end_that_request_last()).
+ readms = strtoull(procfile_lineword(ff, l, 6), NULL, 10); // rd_ticks_or_wr_sec
+ writems = strtoull(procfile_lineword(ff, l, 10), NULL, 10); // wr_ticks
+
+ // # of I/Os currently in progress
+ // The only field that should go to zero. Incremented as requests are
+ // given to appropriate struct request_queue and decremented as they finish.
+ queued_ios = strtoull(procfile_lineword(ff, l, 11), NULL, 10); // ios_pgr
+
+ // # of milliseconds spent doing I/Os
+ // This field increases so long as field queued_ios is nonzero.
+ busy_ms = strtoull(procfile_lineword(ff, l, 12), NULL, 10); // tot_ticks
+
+ // weighted # of milliseconds spent doing I/Os
+ // This field is incremented at each I/O start, I/O completion, I/O
+ // merge, or read of these stats by the number of I/Os in progress
+ // (field queued_ios) times the number of milliseconds spent doing I/O since the
+ // last update of this field. This can provide an easy measure of both
+ // I/O completion time and the backlog that may be accumulating.
+ backlog_ms = strtoull(procfile_lineword(ff, l, 13), NULL, 10); // rq_ticks
+
+
+ // --------------------------------------------------------------------------
+ // remove slashes from disk names
+ char *s;
+ for(s = disk; *s ;s++)
+ if(*s == '/') *s = '_';
+
+ // --------------------------------------------------------------------------
+ // get a disk structure for the disk
+
+ struct disk *d = get_disk(major, minor, disk);
+
+
+ // --------------------------------------------------------------------------
+ // Set its family based on mount point
+
+ char *family = d->mount_point;
+ if(!family) family = disk;
+
+
+ // --------------------------------------------------------------------------
+ // Check the configuration for the device
+
+ if(unlikely(!d->configured)) {
+ char var_name[4096 + 1];
+ snprintfz(var_name, 4096, "plugin:proc:/proc/diskstats:%s", disk);
+
+ int def_enable = config_get_boolean_ondemand(var_name, "enable", global_enable_new_disks_detected_at_runtime);
+ if(def_enable == CONFIG_ONDEMAND_NO) {
+ // the user does not want any metrics for this disk
+ d->do_io = CONFIG_ONDEMAND_NO;
+ d->do_ops = CONFIG_ONDEMAND_NO;
+ d->do_mops = CONFIG_ONDEMAND_NO;
+ d->do_iotime = CONFIG_ONDEMAND_NO;
+ d->do_qops = CONFIG_ONDEMAND_NO;
+ d->do_util = CONFIG_ONDEMAND_NO;
+ d->do_backlog = CONFIG_ONDEMAND_NO;
+ d->do_space = CONFIG_ONDEMAND_NO;
+ d->do_inodes = CONFIG_ONDEMAND_NO;
+ }
+ else {
+ // this disk is enabled
+ // check its direct settings
+
+ int def_performance = CONFIG_ONDEMAND_ONDEMAND;
+ int def_space = (d->mount_point)?CONFIG_ONDEMAND_ONDEMAND:CONFIG_ONDEMAND_NO;
+
+ // since this is 'on demand' we can figure the performance settings
+ // based on the type of disk
+
+ switch(d->type) {
+ case DISK_TYPE_PHYSICAL:
+ def_performance = global_enable_performance_for_physical_disks;
+ break;
+
+ case DISK_TYPE_PARTITION:
+ def_performance = global_enable_performance_for_partitions;
+ break;
+
+ case DISK_TYPE_CONTAINER:
+ def_performance = global_enable_performance_for_virtual_disks;
+
+ if(d->mount_point)
+ def_performance = select_positive_option(def_performance, global_enable_performance_for_virtual_mountpoints);
+
+ break;
+ }
+
+ if(d->mount_point)
+ def_performance = select_positive_option(def_performance, global_enable_performance_for_mountpoints);
+
+ // ------------------------------------------------------------
+ // now we have def_performance and def_space
+ // to work further
+
+ // def_performance
+ // check the user configuration (this will also show our 'on demand' decision)
+ def_performance = config_get_boolean_ondemand(var_name, "enable performance metrics", def_performance);
+
+ int ddo_io = CONFIG_ONDEMAND_NO,
+ ddo_ops = CONFIG_ONDEMAND_NO,
+ ddo_mops = CONFIG_ONDEMAND_NO,
+ ddo_iotime = CONFIG_ONDEMAND_NO,
+ ddo_qops = CONFIG_ONDEMAND_NO,
+ ddo_util = CONFIG_ONDEMAND_NO,
+ ddo_backlog = CONFIG_ONDEMAND_NO;
+
+ // we enable individual performance charts only when def_performance is not disabled
+ if(def_performance != CONFIG_ONDEMAND_NO) {
+ ddo_io = global_do_io,
+ ddo_ops = global_do_ops,
+ ddo_mops = global_do_mops,
+ ddo_iotime = global_do_iotime,
+ ddo_qops = global_do_qops,
+ ddo_util = global_do_util,
+ ddo_backlog = global_do_backlog;
+ }
+
+ d->do_io = config_get_boolean_ondemand(var_name, "bandwidth", ddo_io);
+ d->do_ops = config_get_boolean_ondemand(var_name, "operations", ddo_ops);
+ d->do_mops = config_get_boolean_ondemand(var_name, "merged operations", ddo_mops);
+ d->do_iotime = config_get_boolean_ondemand(var_name, "i/o time", ddo_iotime);
+ d->do_qops = config_get_boolean_ondemand(var_name, "queued operations", ddo_qops);
+ d->do_util = config_get_boolean_ondemand(var_name, "utilization percentage", ddo_util);
+ d->do_backlog = config_get_boolean_ondemand(var_name, "backlog", ddo_backlog);
+
+ // def_space
+ if(d->mount_point) {
+ // check the user configuration (this will also show our 'on demand' decision)
+ def_space = config_get_boolean_ondemand(var_name, "enable space metrics", def_space);
+
+ int ddo_space = def_space,
+ ddo_inodes = def_space;
+
+ d->do_space = config_get_boolean_ondemand(var_name, "space usage", ddo_space);
+ d->do_inodes = config_get_boolean_ondemand(var_name, "inodes usage", ddo_inodes);
+ }
+ else {
+ // don't show settings for this disk
+ d->do_space = CONFIG_ONDEMAND_NO;
+ d->do_inodes = CONFIG_ONDEMAND_NO;
+ }
+ }
+
+ d->configured = 1;
+ }
+
+ RRDSET *st;
+
+ // --------------------------------------------------------------------------
+ // Do performance metrics
+
+ if(d->do_io == CONFIG_ONDEMAND_YES || (d->do_io == CONFIG_ONDEMAND_ONDEMAND && (readsectors || writesectors))) {
+ d->do_io = CONFIG_ONDEMAND_YES;
+
+ st = rrdset_find_bytype(RRD_TYPE_DISK, disk);
+ if(!st) {
+ st = rrdset_create(RRD_TYPE_DISK, disk, NULL, family, "disk.io", "Disk I/O Bandwidth", "kilobytes/s", 2000, update_every, RRDSET_TYPE_AREA);
+
+ rrddim_add(st, "reads", NULL, d->sector_size, 1024, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "writes", NULL, d->sector_size * -1, 1024, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next_usec(st, dt);
+
+ last_readsectors = rrddim_set(st, "reads", readsectors);
+ last_writesectors = rrddim_set(st, "writes", writesectors);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(d->do_ops == CONFIG_ONDEMAND_YES || (d->do_ops == CONFIG_ONDEMAND_ONDEMAND && (reads || writes))) {
+ d->do_ops = CONFIG_ONDEMAND_YES;
+
+ st = rrdset_find_bytype("disk_ops", disk);
+ if(!st) {
+ st = rrdset_create("disk_ops", disk, NULL, family, "disk.ops", "Disk Completed I/O Operations", "operations/s", 2001, update_every, RRDSET_TYPE_LINE);
+ st->isdetail = 1;
+
+ rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next_usec(st, dt);
+
+ last_reads = rrddim_set(st, "reads", reads);
+ last_writes = rrddim_set(st, "writes", writes);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(d->do_qops == CONFIG_ONDEMAND_YES || (d->do_qops == CONFIG_ONDEMAND_ONDEMAND && queued_ios)) {
+ d->do_qops = CONFIG_ONDEMAND_YES;
+
+ st = rrdset_find_bytype("disk_qops", disk);
+ if(!st) {
+ st = rrdset_create("disk_qops", disk, NULL, family, "disk.qops", "Disk Current I/O Operations", "operations", 2002, update_every, RRDSET_TYPE_LINE);
+ st->isdetail = 1;
+
+ rrddim_add(st, "operations", NULL, 1, 1, RRDDIM_ABSOLUTE);
+ }
+ else rrdset_next_usec(st, dt);
+
+ rrddim_set(st, "operations", queued_ios);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(d->do_backlog == CONFIG_ONDEMAND_YES || (d->do_backlog == CONFIG_ONDEMAND_ONDEMAND && backlog_ms)) {
+ d->do_backlog = CONFIG_ONDEMAND_YES;
+
+ st = rrdset_find_bytype("disk_backlog", disk);
+ if(!st) {
+ st = rrdset_create("disk_backlog", disk, NULL, family, "disk.backlog", "Disk Backlog", "backlog (ms)", 2003, update_every, RRDSET_TYPE_AREA);
+ st->isdetail = 1;
+
+ rrddim_add(st, "backlog", NULL, 1, 10, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next_usec(st, dt);
+
+ rrddim_set(st, "backlog", backlog_ms);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(d->do_util == CONFIG_ONDEMAND_YES || (d->do_util == CONFIG_ONDEMAND_ONDEMAND && busy_ms)) {
+ d->do_util = CONFIG_ONDEMAND_YES;
+
+ st = rrdset_find_bytype("disk_util", disk);
+ if(!st) {
+ st = rrdset_create("disk_util", disk, NULL, family, "disk.util", "Disk Utilization Time", "% of time working", 2004, update_every, RRDSET_TYPE_AREA);
+ st->isdetail = 1;
+
+ rrddim_add(st, "utilization", NULL, 1, 10, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next_usec(st, dt);
+
+ last_busy_ms = rrddim_set(st, "utilization", busy_ms);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(d->do_mops == CONFIG_ONDEMAND_YES || (d->do_mops == CONFIG_ONDEMAND_ONDEMAND && (mreads || mwrites))) {
+ d->do_mops = CONFIG_ONDEMAND_YES;
+
+ st = rrdset_find_bytype("disk_mops", disk);
+ if(!st) {
+ st = rrdset_create("disk_mops", disk, NULL, family, "disk.mops", "Disk Merged Operations", "merged operations/s", 2021, update_every, RRDSET_TYPE_LINE);
+ st->isdetail = 1;
+
+ rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next_usec(st, dt);
+
+ rrddim_set(st, "reads", mreads);
+ rrddim_set(st, "writes", mwrites);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(d->do_iotime == CONFIG_ONDEMAND_YES || (d->do_iotime == CONFIG_ONDEMAND_ONDEMAND && (readms || writems))) {
+ d->do_iotime = CONFIG_ONDEMAND_YES;
+
+ st = rrdset_find_bytype("disk_iotime", disk);
+ if(!st) {
+ st = rrdset_create("disk_iotime", disk, NULL, family, "disk.iotime", "Disk Total I/O Time", "milliseconds/s", 2022, update_every, RRDSET_TYPE_LINE);
+ st->isdetail = 1;
+
+ rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next_usec(st, dt);
+
+ last_readms = rrddim_set(st, "reads", readms);
+ last_writems = rrddim_set(st, "writes", writems);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+ // calculate differential charts
+ // only if this is not the first time we run
+
+ if(dt) {
+ if( (d->do_iotime == CONFIG_ONDEMAND_YES || (d->do_iotime == CONFIG_ONDEMAND_ONDEMAND && (readms || writems))) &&
+ (d->do_ops == CONFIG_ONDEMAND_YES || (d->do_ops == CONFIG_ONDEMAND_ONDEMAND && (reads || writes)))) {
+ st = rrdset_find_bytype("disk_await", disk);
+ if(!st) {
+ st = rrdset_create("disk_await", disk, NULL, family, "disk.await", "Average Completed I/O Operation Time", "ms per operation", 2005, update_every, RRDSET_TYPE_LINE);
+ st->isdetail = 1;
+
+ rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_ABSOLUTE);
+ rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_ABSOLUTE);
+ }
+ else rrdset_next_usec(st, dt);
+
+ rrddim_set(st, "reads", (reads - last_reads) ? (readms - last_readms) / (reads - last_reads) : 0);
+ rrddim_set(st, "writes", (writes - last_writes) ? (writems - last_writems) / (writes - last_writes) : 0);
+ rrdset_done(st);
+ }
+
+ if( (d->do_io == CONFIG_ONDEMAND_YES || (d->do_io == CONFIG_ONDEMAND_ONDEMAND && (readsectors || writesectors))) &&
+ (d->do_ops == CONFIG_ONDEMAND_YES || (d->do_ops == CONFIG_ONDEMAND_ONDEMAND && (reads || writes)))) {
+ st = rrdset_find_bytype("disk_avgsz", disk);
+ if(!st) {
+ st = rrdset_create("disk_avgsz", disk, NULL, family, "disk.avgsz", "Average Completed I/O Operation Bandwidth", "kilobytes per operation", 2006, update_every, RRDSET_TYPE_AREA);
+ st->isdetail = 1;
+
+ rrddim_add(st, "reads", NULL, d->sector_size, 1024, RRDDIM_ABSOLUTE);
+ rrddim_add(st, "writes", NULL, d->sector_size * -1, 1024, RRDDIM_ABSOLUTE);
+ }
+ else rrdset_next_usec(st, dt);
+
+ rrddim_set(st, "reads", (reads - last_reads) ? (readsectors - last_readsectors) / (reads - last_reads) : 0);
+ rrddim_set(st, "writes", (writes - last_writes) ? (writesectors - last_writesectors) / (writes - last_writes) : 0);
+ rrdset_done(st);
+ }
+
+ if( (d->do_util == CONFIG_ONDEMAND_YES || (d->do_util == CONFIG_ONDEMAND_ONDEMAND && busy_ms)) &&
+ (d->do_ops == CONFIG_ONDEMAND_YES || (d->do_ops == CONFIG_ONDEMAND_ONDEMAND && (reads || writes)))) {
+ st = rrdset_find_bytype("disk_svctm", disk);
+ if(!st) {
+ st = rrdset_create("disk_svctm", disk, NULL, family, "disk.svctm", "Average Service Time", "ms per operation", 2007, update_every, RRDSET_TYPE_LINE);
+ st->isdetail = 1;
+
+ rrddim_add(st, "svctm", NULL, 1, 1, RRDDIM_ABSOLUTE);
+ }
+ else rrdset_next_usec(st, dt);
+
+ rrddim_set(st, "svctm", ((reads - last_reads) + (writes - last_writes)) ? (busy_ms - last_busy_ms) / ((reads - last_reads) + (writes - last_writes)) : 0);
+ rrdset_done(st);
+ }
+ }
+
+ // --------------------------------------------------------------------------
+ // space metrics
+
+ if(d->mount_point && (d->do_space || d->do_inodes) ) {
+ // collect space metrics using statvfs
+
+ if (statvfs(d->mount_point, &buff_statvfs) < 0)
+ error("Failed statvfs() for '%s' (disk '%s')", d->mount_point, d->disk);
+ else {
+ space_avail = buff_statvfs.f_bavail * buff_statvfs.f_bsize;
+ space_avail_root = (buff_statvfs.f_bfree - buff_statvfs.f_bavail) * buff_statvfs.f_bsize;
+ space_used = (buff_statvfs.f_blocks - buff_statvfs.f_bfree) * buff_statvfs.f_bsize;
+
+ inodes_avail = buff_statvfs.f_favail;
+ inodes_avail_root = buff_statvfs.f_ffree - buff_statvfs.f_favail;
+ inodes_used = buff_statvfs.f_files - buff_statvfs.f_ffree;
+
+ // verify we collected the metrics for the right disk.
+ // if not the mountpoint has changed.
+
+ if(stat(d->mount_point, &buff_stat) == -1)
+ error("Failed to stat() for '%s' (disk '%s')", d->mount_point, d->disk);
+ else {
+ if(major(buff_stat.st_dev) == major && minor(buff_stat.st_dev) == minor) {
+
+ // --------------------------------------------------------------------------
+
+ if(d->do_space == CONFIG_ONDEMAND_YES || (d->do_space == CONFIG_ONDEMAND_ONDEMAND && (space_avail || space_avail_root || space_used))) {
+ st = rrdset_find_bytype("disk_space", disk);
+ if(!st) {
+ st = rrdset_create("disk_space", disk, NULL, family, "disk.space", "Disk Space Usage", "GB", 2023, update_every, RRDSET_TYPE_STACKED);
+ st->isdetail = 1;
+
+ rrddim_add(st, "avail", NULL, 1, 1024*1024*1024, RRDDIM_ABSOLUTE);
+ rrddim_add(st, "used" , NULL, 1, 1024*1024*1024, RRDDIM_ABSOLUTE);
+ rrddim_add(st, "reserved_for_root", "reserved for root", 1, 1024*1024*1024, RRDDIM_ABSOLUTE);
+ }
+ else rrdset_next_usec(st, dt);
+
+ rrddim_set(st, "avail", space_avail);
+ rrddim_set(st, "used", space_used);
+ rrddim_set(st, "reserved_for_root", space_avail_root);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------------
+
+ if(d->do_inodes == CONFIG_ONDEMAND_YES || (d->do_inodes == CONFIG_ONDEMAND_ONDEMAND && (inodes_avail || inodes_avail_root || inodes_used))) {
+ st = rrdset_find_bytype("disk_inodes", disk);
+ if(!st) {
+ st = rrdset_create("disk_inodes", disk, NULL, family, "disk.inodes", "Disk Inodes Usage", "Inodes", 2024, update_every, RRDSET_TYPE_STACKED);
+ st->isdetail = 1;
+
+ rrddim_add(st, "avail", NULL, 1, 1, RRDDIM_ABSOLUTE);
+ rrddim_add(st, "used" , NULL, 1, 1, RRDDIM_ABSOLUTE);
+ rrddim_add(st, "reserved_for_root", "reserved for root", 1, 1, RRDDIM_ABSOLUTE);
+ }
+ else rrdset_next_usec(st, dt);
+
+ rrddim_set(st, "avail", inodes_avail);
+ rrddim_set(st, "used", inodes_used);
+ rrddim_set(st, "reserved_for_root", inodes_avail_root);
+ rrdset_done(st);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return 0;
}
diff --git a/src/proc_interrupts.c b/src/proc_interrupts.c
index ad00c2022..fe0e9b1a5 100644
--- a/src/proc_interrupts.c
+++ b/src/proc_interrupts.c
@@ -1,26 +1,13 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-
#include "common.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "rrd.h"
-#include "plugin_proc.h"
-#include "log.h"
#define MAX_INTERRUPT_NAME 50
struct interrupt {
- int used;
- char *id;
- char name[MAX_INTERRUPT_NAME + 1];
- unsigned long long total;
- unsigned long long value[];
+ int used;
+ char *id;
+ char name[MAX_INTERRUPT_NAME + 1];
+ unsigned long long total;
+ unsigned long long value[];
};
// since each interrupt is variable in size
@@ -31,160 +18,157 @@ struct interrupt {
#define irrindex(base, line, cpus) ((struct interrupt *)&((char *)(base))[line * recordsize(cpus)])
static inline struct interrupt *get_interrupts_array(int lines, int cpus) {
- static struct interrupt *irrs = NULL;
- static int allocated = 0;
-
- if(lines < allocated) return irrs;
- else {
- irrs = (struct interrupt *)realloc(irrs, lines * recordsize(cpus));
- if(!irrs)
- fatal("Cannot allocate memory for %d interrupts", lines);
+ static struct interrupt *irrs = NULL;
+ static int allocated = 0;
- allocated = lines;
- }
+ if(lines < allocated) return irrs;
+ else {
+ irrs = (struct interrupt *)reallocz(irrs, lines * recordsize(cpus));
+ allocated = lines;
+ }
- return irrs;
+ return irrs;
}
int do_proc_interrupts(int update_every, unsigned long long dt) {
- static procfile *ff = NULL;
- static int cpus = -1, do_per_core = -1;
- struct interrupt *irrs = NULL;
-
- if(dt) {};
-
- if(do_per_core == -1) do_per_core = config_get_boolean("plugin:proc:/proc/interrupts", "interrupts per core", 1);
-
- if(!ff) {
- char filename[FILENAME_MAX + 1];
- snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/interrupts");
- ff = procfile_open(config_get("plugin:proc:/proc/interrupts", "filename to monitor", filename), " \t", PROCFILE_FLAG_DEFAULT);
- }
- if(!ff) return 1;
-
- ff = procfile_readall(ff);
- if(!ff) return 0; // we return 0, so that we will retry to open it next time
-
- uint32_t lines = procfile_lines(ff), l;
- uint32_t words = procfile_linewords(ff, 0), w;
-
- if(!lines) {
- error("Cannot read /proc/interrupts, zero lines reported.");
- return 1;
- }
-
- // find how many CPUs are there
- if(cpus == -1) {
- cpus = 0;
- for(w = 0; w < words ; w++) {
- if(strncmp(procfile_lineword(ff, 0, w), "CPU", 3) == 0)
- cpus++;
- }
- }
-
- if(!cpus) {
- error("PLUGIN: PROC_INTERRUPTS: Cannot find the number of CPUs in /proc/interrupts");
- return 1;
- }
-
- // allocate the size we need;
- irrs = get_interrupts_array(lines, cpus);
- irrs[0].used = 0;
-
- // loop through all lines
- for(l = 1; l < lines ;l++) {
- struct interrupt *irr = irrindex(irrs, l, cpus);
- irr->used = 0;
- irr->total = 0;
-
- words = procfile_linewords(ff, l);
- if(!words) continue;
-
- irr->id = procfile_lineword(ff, l, 0);
- if(!irr->id || !irr->id[0]) continue;
-
- int idlen = strlen(irr->id);
- if(irr->id[idlen - 1] == ':')
- irr->id[idlen - 1] = '\0';
-
- int c;
- for(c = 0; c < cpus ;c++) {
- if((c + 1) < (int)words)
- irr->value[c] = strtoull(procfile_lineword(ff, l, (uint32_t)(c + 1)), NULL, 10);
- else
- irr->value[c] = 0;
-
- irr->total += irr->value[c];
- }
-
- if(isdigit(irr->id[0]) && (uint32_t)(cpus + 2) < words) {
- strncpyz(irr->name, procfile_lineword(ff, l, words - 1), MAX_INTERRUPT_NAME);
- int nlen = strlen(irr->name);
- if(nlen < (MAX_INTERRUPT_NAME-1)) {
- irr->name[nlen] = '_';
- strncpyz(&irr->name[nlen + 1], irr->id, MAX_INTERRUPT_NAME - nlen);
- }
- }
- else {
- strncpyz(irr->name, irr->id, MAX_INTERRUPT_NAME);
- }
-
- irr->used = 1;
- }
-
- RRDSET *st;
-
- // --------------------------------------------------------------------
-
- st = rrdset_find_bytype("system", "interrupts");
- if(!st) {
- st = rrdset_create("system", "interrupts", NULL, "interrupts", NULL, "System interrupts", "interrupts/s", 1000, update_every, RRDSET_TYPE_STACKED);
-
- for(l = 0; l < lines ;l++) {
- struct interrupt *irr = irrindex(irrs, l, cpus);
- if(!irr->used) continue;
- rrddim_add(st, irr->id, irr->name, 1, 1, RRDDIM_INCREMENTAL);
- }
- }
- else rrdset_next(st);
-
- for(l = 0; l < lines ;l++) {
- struct interrupt *irr = irrindex(irrs, l, cpus);
- if(!irr->used) continue;
- rrddim_set(st, irr->id, irr->total);
- }
- rrdset_done(st);
-
- if(do_per_core) {
- int c;
-
- for(c = 0; c < cpus ; c++) {
- char id[256+1];
- snprintfz(id, 256, "cpu%d_interrupts", c);
-
- st = rrdset_find_bytype("cpu", id);
- if(!st) {
- char name[256+1], title[256+1];
- snprintfz(name, 256, "cpu%d_interrupts", c);
- snprintfz(title, 256, "CPU%d Interrupts", c);
- st = rrdset_create("cpu", id, name, "interrupts", "cpu.interrupts", title, "interrupts/s", 2000 + c, update_every, RRDSET_TYPE_STACKED);
-
- for(l = 0; l < lines ;l++) {
- struct interrupt *irr = irrindex(irrs, l, cpus);
- if(!irr->used) continue;
- rrddim_add(st, irr->id, irr->name, 1, 1, RRDDIM_INCREMENTAL);
- }
- }
- else rrdset_next(st);
-
- for(l = 0; l < lines ;l++) {
- struct interrupt *irr = irrindex(irrs, l, cpus);
- if(!irr->used) continue;
- rrddim_set(st, irr->id, irr->value[c]);
- }
- rrdset_done(st);
- }
- }
-
- return 0;
+ static procfile *ff = NULL;
+ static int cpus = -1, do_per_core = -1;
+ struct interrupt *irrs = NULL;
+
+ if(dt) {};
+
+ if(do_per_core == -1) do_per_core = config_get_boolean("plugin:proc:/proc/interrupts", "interrupts per core", 1);
+
+ if(!ff) {
+ char filename[FILENAME_MAX + 1];
+ snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/interrupts");
+ ff = procfile_open(config_get("plugin:proc:/proc/interrupts", "filename to monitor", filename), " \t", PROCFILE_FLAG_DEFAULT);
+ }
+ if(!ff) return 1;
+
+ ff = procfile_readall(ff);
+ if(!ff) return 0; // we return 0, so that we will retry to open it next time
+
+ uint32_t lines = procfile_lines(ff), l;
+ uint32_t words = procfile_linewords(ff, 0), w;
+
+ if(!lines) {
+ error("Cannot read /proc/interrupts, zero lines reported.");
+ return 1;
+ }
+
+ // find how many CPUs are there
+ if(cpus == -1) {
+ cpus = 0;
+ for(w = 0; w < words ; w++) {
+ if(strncmp(procfile_lineword(ff, 0, w), "CPU", 3) == 0)
+ cpus++;
+ }
+ }
+
+ if(!cpus) {
+ error("PLUGIN: PROC_INTERRUPTS: Cannot find the number of CPUs in /proc/interrupts");
+ return 1;
+ }
+
+ // allocate the size we need;
+ irrs = get_interrupts_array(lines, cpus);
+ irrs[0].used = 0;
+
+ // loop through all lines
+ for(l = 1; l < lines ;l++) {
+ struct interrupt *irr = irrindex(irrs, l, cpus);
+ irr->used = 0;
+ irr->total = 0;
+
+ words = procfile_linewords(ff, l);
+ if(!words) continue;
+
+ irr->id = procfile_lineword(ff, l, 0);
+ if(!irr->id || !irr->id[0]) continue;
+
+ int idlen = strlen(irr->id);
+ if(irr->id[idlen - 1] == ':')
+ irr->id[idlen - 1] = '\0';
+
+ int c;
+ for(c = 0; c < cpus ;c++) {
+ if((c + 1) < (int)words)
+ irr->value[c] = strtoull(procfile_lineword(ff, l, (uint32_t)(c + 1)), NULL, 10);
+ else
+ irr->value[c] = 0;
+
+ irr->total += irr->value[c];
+ }
+
+ if(isdigit(irr->id[0]) && (uint32_t)(cpus + 2) < words) {
+ strncpyz(irr->name, procfile_lineword(ff, l, words - 1), MAX_INTERRUPT_NAME);
+ int nlen = strlen(irr->name);
+ if(nlen < (MAX_INTERRUPT_NAME-1)) {
+ irr->name[nlen] = '_';
+ strncpyz(&irr->name[nlen + 1], irr->id, MAX_INTERRUPT_NAME - nlen);
+ }
+ }
+ else {
+ strncpyz(irr->name, irr->id, MAX_INTERRUPT_NAME);
+ }
+
+ irr->used = 1;
+ }
+
+ RRDSET *st;
+
+ // --------------------------------------------------------------------
+
+ st = rrdset_find_bytype("system", "interrupts");
+ if(!st) {
+ st = rrdset_create("system", "interrupts", NULL, "interrupts", NULL, "System interrupts", "interrupts/s", 1000, update_every, RRDSET_TYPE_STACKED);
+
+ for(l = 0; l < lines ;l++) {
+ struct interrupt *irr = irrindex(irrs, l, cpus);
+ if(!irr->used) continue;
+ rrddim_add(st, irr->id, irr->name, 1, 1, RRDDIM_INCREMENTAL);
+ }
+ }
+ else rrdset_next(st);
+
+ for(l = 0; l < lines ;l++) {
+ struct interrupt *irr = irrindex(irrs, l, cpus);
+ if(!irr->used) continue;
+ rrddim_set(st, irr->id, irr->total);
+ }
+ rrdset_done(st);
+
+ if(do_per_core) {
+ int c;
+
+ for(c = 0; c < cpus ; c++) {
+ char id[256+1];
+ snprintfz(id, 256, "cpu%d_interrupts", c);
+
+ st = rrdset_find_bytype("cpu", id);
+ if(!st) {
+ char name[256+1], title[256+1];
+ snprintfz(name, 256, "cpu%d_interrupts", c);
+ snprintfz(title, 256, "CPU%d Interrupts", c);
+ st = rrdset_create("cpu", id, name, "interrupts", "cpu.interrupts", title, "interrupts/s", 2000 + c, update_every, RRDSET_TYPE_STACKED);
+
+ for(l = 0; l < lines ;l++) {
+ struct interrupt *irr = irrindex(irrs, l, cpus);
+ if(!irr->used) continue;
+ rrddim_add(st, irr->id, irr->name, 1, 1, RRDDIM_INCREMENTAL);
+ }
+ }
+ else rrdset_next(st);
+
+ for(l = 0; l < lines ;l++) {
+ struct interrupt *irr = irrindex(irrs, l, cpus);
+ if(!irr->used) continue;
+ rrddim_set(st, irr->id, irr->value[c]);
+ }
+ rrdset_done(st);
+ }
+ }
+
+ return 0;
}
diff --git a/src/proc_loadavg.c b/src/proc_loadavg.c
index c8e893b99..44ea70191 100644
--- a/src/proc_loadavg.c
+++ b/src/proc_loadavg.c
@@ -1,89 +1,80 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "log.h"
#include "common.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "rrd.h"
-#include "plugin_proc.h"
+
+// linux calculates this once every 5 seconds
+#define MIN_LOADAVG_UPDATE_EVERY 5
int do_proc_loadavg(int update_every, unsigned long long dt) {
- static procfile *ff = NULL;
- static int do_loadavg = -1, do_all_processes = -1;
+ static procfile *ff = NULL;
+ static int do_loadavg = -1, do_all_processes = -1;
- if(dt) {};
+ if(dt) {};
- if(!ff) {
- char filename[FILENAME_MAX + 1];
- snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/loadavg");
- ff = procfile_open(config_get("plugin:proc:/proc/loadavg", "filename to monitor", filename), " \t,:|/", PROCFILE_FLAG_DEFAULT);
- }
- if(!ff) return 1;
+ if(!ff) {
+ char filename[FILENAME_MAX + 1];
+ snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/loadavg");
+ ff = procfile_open(config_get("plugin:proc:/proc/loadavg", "filename to monitor", filename), " \t,:|/", PROCFILE_FLAG_DEFAULT);
+ }
+ if(!ff) return 1;
- ff = procfile_readall(ff);
- if(!ff) return 0; // we return 0, so that we will retry to open it next time
+ ff = procfile_readall(ff);
+ if(!ff) return 0; // we return 0, so that we will retry to open it next time
- if(do_loadavg == -1) do_loadavg = config_get_boolean("plugin:proc:/proc/loadavg", "enable load average", 1);
- if(do_all_processes == -1) do_all_processes = config_get_boolean("plugin:proc:/proc/loadavg", "enable total processes", 1);
+ if(do_loadavg == -1) do_loadavg = config_get_boolean("plugin:proc:/proc/loadavg", "enable load average", 1);
+ if(do_all_processes == -1) do_all_processes = config_get_boolean("plugin:proc:/proc/loadavg", "enable total processes", 1);
- if(procfile_lines(ff) < 1) {
- error("/proc/loadavg has no lines.");
- return 1;
- }
- if(procfile_linewords(ff, 0) < 6) {
- error("/proc/loadavg has less than 6 words in it.");
- return 1;
- }
+ if(procfile_lines(ff) < 1) {
+ error("/proc/loadavg has no lines.");
+ return 1;
+ }
+ if(procfile_linewords(ff, 0) < 6) {
+ error("/proc/loadavg has less than 6 words in it.");
+ return 1;
+ }
- double load1 = strtod(procfile_lineword(ff, 0, 0), NULL);
- double load5 = strtod(procfile_lineword(ff, 0, 1), NULL);
- double load15 = strtod(procfile_lineword(ff, 0, 2), NULL);
+ double load1 = strtod(procfile_lineword(ff, 0, 0), NULL);
+ double load5 = strtod(procfile_lineword(ff, 0, 1), NULL);
+ double load15 = strtod(procfile_lineword(ff, 0, 2), NULL);
- //unsigned long long running_processes = strtoull(procfile_lineword(ff, 0, 3), NULL, 10);
- unsigned long long active_processes = strtoull(procfile_lineword(ff, 0, 4), NULL, 10);
- //unsigned long long next_pid = strtoull(procfile_lineword(ff, 0, 5), NULL, 10);
+ //unsigned long long running_processes = strtoull(procfile_lineword(ff, 0, 3), NULL, 10);
+ unsigned long long active_processes = strtoull(procfile_lineword(ff, 0, 4), NULL, 10);
+ //unsigned long long next_pid = strtoull(procfile_lineword(ff, 0, 5), NULL, 10);
- RRDSET *st;
+ RRDSET *st;
- // --------------------------------------------------------------------
+ // --------------------------------------------------------------------
- if(do_loadavg) {
- st = rrdset_find_byname("system.load");
- if(!st) {
- st = rrdset_create("system", "load", NULL, "load", NULL, "System Load Average", "load", 100, update_every, RRDSET_TYPE_LINE);
+ if(do_loadavg) {
+ st = rrdset_find_byname("system.load");
+ if(!st) {
+ st = rrdset_create("system", "load", NULL, "load", NULL, "System Load Average", "load", 100, (update_every < MIN_LOADAVG_UPDATE_EVERY)?MIN_LOADAVG_UPDATE_EVERY:update_every, RRDSET_TYPE_LINE);
- rrddim_add(st, "load1", NULL, 1, 1000, RRDDIM_ABSOLUTE);
- rrddim_add(st, "load5", NULL, 1, 1000, RRDDIM_ABSOLUTE);
- rrddim_add(st, "load15", NULL, 1, 1000, RRDDIM_ABSOLUTE);
- }
- else rrdset_next(st);
+ rrddim_add(st, "load1", NULL, 1, 1000, RRDDIM_ABSOLUTE);
+ rrddim_add(st, "load5", NULL, 1, 1000, RRDDIM_ABSOLUTE);
+ rrddim_add(st, "load15", NULL, 1, 1000, RRDDIM_ABSOLUTE);
+ }
+ else rrdset_next(st);
- rrddim_set(st, "load1", load1 * 1000);
- rrddim_set(st, "load5", load5 * 1000);
- rrddim_set(st, "load15", load15 * 1000);
- rrdset_done(st);
- }
+ rrddim_set(st, "load1", load1 * 1000);
+ rrddim_set(st, "load5", load5 * 1000);
+ rrddim_set(st, "load15", load15 * 1000);
+ rrdset_done(st);
+ }
- // --------------------------------------------------------------------
+ // --------------------------------------------------------------------
- if(do_all_processes) {
- st = rrdset_find_byname("system.active_processes");
- if(!st) {
- st = rrdset_create("system", "active_processes", NULL, "processes", NULL, "System Active Processes", "processes", 750, update_every, RRDSET_TYPE_LINE);
+ if(do_all_processes) {
+ st = rrdset_find_byname("system.active_processes");
+ if(!st) {
+ st = rrdset_create("system", "active_processes", NULL, "processes", NULL, "System Active Processes", "processes", 750, update_every, RRDSET_TYPE_LINE);
- rrddim_add(st, "active", NULL, 1, 1, RRDDIM_ABSOLUTE);
- }
- else rrdset_next(st);
+ rrddim_add(st, "active", NULL, 1, 1, RRDDIM_ABSOLUTE);
+ }
+ else rrdset_next(st);
- rrddim_set(st, "active", active_processes);
- rrdset_done(st);
- }
+ rrddim_set(st, "active", active_processes);
+ rrdset_done(st);
+ }
- return 0;
+ return 0;
}
diff --git a/src/proc_meminfo.c b/src/proc_meminfo.c
index 611b4ed21..4295cd6da 100644
--- a/src/proc_meminfo.c
+++ b/src/proc_meminfo.c
@@ -1,254 +1,242 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
#include "common.h"
-#include "log.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "rrd.h"
-#include "plugin_proc.h"
#define MAX_PROC_MEMINFO_LINE 4096
#define MAX_PROC_MEMINFO_NAME 1024
int do_proc_meminfo(int update_every, unsigned long long dt) {
- static procfile *ff = NULL;
-
- static int do_ram = -1, do_swap = -1, do_hwcorrupt = -1, do_committed = -1, do_writeback = -1, do_kernel = -1, do_slab = -1;
-
- if(do_ram == -1) do_ram = config_get_boolean("plugin:proc:/proc/meminfo", "system ram", 1);
- if(do_swap == -1) do_swap = config_get_boolean("plugin:proc:/proc/meminfo", "system swap", 1);
- if(do_hwcorrupt == -1) do_hwcorrupt = config_get_boolean_ondemand("plugin:proc:/proc/meminfo", "hardware corrupted ECC", CONFIG_ONDEMAND_ONDEMAND);
- if(do_committed == -1) do_committed = config_get_boolean("plugin:proc:/proc/meminfo", "committed memory", 1);
- if(do_writeback == -1) do_writeback = config_get_boolean("plugin:proc:/proc/meminfo", "writeback memory", 1);
- if(do_kernel == -1) do_kernel = config_get_boolean("plugin:proc:/proc/meminfo", "kernel memory", 1);
- if(do_slab == -1) do_slab = config_get_boolean("plugin:proc:/proc/meminfo", "slab memory", 1);
-
- if(dt) {};
-
- if(!ff) {
- char filename[FILENAME_MAX + 1];
- snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/meminfo");
- ff = procfile_open(config_get("plugin:proc:/proc/meminfo", "filename to monitor", filename), " \t:", PROCFILE_FLAG_DEFAULT);
- }
- if(!ff) return 1;
-
- ff = procfile_readall(ff);
- if(!ff) return 0; // we return 0, so that we will retry to open it next time
-
- uint32_t lines = procfile_lines(ff), l;
- uint32_t words;
-
- int hwcorrupted = 0;
-
- unsigned long long MemTotal = 0, MemFree = 0, Buffers = 0, Cached = 0, SwapCached = 0,
- Active = 0, Inactive = 0, ActiveAnon = 0, InactiveAnon = 0, ActiveFile = 0, InactiveFile = 0,
- Unevictable = 0, Mlocked = 0, SwapTotal = 0, SwapFree = 0, Dirty = 0, Writeback = 0, AnonPages = 0,
- Mapped = 0, Shmem = 0, Slab = 0, SReclaimable = 0, SUnreclaim = 0, KernelStack = 0, PageTables = 0,
- NFS_Unstable = 0, Bounce = 0, WritebackTmp = 0, CommitLimit = 0, Committed_AS = 0,
- VmallocTotal = 0, VmallocUsed = 0, VmallocChunk = 0,
- AnonHugePages = 0, HugePages_Total = 0, HugePages_Free = 0, HugePages_Rsvd = 0, HugePages_Surp = 0, Hugepagesize = 0,
- DirectMap4k = 0, DirectMap2M = 0, HardwareCorrupted = 0;
-
- for(l = 0; l < lines ;l++) {
- words = procfile_linewords(ff, l);
- if(words < 2) continue;
-
- char *name = procfile_lineword(ff, l, 0);
- unsigned long long value = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
-
- if(!MemTotal && strcmp(name, "MemTotal") == 0) MemTotal = value;
- else if(!MemFree && strcmp(name, "MemFree") == 0) MemFree = value;
- else if(!Buffers && strcmp(name, "Buffers") == 0) Buffers = value;
- else if(!Cached && strcmp(name, "Cached") == 0) Cached = value;
- else if(!SwapCached && strcmp(name, "SwapCached") == 0) SwapCached = value;
- else if(!Active && strcmp(name, "Active") == 0) Active = value;
- else if(!Inactive && strcmp(name, "Inactive") == 0) Inactive = value;
- else if(!ActiveAnon && strcmp(name, "ActiveAnon") == 0) ActiveAnon = value;
- else if(!InactiveAnon && strcmp(name, "InactiveAnon") == 0) InactiveAnon = value;
- else if(!ActiveFile && strcmp(name, "ActiveFile") == 0) ActiveFile = value;
- else if(!InactiveFile && strcmp(name, "InactiveFile") == 0) InactiveFile = value;
- else if(!Unevictable && strcmp(name, "Unevictable") == 0) Unevictable = value;
- else if(!Mlocked && strcmp(name, "Mlocked") == 0) Mlocked = value;
- else if(!SwapTotal && strcmp(name, "SwapTotal") == 0) SwapTotal = value;
- else if(!SwapFree && strcmp(name, "SwapFree") == 0) SwapFree = value;
- else if(!Dirty && strcmp(name, "Dirty") == 0) Dirty = value;
- else if(!Writeback && strcmp(name, "Writeback") == 0) Writeback = value;
- else if(!AnonPages && strcmp(name, "AnonPages") == 0) AnonPages = value;
- else if(!Mapped && strcmp(name, "Mapped") == 0) Mapped = value;
- else if(!Shmem && strcmp(name, "Shmem") == 0) Shmem = value;
- else if(!Slab && strcmp(name, "Slab") == 0) Slab = value;
- else if(!SReclaimable && strcmp(name, "SReclaimable") == 0) SReclaimable = value;
- else if(!SUnreclaim && strcmp(name, "SUnreclaim") == 0) SUnreclaim = value;
- else if(!KernelStack && strcmp(name, "KernelStack") == 0) KernelStack = value;
- else if(!PageTables && strcmp(name, "PageTables") == 0) PageTables = value;
- else if(!NFS_Unstable && strcmp(name, "NFS_Unstable") == 0) NFS_Unstable = value;
- else if(!Bounce && strcmp(name, "Bounce") == 0) Bounce = value;
- else if(!WritebackTmp && strcmp(name, "WritebackTmp") == 0) WritebackTmp = value;
- else if(!CommitLimit && strcmp(name, "CommitLimit") == 0) CommitLimit = value;
- else if(!Committed_AS && strcmp(name, "Committed_AS") == 0) Committed_AS = value;
- else if(!VmallocTotal && strcmp(name, "VmallocTotal") == 0) VmallocTotal = value;
- else if(!VmallocUsed && strcmp(name, "VmallocUsed") == 0) VmallocUsed = value;
- else if(!VmallocChunk && strcmp(name, "VmallocChunk") == 0) VmallocChunk = value;
- else if(!HardwareCorrupted && strcmp(name, "HardwareCorrupted") == 0) { HardwareCorrupted = value; hwcorrupted = 1; }
- else if(!AnonHugePages && strcmp(name, "AnonHugePages") == 0) AnonHugePages = value;
- else if(!HugePages_Total && strcmp(name, "HugePages_Total") == 0) HugePages_Total = value;
- else if(!HugePages_Free && strcmp(name, "HugePages_Free") == 0) HugePages_Free = value;
- else if(!HugePages_Rsvd && strcmp(name, "HugePages_Rsvd") == 0) HugePages_Rsvd = value;
- else if(!HugePages_Surp && strcmp(name, "HugePages_Surp") == 0) HugePages_Surp = value;
- else if(!Hugepagesize && strcmp(name, "Hugepagesize") == 0) Hugepagesize = value;
- else if(!DirectMap4k && strcmp(name, "DirectMap4k") == 0) DirectMap4k = value;
- else if(!DirectMap2M && strcmp(name, "DirectMap2M") == 0) DirectMap2M = value;
- }
-
- RRDSET *st;
-
- // --------------------------------------------------------------------
-
- // http://stackoverflow.com/questions/3019748/how-to-reliably-measure-available-memory-in-linux
- unsigned long long MemUsed = MemTotal - MemFree - Cached - Buffers;
-
- if(do_ram) {
- st = rrdset_find("system.ram");
- if(!st) {
- st = rrdset_create("system", "ram", NULL, "ram", NULL, "System RAM", "MB", 200, update_every, RRDSET_TYPE_STACKED);
-
- rrddim_add(st, "buffers", NULL, 1, 1024, RRDDIM_ABSOLUTE);
- rrddim_add(st, "used", NULL, 1, 1024, RRDDIM_ABSOLUTE);
- rrddim_add(st, "cached", NULL, 1, 1024, RRDDIM_ABSOLUTE);
- rrddim_add(st, "free", NULL, 1, 1024, RRDDIM_ABSOLUTE);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "used", MemUsed);
- rrddim_set(st, "free", MemFree);
- rrddim_set(st, "cached", Cached);
- rrddim_set(st, "buffers", Buffers);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- unsigned long long SwapUsed = SwapTotal - SwapFree;
-
- if(do_swap) {
- st = rrdset_find("system.swap");
- if(!st) {
- st = rrdset_create("system", "swap", NULL, "swap", NULL, "System Swap", "MB", 201, update_every, RRDSET_TYPE_STACKED);
- st->isdetail = 1;
-
- rrddim_add(st, "free", NULL, 1, 1024, RRDDIM_ABSOLUTE);
- rrddim_add(st, "used", NULL, 1, 1024, RRDDIM_ABSOLUTE);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "used", SwapUsed);
- rrddim_set(st, "free", SwapFree);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(hwcorrupted && do_hwcorrupt && HardwareCorrupted > 0) {
- do_hwcorrupt = CONFIG_ONDEMAND_YES;
-
- st = rrdset_find("mem.hwcorrupt");
- if(!st) {
- st = rrdset_create("mem", "hwcorrupt", NULL, "errors", NULL, "Hardware Corrupted ECC", "MB", 9000, update_every, RRDSET_TYPE_LINE);
- st->isdetail = 1;
-
- rrddim_add(st, "HardwareCorrupted", NULL, 1, 1024, RRDDIM_ABSOLUTE);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "HardwareCorrupted", HardwareCorrupted);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(do_committed) {
- st = rrdset_find("mem.committed");
- if(!st) {
- st = rrdset_create("mem", "committed", NULL, "system", NULL, "Committed (Allocated) Memory", "MB", 5000, update_every, RRDSET_TYPE_AREA);
- st->isdetail = 1;
-
- rrddim_add(st, "Committed_AS", NULL, 1, 1024, RRDDIM_ABSOLUTE);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "Committed_AS", Committed_AS);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(do_writeback) {
- st = rrdset_find("mem.writeback");
- if(!st) {
- st = rrdset_create("mem", "writeback", NULL, "kernel", NULL, "Writeback Memory", "MB", 4000, update_every, RRDSET_TYPE_LINE);
- st->isdetail = 1;
-
- rrddim_add(st, "Dirty", NULL, 1, 1024, RRDDIM_ABSOLUTE);
- rrddim_add(st, "Writeback", NULL, 1, 1024, RRDDIM_ABSOLUTE);
- rrddim_add(st, "FuseWriteback", NULL, 1, 1024, RRDDIM_ABSOLUTE);
- rrddim_add(st, "NfsWriteback", NULL, 1, 1024, RRDDIM_ABSOLUTE);
- rrddim_add(st, "Bounce", NULL, 1, 1024, RRDDIM_ABSOLUTE);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "Dirty", Dirty);
- rrddim_set(st, "Writeback", Writeback);
- rrddim_set(st, "FuseWriteback", WritebackTmp);
- rrddim_set(st, "NfsWriteback", NFS_Unstable);
- rrddim_set(st, "Bounce", Bounce);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(do_kernel) {
- st = rrdset_find("mem.kernel");
- if(!st) {
- st = rrdset_create("mem", "kernel", NULL, "kernel", NULL, "Memory Used by Kernel", "MB", 6000, update_every, RRDSET_TYPE_STACKED);
- st->isdetail = 1;
-
- rrddim_add(st, "Slab", NULL, 1, 1024, RRDDIM_ABSOLUTE);
- rrddim_add(st, "KernelStack", NULL, 1, 1024, RRDDIM_ABSOLUTE);
- rrddim_add(st, "PageTables", NULL, 1, 1024, RRDDIM_ABSOLUTE);
- rrddim_add(st, "VmallocUsed", NULL, 1, 1024, RRDDIM_ABSOLUTE);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "KernelStack", KernelStack);
- rrddim_set(st, "Slab", Slab);
- rrddim_set(st, "PageTables", PageTables);
- rrddim_set(st, "VmallocUsed", VmallocUsed);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(do_slab) {
- st = rrdset_find("mem.slab");
- if(!st) {
- st = rrdset_create("mem", "slab", NULL, "slab", NULL, "Reclaimable Kernel Memory", "MB", 6500, update_every, RRDSET_TYPE_STACKED);
- st->isdetail = 1;
-
- rrddim_add(st, "reclaimable", NULL, 1, 1024, RRDDIM_ABSOLUTE);
- rrddim_add(st, "unreclaimable", NULL, 1, 1024, RRDDIM_ABSOLUTE);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "reclaimable", SReclaimable);
- rrddim_set(st, "unreclaimable", SUnreclaim);
- rrdset_done(st);
- }
-
- return 0;
+ static procfile *ff = NULL;
+
+ static int do_ram = -1, do_swap = -1, do_hwcorrupt = -1, do_committed = -1, do_writeback = -1, do_kernel = -1, do_slab = -1;
+
+ if(do_ram == -1) do_ram = config_get_boolean("plugin:proc:/proc/meminfo", "system ram", 1);
+ if(do_swap == -1) do_swap = config_get_boolean("plugin:proc:/proc/meminfo", "system swap", 1);
+ if(do_hwcorrupt == -1) do_hwcorrupt = config_get_boolean_ondemand("plugin:proc:/proc/meminfo", "hardware corrupted ECC", CONFIG_ONDEMAND_ONDEMAND);
+ if(do_committed == -1) do_committed = config_get_boolean("plugin:proc:/proc/meminfo", "committed memory", 1);
+ if(do_writeback == -1) do_writeback = config_get_boolean("plugin:proc:/proc/meminfo", "writeback memory", 1);
+ if(do_kernel == -1) do_kernel = config_get_boolean("plugin:proc:/proc/meminfo", "kernel memory", 1);
+ if(do_slab == -1) do_slab = config_get_boolean("plugin:proc:/proc/meminfo", "slab memory", 1);
+
+ if(dt) {};
+
+ if(!ff) {
+ char filename[FILENAME_MAX + 1];
+ snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/meminfo");
+ ff = procfile_open(config_get("plugin:proc:/proc/meminfo", "filename to monitor", filename), " \t:", PROCFILE_FLAG_DEFAULT);
+ }
+ if(!ff) return 1;
+
+ ff = procfile_readall(ff);
+ if(!ff) return 0; // we return 0, so that we will retry to open it next time
+
+ uint32_t lines = procfile_lines(ff), l;
+ uint32_t words;
+
+ int hwcorrupted = 0;
+
+ unsigned long long MemTotal = 0, MemFree = 0, Buffers = 0, Cached = 0, SwapCached = 0,
+ Active = 0, Inactive = 0, ActiveAnon = 0, InactiveAnon = 0, ActiveFile = 0, InactiveFile = 0,
+ Unevictable = 0, Mlocked = 0, SwapTotal = 0, SwapFree = 0, Dirty = 0, Writeback = 0, AnonPages = 0,
+ Mapped = 0, Shmem = 0, Slab = 0, SReclaimable = 0, SUnreclaim = 0, KernelStack = 0, PageTables = 0,
+ NFS_Unstable = 0, Bounce = 0, WritebackTmp = 0, CommitLimit = 0, Committed_AS = 0,
+ VmallocTotal = 0, VmallocUsed = 0, VmallocChunk = 0,
+ AnonHugePages = 0, HugePages_Total = 0, HugePages_Free = 0, HugePages_Rsvd = 0, HugePages_Surp = 0, Hugepagesize = 0,
+ DirectMap4k = 0, DirectMap2M = 0, HardwareCorrupted = 0;
+
+ for(l = 0; l < lines ;l++) {
+ words = procfile_linewords(ff, l);
+ if(words < 2) continue;
+
+ char *name = procfile_lineword(ff, l, 0);
+ unsigned long long value = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
+
+ if(!MemTotal && strcmp(name, "MemTotal") == 0) MemTotal = value;
+ else if(!MemFree && strcmp(name, "MemFree") == 0) MemFree = value;
+ else if(!Buffers && strcmp(name, "Buffers") == 0) Buffers = value;
+ else if(!Cached && strcmp(name, "Cached") == 0) Cached = value;
+ else if(!SwapCached && strcmp(name, "SwapCached") == 0) SwapCached = value;
+ else if(!Active && strcmp(name, "Active") == 0) Active = value;
+ else if(!Inactive && strcmp(name, "Inactive") == 0) Inactive = value;
+ else if(!ActiveAnon && strcmp(name, "ActiveAnon") == 0) ActiveAnon = value;
+ else if(!InactiveAnon && strcmp(name, "InactiveAnon") == 0) InactiveAnon = value;
+ else if(!ActiveFile && strcmp(name, "ActiveFile") == 0) ActiveFile = value;
+ else if(!InactiveFile && strcmp(name, "InactiveFile") == 0) InactiveFile = value;
+ else if(!Unevictable && strcmp(name, "Unevictable") == 0) Unevictable = value;
+ else if(!Mlocked && strcmp(name, "Mlocked") == 0) Mlocked = value;
+ else if(!SwapTotal && strcmp(name, "SwapTotal") == 0) SwapTotal = value;
+ else if(!SwapFree && strcmp(name, "SwapFree") == 0) SwapFree = value;
+ else if(!Dirty && strcmp(name, "Dirty") == 0) Dirty = value;
+ else if(!Writeback && strcmp(name, "Writeback") == 0) Writeback = value;
+ else if(!AnonPages && strcmp(name, "AnonPages") == 0) AnonPages = value;
+ else if(!Mapped && strcmp(name, "Mapped") == 0) Mapped = value;
+ else if(!Shmem && strcmp(name, "Shmem") == 0) Shmem = value;
+ else if(!Slab && strcmp(name, "Slab") == 0) Slab = value;
+ else if(!SReclaimable && strcmp(name, "SReclaimable") == 0) SReclaimable = value;
+ else if(!SUnreclaim && strcmp(name, "SUnreclaim") == 0) SUnreclaim = value;
+ else if(!KernelStack && strcmp(name, "KernelStack") == 0) KernelStack = value;
+ else if(!PageTables && strcmp(name, "PageTables") == 0) PageTables = value;
+ else if(!NFS_Unstable && strcmp(name, "NFS_Unstable") == 0) NFS_Unstable = value;
+ else if(!Bounce && strcmp(name, "Bounce") == 0) Bounce = value;
+ else if(!WritebackTmp && strcmp(name, "WritebackTmp") == 0) WritebackTmp = value;
+ else if(!CommitLimit && strcmp(name, "CommitLimit") == 0) CommitLimit = value;
+ else if(!Committed_AS && strcmp(name, "Committed_AS") == 0) Committed_AS = value;
+ else if(!VmallocTotal && strcmp(name, "VmallocTotal") == 0) VmallocTotal = value;
+ else if(!VmallocUsed && strcmp(name, "VmallocUsed") == 0) VmallocUsed = value;
+ else if(!VmallocChunk && strcmp(name, "VmallocChunk") == 0) VmallocChunk = value;
+ else if(!HardwareCorrupted && strcmp(name, "HardwareCorrupted") == 0) { HardwareCorrupted = value; hwcorrupted = 1; }
+ else if(!AnonHugePages && strcmp(name, "AnonHugePages") == 0) AnonHugePages = value;
+ else if(!HugePages_Total && strcmp(name, "HugePages_Total") == 0) HugePages_Total = value;
+ else if(!HugePages_Free && strcmp(name, "HugePages_Free") == 0) HugePages_Free = value;
+ else if(!HugePages_Rsvd && strcmp(name, "HugePages_Rsvd") == 0) HugePages_Rsvd = value;
+ else if(!HugePages_Surp && strcmp(name, "HugePages_Surp") == 0) HugePages_Surp = value;
+ else if(!Hugepagesize && strcmp(name, "Hugepagesize") == 0) Hugepagesize = value;
+ else if(!DirectMap4k && strcmp(name, "DirectMap4k") == 0) DirectMap4k = value;
+ else if(!DirectMap2M && strcmp(name, "DirectMap2M") == 0) DirectMap2M = value;
+ }
+
+ RRDSET *st;
+
+ // --------------------------------------------------------------------
+
+ // http://stackoverflow.com/questions/3019748/how-to-reliably-measure-available-memory-in-linux
+ unsigned long long MemUsed = MemTotal - MemFree - Cached - Buffers;
+
+ if(do_ram) {
+ st = rrdset_find("system.ram");
+ if(!st) {
+ st = rrdset_create("system", "ram", NULL, "ram", NULL, "System RAM", "MB", 200, update_every, RRDSET_TYPE_STACKED);
+
+ rrddim_add(st, "buffers", NULL, 1, 1024, RRDDIM_ABSOLUTE);
+ rrddim_add(st, "used", NULL, 1, 1024, RRDDIM_ABSOLUTE);
+ rrddim_add(st, "cached", NULL, 1, 1024, RRDDIM_ABSOLUTE);
+ rrddim_add(st, "free", NULL, 1, 1024, RRDDIM_ABSOLUTE);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "used", MemUsed);
+ rrddim_set(st, "free", MemFree);
+ rrddim_set(st, "cached", Cached);
+ rrddim_set(st, "buffers", Buffers);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ unsigned long long SwapUsed = SwapTotal - SwapFree;
+
+ if(do_swap) {
+ st = rrdset_find("system.swap");
+ if(!st) {
+ st = rrdset_create("system", "swap", NULL, "swap", NULL, "System Swap", "MB", 201, update_every, RRDSET_TYPE_STACKED);
+ st->isdetail = 1;
+
+ rrddim_add(st, "free", NULL, 1, 1024, RRDDIM_ABSOLUTE);
+ rrddim_add(st, "used", NULL, 1, 1024, RRDDIM_ABSOLUTE);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "used", SwapUsed);
+ rrddim_set(st, "free", SwapFree);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(hwcorrupted && do_hwcorrupt && HardwareCorrupted > 0) {
+ do_hwcorrupt = CONFIG_ONDEMAND_YES;
+
+ st = rrdset_find("mem.hwcorrupt");
+ if(!st) {
+ st = rrdset_create("mem", "hwcorrupt", NULL, "errors", NULL, "Hardware Corrupted ECC", "MB", 9000, update_every, RRDSET_TYPE_LINE);
+ st->isdetail = 1;
+
+ rrddim_add(st, "HardwareCorrupted", NULL, 1, 1024, RRDDIM_ABSOLUTE);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "HardwareCorrupted", HardwareCorrupted);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_committed) {
+ st = rrdset_find("mem.committed");
+ if(!st) {
+ st = rrdset_create("mem", "committed", NULL, "system", NULL, "Committed (Allocated) Memory", "MB", 5000, update_every, RRDSET_TYPE_AREA);
+ st->isdetail = 1;
+
+ rrddim_add(st, "Committed_AS", NULL, 1, 1024, RRDDIM_ABSOLUTE);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "Committed_AS", Committed_AS);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_writeback) {
+ st = rrdset_find("mem.writeback");
+ if(!st) {
+ st = rrdset_create("mem", "writeback", NULL, "kernel", NULL, "Writeback Memory", "MB", 4000, update_every, RRDSET_TYPE_LINE);
+ st->isdetail = 1;
+
+ rrddim_add(st, "Dirty", NULL, 1, 1024, RRDDIM_ABSOLUTE);
+ rrddim_add(st, "Writeback", NULL, 1, 1024, RRDDIM_ABSOLUTE);
+ rrddim_add(st, "FuseWriteback", NULL, 1, 1024, RRDDIM_ABSOLUTE);
+ rrddim_add(st, "NfsWriteback", NULL, 1, 1024, RRDDIM_ABSOLUTE);
+ rrddim_add(st, "Bounce", NULL, 1, 1024, RRDDIM_ABSOLUTE);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "Dirty", Dirty);
+ rrddim_set(st, "Writeback", Writeback);
+ rrddim_set(st, "FuseWriteback", WritebackTmp);
+ rrddim_set(st, "NfsWriteback", NFS_Unstable);
+ rrddim_set(st, "Bounce", Bounce);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_kernel) {
+ st = rrdset_find("mem.kernel");
+ if(!st) {
+ st = rrdset_create("mem", "kernel", NULL, "kernel", NULL, "Memory Used by Kernel", "MB", 6000, update_every, RRDSET_TYPE_STACKED);
+ st->isdetail = 1;
+
+ rrddim_add(st, "Slab", NULL, 1, 1024, RRDDIM_ABSOLUTE);
+ rrddim_add(st, "KernelStack", NULL, 1, 1024, RRDDIM_ABSOLUTE);
+ rrddim_add(st, "PageTables", NULL, 1, 1024, RRDDIM_ABSOLUTE);
+ rrddim_add(st, "VmallocUsed", NULL, 1, 1024, RRDDIM_ABSOLUTE);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "KernelStack", KernelStack);
+ rrddim_set(st, "Slab", Slab);
+ rrddim_set(st, "PageTables", PageTables);
+ rrddim_set(st, "VmallocUsed", VmallocUsed);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_slab) {
+ st = rrdset_find("mem.slab");
+ if(!st) {
+ st = rrdset_create("mem", "slab", NULL, "slab", NULL, "Reclaimable Kernel Memory", "MB", 6500, update_every, RRDSET_TYPE_STACKED);
+ st->isdetail = 1;
+
+ rrddim_add(st, "reclaimable", NULL, 1, 1024, RRDDIM_ABSOLUTE);
+ rrddim_add(st, "unreclaimable", NULL, 1, 1024, RRDDIM_ABSOLUTE);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "reclaimable", SReclaimable);
+ rrddim_set(st, "unreclaimable", SUnreclaim);
+ rrdset_done(st);
+ }
+
+ return 0;
}
diff --git a/src/proc_net_dev.c b/src/proc_net_dev.c
index 12d8078c7..53981182a 100644
--- a/src/proc_net_dev.c
+++ b/src/proc_net_dev.c
@@ -1,247 +1,236 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
#include "common.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "rrd.h"
-#include "plugin_proc.h"
int do_proc_net_dev(int update_every, unsigned long long dt) {
- static procfile *ff = NULL;
- static int enable_new_interfaces = -1, enable_ifb_interfaces = -1;
- static int do_bandwidth = -1, do_packets = -1, do_errors = -1, do_drops = -1, do_fifo = -1, do_compressed = -1, do_events = -1;
-
- if(dt) {};
-
- if(!ff) {
- char filename[FILENAME_MAX + 1];
- snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/net/dev");
- ff = procfile_open(config_get("plugin:proc:/proc/net/dev", "filename to monitor", filename), " \t,:|", PROCFILE_FLAG_DEFAULT);
- }
- if(!ff) return 1;
-
- ff = procfile_readall(ff);
- if(!ff) return 0; // we return 0, so that we will retry to open it next time
-
- if(enable_new_interfaces == -1) enable_new_interfaces = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "enable new interfaces detected at runtime", CONFIG_ONDEMAND_ONDEMAND);
- if(enable_ifb_interfaces == -1) enable_ifb_interfaces = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "enable ifb interfaces", CONFIG_ONDEMAND_NO);
-
- if(do_bandwidth == -1) do_bandwidth = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "bandwidth for all interfaces", CONFIG_ONDEMAND_ONDEMAND);
- if(do_packets == -1) do_packets = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "packets for all interfaces", CONFIG_ONDEMAND_ONDEMAND);
- if(do_errors == -1) do_errors = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "errors for all interfaces", CONFIG_ONDEMAND_ONDEMAND);
- if(do_drops == -1) do_drops = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "drops for all interfaces", CONFIG_ONDEMAND_ONDEMAND);
- if(do_fifo == -1) do_fifo = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "fifo for all interfaces", CONFIG_ONDEMAND_ONDEMAND);
- if(do_compressed == -1) do_compressed = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "compressed packets for all interfaces", CONFIG_ONDEMAND_ONDEMAND);
- if(do_events == -1) do_events = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "frames, collisions, carrier counters for all interfaces", CONFIG_ONDEMAND_ONDEMAND);
-
- uint32_t lines = procfile_lines(ff), l;
- uint32_t words;
-
- char *iface;
- unsigned long long rbytes, rpackets, rerrors, rdrops, rfifo, rframe, rcompressed, rmulticast;
- unsigned long long tbytes, tpackets, terrors, tdrops, tfifo, tcollisions, tcarrier, tcompressed;
-
- for(l = 2; l < lines ;l++) {
- words = procfile_linewords(ff, l);
- if(words < 17) continue;
-
- iface = procfile_lineword(ff, l, 0);
-
- rbytes = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
- rpackets = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
- rerrors = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
- rdrops = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
- rfifo = strtoull(procfile_lineword(ff, l, 5), NULL, 10);
- rframe = strtoull(procfile_lineword(ff, l, 6), NULL, 10);
- rcompressed = strtoull(procfile_lineword(ff, l, 7), NULL, 10);
- rmulticast = strtoull(procfile_lineword(ff, l, 8), NULL, 10);
-
- tbytes = strtoull(procfile_lineword(ff, l, 9), NULL, 10);
- tpackets = strtoull(procfile_lineword(ff, l, 10), NULL, 10);
- terrors = strtoull(procfile_lineword(ff, l, 11), NULL, 10);
- tdrops = strtoull(procfile_lineword(ff, l, 12), NULL, 10);
- tfifo = strtoull(procfile_lineword(ff, l, 13), NULL, 10);
- tcollisions = strtoull(procfile_lineword(ff, l, 14), NULL, 10);
- tcarrier = strtoull(procfile_lineword(ff, l, 15), NULL, 10);
- tcompressed = strtoull(procfile_lineword(ff, l, 16), NULL, 10);
-
- int ddo_bandwidth = do_bandwidth, ddo_packets = do_packets, ddo_errors = do_errors, ddo_drops = do_drops, ddo_fifo = do_fifo, ddo_compressed = do_compressed, ddo_events = do_events;
-
- int default_enable = enable_new_interfaces;
-
- // prevent unused interfaces from creating charts
- if(strcmp(iface, "lo") == 0)
- default_enable = 0;
- else {
- int len = strlen(iface);
- if(len >= 4 && strcmp(&iface[len-4], "-ifb") == 0)
- default_enable = enable_ifb_interfaces;
- }
-
- // check if the user wants it
- {
- char var_name[512 + 1];
- snprintfz(var_name, 512, "plugin:proc:/proc/net/dev:%s", iface);
- default_enable = config_get_boolean_ondemand(var_name, "enabled", default_enable);
- if(default_enable == CONFIG_ONDEMAND_NO) continue;
- if(default_enable == CONFIG_ONDEMAND_ONDEMAND && !rbytes && !tbytes) continue;
-
- ddo_bandwidth = config_get_boolean_ondemand(var_name, "bandwidth", ddo_bandwidth);
- ddo_packets = config_get_boolean_ondemand(var_name, "packets", ddo_packets);
- ddo_errors = config_get_boolean_ondemand(var_name, "errors", ddo_errors);
- ddo_drops = config_get_boolean_ondemand(var_name, "drops", ddo_drops);
- ddo_fifo = config_get_boolean_ondemand(var_name, "fifo", ddo_fifo);
- ddo_compressed = config_get_boolean_ondemand(var_name, "compressed", ddo_compressed);
- ddo_events = config_get_boolean_ondemand(var_name, "events", ddo_events);
-
- if(ddo_bandwidth == CONFIG_ONDEMAND_ONDEMAND && rbytes == 0 && tbytes == 0) ddo_bandwidth = 0;
- if(ddo_errors == CONFIG_ONDEMAND_ONDEMAND && rerrors == 0 && terrors == 0) ddo_errors = 0;
- if(ddo_drops == CONFIG_ONDEMAND_ONDEMAND && rdrops == 0 && tdrops == 0) ddo_drops = 0;
- if(ddo_fifo == CONFIG_ONDEMAND_ONDEMAND && rfifo == 0 && tfifo == 0) ddo_fifo = 0;
- if(ddo_compressed == CONFIG_ONDEMAND_ONDEMAND && rcompressed == 0 && tcompressed == 0) ddo_compressed = 0;
- if(ddo_events == CONFIG_ONDEMAND_ONDEMAND && rframe == 0 && tcollisions == 0 && tcarrier == 0) ddo_events = 0;
-
- // for absolute values, we need to switch the setting to 'yes'
- // to allow it refresh from now on
- // if(ddo_fifo == CONFIG_ONDEMAND_ONDEMAND) config_set(var_name, "fifo", "yes");
- }
-
- RRDSET *st;
-
- // --------------------------------------------------------------------
-
- if(ddo_bandwidth) {
- st = rrdset_find_bytype("net", iface);
- if(!st) {
- st = rrdset_create("net", iface, NULL, iface, "net.net", "Bandwidth", "kilobits/s", 7000, update_every, RRDSET_TYPE_AREA);
-
- rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL);
- rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "received", rbytes);
- rrddim_set(st, "sent", tbytes);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(ddo_packets) {
- st = rrdset_find_bytype("net_packets", iface);
- if(!st) {
- st = rrdset_create("net_packets", iface, NULL, iface, "net.packets", "Packets", "packets/s", 7001, update_every, RRDSET_TYPE_LINE);
- st->isdetail = 1;
-
- rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "multicast", NULL, 1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "received", rpackets);
- rrddim_set(st, "sent", tpackets);
- rrddim_set(st, "multicast", rmulticast);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(ddo_errors) {
- st = rrdset_find_bytype("net_errors", iface);
- if(!st) {
- st = rrdset_create("net_errors", iface, NULL, iface, "net.errors", "Interface Errors", "errors/s", 7002, update_every, RRDSET_TYPE_LINE);
- st->isdetail = 1;
-
- rrddim_add(st, "inbound", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "outbound", NULL, -1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "inbound", rerrors);
- rrddim_set(st, "outbound", terrors);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(ddo_drops) {
- st = rrdset_find_bytype("net_drops", iface);
- if(!st) {
- st = rrdset_create("net_drops", iface, NULL, iface, "net.drops", "Interface Drops", "drops/s", 7003, update_every, RRDSET_TYPE_LINE);
- st->isdetail = 1;
-
- rrddim_add(st, "inbound", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "outbound", NULL, -1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "inbound", rdrops);
- rrddim_set(st, "outbound", tdrops);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(ddo_fifo) {
- st = rrdset_find_bytype("net_fifo", iface);
- if(!st) {
- st = rrdset_create("net_fifo", iface, NULL, iface, "net.fifo", "Interface FIFO Buffer Errors", "errors", 7004, update_every, RRDSET_TYPE_LINE);
- st->isdetail = 1;
-
- rrddim_add(st, "receive", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "transmit", NULL, -1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "receive", rfifo);
- rrddim_set(st, "transmit", tfifo);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(ddo_compressed) {
- st = rrdset_find_bytype("net_compressed", iface);
- if(!st) {
- st = rrdset_create("net_compressed", iface, NULL, iface, "net.compressed", "Compressed Packets", "packets/s", 7005, update_every, RRDSET_TYPE_LINE);
- st->isdetail = 1;
-
- rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "received", rcompressed);
- rrddim_set(st, "sent", tcompressed);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(ddo_events) {
- st = rrdset_find_bytype("net_events", iface);
- if(!st) {
- st = rrdset_create("net_events", iface, NULL, iface, "net.events", "Network Interface Events", "events/s", 7006, update_every, RRDSET_TYPE_LINE);
- st->isdetail = 1;
-
- rrddim_add(st, "frames", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "collisions", NULL, -1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "carrier", NULL, -1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "frames", rframe);
- rrddim_set(st, "collisions", tcollisions);
- rrddim_set(st, "carrier", tcarrier);
- rrdset_done(st);
- }
- }
-
- return 0;
+ static procfile *ff = NULL;
+ static int enable_new_interfaces = -1, enable_ifb_interfaces = -1;
+ static int do_bandwidth = -1, do_packets = -1, do_errors = -1, do_drops = -1, do_fifo = -1, do_compressed = -1, do_events = -1;
+
+ if(dt) {};
+
+ if(!ff) {
+ char filename[FILENAME_MAX + 1];
+ snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/net/dev");
+ ff = procfile_open(config_get("plugin:proc:/proc/net/dev", "filename to monitor", filename), " \t,:|", PROCFILE_FLAG_DEFAULT);
+ }
+ if(!ff) return 1;
+
+ ff = procfile_readall(ff);
+ if(!ff) return 0; // we return 0, so that we will retry to open it next time
+
+ if(enable_new_interfaces == -1) enable_new_interfaces = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "enable new interfaces detected at runtime", CONFIG_ONDEMAND_ONDEMAND);
+ if(enable_ifb_interfaces == -1) enable_ifb_interfaces = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "enable ifb interfaces", CONFIG_ONDEMAND_NO);
+
+ if(do_bandwidth == -1) do_bandwidth = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "bandwidth for all interfaces", CONFIG_ONDEMAND_ONDEMAND);
+ if(do_packets == -1) do_packets = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "packets for all interfaces", CONFIG_ONDEMAND_ONDEMAND);
+ if(do_errors == -1) do_errors = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "errors for all interfaces", CONFIG_ONDEMAND_ONDEMAND);
+ if(do_drops == -1) do_drops = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "drops for all interfaces", CONFIG_ONDEMAND_ONDEMAND);
+ if(do_fifo == -1) do_fifo = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "fifo for all interfaces", CONFIG_ONDEMAND_ONDEMAND);
+ if(do_compressed == -1) do_compressed = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "compressed packets for all interfaces", CONFIG_ONDEMAND_ONDEMAND);
+ if(do_events == -1) do_events = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "frames, collisions, carrier counters for all interfaces", CONFIG_ONDEMAND_ONDEMAND);
+
+ uint32_t lines = procfile_lines(ff), l;
+ uint32_t words;
+
+ char *iface;
+ unsigned long long rbytes, rpackets, rerrors, rdrops, rfifo, rframe, rcompressed, rmulticast;
+ unsigned long long tbytes, tpackets, terrors, tdrops, tfifo, tcollisions, tcarrier, tcompressed;
+
+ for(l = 2; l < lines ;l++) {
+ words = procfile_linewords(ff, l);
+ if(words < 17) continue;
+
+ iface = procfile_lineword(ff, l, 0);
+
+ rbytes = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
+ rpackets = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
+ rerrors = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
+ rdrops = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
+ rfifo = strtoull(procfile_lineword(ff, l, 5), NULL, 10);
+ rframe = strtoull(procfile_lineword(ff, l, 6), NULL, 10);
+ rcompressed = strtoull(procfile_lineword(ff, l, 7), NULL, 10);
+ rmulticast = strtoull(procfile_lineword(ff, l, 8), NULL, 10);
+
+ tbytes = strtoull(procfile_lineword(ff, l, 9), NULL, 10);
+ tpackets = strtoull(procfile_lineword(ff, l, 10), NULL, 10);
+ terrors = strtoull(procfile_lineword(ff, l, 11), NULL, 10);
+ tdrops = strtoull(procfile_lineword(ff, l, 12), NULL, 10);
+ tfifo = strtoull(procfile_lineword(ff, l, 13), NULL, 10);
+ tcollisions = strtoull(procfile_lineword(ff, l, 14), NULL, 10);
+ tcarrier = strtoull(procfile_lineword(ff, l, 15), NULL, 10);
+ tcompressed = strtoull(procfile_lineword(ff, l, 16), NULL, 10);
+
+ int ddo_bandwidth = do_bandwidth, ddo_packets = do_packets, ddo_errors = do_errors, ddo_drops = do_drops, ddo_fifo = do_fifo, ddo_compressed = do_compressed, ddo_events = do_events;
+
+ int default_enable = enable_new_interfaces;
+
+ // prevent unused interfaces from creating charts
+ if(strcmp(iface, "lo") == 0)
+ default_enable = 0;
+ else {
+ int len = strlen(iface);
+ if(len >= 4 && strcmp(&iface[len-4], "-ifb") == 0)
+ default_enable = enable_ifb_interfaces;
+ }
+
+ // check if the user wants it
+ {
+ char var_name[512 + 1];
+ snprintfz(var_name, 512, "plugin:proc:/proc/net/dev:%s", iface);
+ default_enable = config_get_boolean_ondemand(var_name, "enabled", default_enable);
+ if(default_enable == CONFIG_ONDEMAND_NO) continue;
+ if(default_enable == CONFIG_ONDEMAND_ONDEMAND && !rbytes && !tbytes) continue;
+
+ ddo_bandwidth = config_get_boolean_ondemand(var_name, "bandwidth", ddo_bandwidth);
+ ddo_packets = config_get_boolean_ondemand(var_name, "packets", ddo_packets);
+ ddo_errors = config_get_boolean_ondemand(var_name, "errors", ddo_errors);
+ ddo_drops = config_get_boolean_ondemand(var_name, "drops", ddo_drops);
+ ddo_fifo = config_get_boolean_ondemand(var_name, "fifo", ddo_fifo);
+ ddo_compressed = config_get_boolean_ondemand(var_name, "compressed", ddo_compressed);
+ ddo_events = config_get_boolean_ondemand(var_name, "events", ddo_events);
+
+ if(ddo_bandwidth == CONFIG_ONDEMAND_ONDEMAND && rbytes == 0 && tbytes == 0) ddo_bandwidth = 0;
+ if(ddo_errors == CONFIG_ONDEMAND_ONDEMAND && rerrors == 0 && terrors == 0) ddo_errors = 0;
+ if(ddo_drops == CONFIG_ONDEMAND_ONDEMAND && rdrops == 0 && tdrops == 0) ddo_drops = 0;
+ if(ddo_fifo == CONFIG_ONDEMAND_ONDEMAND && rfifo == 0 && tfifo == 0) ddo_fifo = 0;
+ if(ddo_compressed == CONFIG_ONDEMAND_ONDEMAND && rcompressed == 0 && tcompressed == 0) ddo_compressed = 0;
+ if(ddo_events == CONFIG_ONDEMAND_ONDEMAND && rframe == 0 && tcollisions == 0 && tcarrier == 0) ddo_events = 0;
+
+ // for absolute values, we need to switch the setting to 'yes'
+ // to allow it refresh from now on
+ // if(ddo_fifo == CONFIG_ONDEMAND_ONDEMAND) config_set(var_name, "fifo", "yes");
+ }
+
+ RRDSET *st;
+
+ // --------------------------------------------------------------------
+
+ if(ddo_bandwidth) {
+ st = rrdset_find_bytype("net", iface);
+ if(!st) {
+ st = rrdset_create("net", iface, NULL, iface, "net.net", "Bandwidth", "kilobits/s", 7000, update_every, RRDSET_TYPE_AREA);
+
+ rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "received", rbytes);
+ rrddim_set(st, "sent", tbytes);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(ddo_packets) {
+ st = rrdset_find_bytype("net_packets", iface);
+ if(!st) {
+ st = rrdset_create("net_packets", iface, NULL, iface, "net.packets", "Packets", "packets/s", 7001, update_every, RRDSET_TYPE_LINE);
+ st->isdetail = 1;
+
+ rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "multicast", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "received", rpackets);
+ rrddim_set(st, "sent", tpackets);
+ rrddim_set(st, "multicast", rmulticast);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(ddo_errors) {
+ st = rrdset_find_bytype("net_errors", iface);
+ if(!st) {
+ st = rrdset_create("net_errors", iface, NULL, iface, "net.errors", "Interface Errors", "errors/s", 7002, update_every, RRDSET_TYPE_LINE);
+ st->isdetail = 1;
+
+ rrddim_add(st, "inbound", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "outbound", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "inbound", rerrors);
+ rrddim_set(st, "outbound", terrors);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(ddo_drops) {
+ st = rrdset_find_bytype("net_drops", iface);
+ if(!st) {
+ st = rrdset_create("net_drops", iface, NULL, iface, "net.drops", "Interface Drops", "drops/s", 7003, update_every, RRDSET_TYPE_LINE);
+ st->isdetail = 1;
+
+ rrddim_add(st, "inbound", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "outbound", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "inbound", rdrops);
+ rrddim_set(st, "outbound", tdrops);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(ddo_fifo) {
+ st = rrdset_find_bytype("net_fifo", iface);
+ if(!st) {
+ st = rrdset_create("net_fifo", iface, NULL, iface, "net.fifo", "Interface FIFO Buffer Errors", "errors", 7004, update_every, RRDSET_TYPE_LINE);
+ st->isdetail = 1;
+
+ rrddim_add(st, "receive", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "transmit", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "receive", rfifo);
+ rrddim_set(st, "transmit", tfifo);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(ddo_compressed) {
+ st = rrdset_find_bytype("net_compressed", iface);
+ if(!st) {
+ st = rrdset_create("net_compressed", iface, NULL, iface, "net.compressed", "Compressed Packets", "packets/s", 7005, update_every, RRDSET_TYPE_LINE);
+ st->isdetail = 1;
+
+ rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "received", rcompressed);
+ rrddim_set(st, "sent", tcompressed);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(ddo_events) {
+ st = rrdset_find_bytype("net_events", iface);
+ if(!st) {
+ st = rrdset_create("net_events", iface, NULL, iface, "net.events", "Network Interface Events", "events/s", 7006, update_every, RRDSET_TYPE_LINE);
+ st->isdetail = 1;
+
+ rrddim_add(st, "frames", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "collisions", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "carrier", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "frames", rframe);
+ rrddim_set(st, "collisions", tcollisions);
+ rrddim_set(st, "carrier", tcarrier);
+ rrdset_done(st);
+ }
+ }
+
+ return 0;
}
diff --git a/src/proc_net_ip_vs_stats.c b/src/proc_net_ip_vs_stats.c
index ffb5da7b5..96efd7925 100644
--- a/src/proc_net_ip_vs_stats.c
+++ b/src/proc_net_ip_vs_stats.c
@@ -1,102 +1,92 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-
#include "common.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "rrd.h"
-#include "plugin_proc.h"
-#define RRD_TYPE_NET_IPVS "ipvs"
-#define RRD_TYPE_NET_IPVS_LEN strlen(RRD_TYPE_NET_IPVS)
+#define RRD_TYPE_NET_IPVS "ipvs"
+#define RRD_TYPE_NET_IPVS_LEN strlen(RRD_TYPE_NET_IPVS)
int do_proc_net_ip_vs_stats(int update_every, unsigned long long dt) {
- static int do_bandwidth = -1, do_sockets = -1, do_packets = -1;
- static procfile *ff = NULL;
+ static int do_bandwidth = -1, do_sockets = -1, do_packets = -1;
+ static procfile *ff = NULL;
- if(do_bandwidth == -1) do_bandwidth = config_get_boolean("plugin:proc:/proc/net/ip_vs_stats", "IPVS bandwidth", 1);
- if(do_sockets == -1) do_sockets = config_get_boolean("plugin:proc:/proc/net/ip_vs_stats", "IPVS connections", 1);
- if(do_packets == -1) do_packets = config_get_boolean("plugin:proc:/proc/net/ip_vs_stats", "IPVS packets", 1);
+ if(do_bandwidth == -1) do_bandwidth = config_get_boolean("plugin:proc:/proc/net/ip_vs_stats", "IPVS bandwidth", 1);
+ if(do_sockets == -1) do_sockets = config_get_boolean("plugin:proc:/proc/net/ip_vs_stats", "IPVS connections", 1);
+ if(do_packets == -1) do_packets = config_get_boolean("plugin:proc:/proc/net/ip_vs_stats", "IPVS packets", 1);
- if(dt) {};
+ if(dt) {};
- if(!ff) {
- char filename[FILENAME_MAX + 1];
- snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/net/ip_vs_stats");
- ff = procfile_open(config_get("plugin:proc:/proc/net/ip_vs_stats", "filename to monitor", filename), " \t,:|", PROCFILE_FLAG_DEFAULT);
- }
- if(!ff) return 1;
+ if(!ff) {
+ char filename[FILENAME_MAX + 1];
+ snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/net/ip_vs_stats");
+ ff = procfile_open(config_get("plugin:proc:/proc/net/ip_vs_stats", "filename to monitor", filename), " \t,:|", PROCFILE_FLAG_DEFAULT);
+ }
+ if(!ff) return 1;
- ff = procfile_readall(ff);
- if(!ff) return 0; // we return 0, so that we will retry to open it next time
+ ff = procfile_readall(ff);
+ if(!ff) return 0; // we return 0, so that we will retry to open it next time
- // make sure we have 3 lines
- if(procfile_lines(ff) < 3) return 1;
+ // make sure we have 3 lines
+ if(procfile_lines(ff) < 3) return 1;
- // make sure we have 5 words on the 3rd line
- if(procfile_linewords(ff, 2) < 5) return 1;
+ // make sure we have 5 words on the 3rd line
+ if(procfile_linewords(ff, 2) < 5) return 1;
- unsigned long long entries, InPackets, OutPackets, InBytes, OutBytes;
+ unsigned long long entries, InPackets, OutPackets, InBytes, OutBytes;
- entries = strtoull(procfile_lineword(ff, 2, 0), NULL, 16);
- InPackets = strtoull(procfile_lineword(ff, 2, 1), NULL, 16);
- OutPackets = strtoull(procfile_lineword(ff, 2, 2), NULL, 16);
- InBytes = strtoull(procfile_lineword(ff, 2, 3), NULL, 16);
- OutBytes = strtoull(procfile_lineword(ff, 2, 4), NULL, 16);
+ entries = strtoull(procfile_lineword(ff, 2, 0), NULL, 16);
+ InPackets = strtoull(procfile_lineword(ff, 2, 1), NULL, 16);
+ OutPackets = strtoull(procfile_lineword(ff, 2, 2), NULL, 16);
+ InBytes = strtoull(procfile_lineword(ff, 2, 3), NULL, 16);
+ OutBytes = strtoull(procfile_lineword(ff, 2, 4), NULL, 16);
- RRDSET *st;
+ RRDSET *st;
- // --------------------------------------------------------------------
+ // --------------------------------------------------------------------
- if(do_sockets) {
- st = rrdset_find(RRD_TYPE_NET_IPVS ".sockets");
- if(!st) {
- st = rrdset_create(RRD_TYPE_NET_IPVS, "sockets", NULL, RRD_TYPE_NET_IPVS, NULL, "IPVS New Connections", "connections/s", 1001, update_every, RRDSET_TYPE_LINE);
+ if(do_sockets) {
+ st = rrdset_find(RRD_TYPE_NET_IPVS ".sockets");
+ if(!st) {
+ st = rrdset_create(RRD_TYPE_NET_IPVS, "sockets", NULL, RRD_TYPE_NET_IPVS, NULL, "IPVS New Connections", "connections/s", 1001, update_every, RRDSET_TYPE_LINE);
- rrddim_add(st, "connections", NULL, 1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
+ rrddim_add(st, "connections", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
- rrddim_set(st, "connections", entries);
- rrdset_done(st);
- }
+ rrddim_set(st, "connections", entries);
+ rrdset_done(st);
+ }
- // --------------------------------------------------------------------
+ // --------------------------------------------------------------------
- if(do_packets) {
- st = rrdset_find(RRD_TYPE_NET_IPVS ".packets");
- if(!st) {
- st = rrdset_create(RRD_TYPE_NET_IPVS, "packets", NULL, RRD_TYPE_NET_IPVS, NULL, "IPVS Packets", "packets/s", 1002, update_every, RRDSET_TYPE_LINE);
+ if(do_packets) {
+ st = rrdset_find(RRD_TYPE_NET_IPVS ".packets");
+ if(!st) {
+ st = rrdset_create(RRD_TYPE_NET_IPVS, "packets", NULL, RRD_TYPE_NET_IPVS, NULL, "IPVS Packets", "packets/s", 1002, update_every, RRDSET_TYPE_LINE);
- rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
+ rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
- rrddim_set(st, "received", InPackets);
- rrddim_set(st, "sent", OutPackets);
- rrdset_done(st);
- }
+ rrddim_set(st, "received", InPackets);
+ rrddim_set(st, "sent", OutPackets);
+ rrdset_done(st);
+ }
- // --------------------------------------------------------------------
+ // --------------------------------------------------------------------
- if(do_bandwidth) {
- st = rrdset_find(RRD_TYPE_NET_IPVS ".net");
- if(!st) {
- st = rrdset_create(RRD_TYPE_NET_IPVS, "net", NULL, RRD_TYPE_NET_IPVS, NULL, "IPVS Bandwidth", "kilobits/s", 1000, update_every, RRDSET_TYPE_AREA);
+ if(do_bandwidth) {
+ st = rrdset_find(RRD_TYPE_NET_IPVS ".net");
+ if(!st) {
+ st = rrdset_create(RRD_TYPE_NET_IPVS, "net", NULL, RRD_TYPE_NET_IPVS, NULL, "IPVS Bandwidth", "kilobits/s", 1000, update_every, RRDSET_TYPE_AREA);
- rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL);
- rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
+ rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
- rrddim_set(st, "received", InBytes);
- rrddim_set(st, "sent", OutBytes);
- rrdset_done(st);
- }
+ rrddim_set(st, "received", InBytes);
+ rrddim_set(st, "sent", OutBytes);
+ rrdset_done(st);
+ }
- return 0;
+ return 0;
}
diff --git a/src/proc_net_netstat.c b/src/proc_net_netstat.c
index c8c12c1db..fe1c4d979 100644
--- a/src/proc_net_netstat.c
+++ b/src/proc_net_netstat.c
@@ -1,191 +1,179 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
#include "common.h"
-#include "log.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "rrd.h"
-#include "plugin_proc.h"
int do_proc_net_netstat(int update_every, unsigned long long dt) {
- static int do_bandwidth = -1, do_inerrors = -1, do_mcast = -1, do_bcast = -1, do_mcast_p = -1, do_bcast_p = -1;
- static procfile *ff = NULL;
-
- if(do_bandwidth == -1) do_bandwidth = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "bandwidth", CONFIG_ONDEMAND_ONDEMAND);
- if(do_inerrors == -1) do_inerrors = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "input errors", CONFIG_ONDEMAND_ONDEMAND);
- if(do_mcast == -1) do_mcast = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "multicast bandwidth", CONFIG_ONDEMAND_ONDEMAND);
- if(do_bcast == -1) do_bcast = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "broadcast bandwidth", CONFIG_ONDEMAND_ONDEMAND);
- if(do_mcast_p == -1) do_mcast_p = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "multicast packets", CONFIG_ONDEMAND_ONDEMAND);
- if(do_bcast_p == -1) do_bcast_p = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "broadcast packets", CONFIG_ONDEMAND_ONDEMAND);
-
- if(dt) {};
-
- if(!ff) {
- char filename[FILENAME_MAX + 1];
- snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/net/netstat");
- ff = procfile_open(config_get("plugin:proc:/proc/net/netstat", "filename to monitor", filename), " \t:", PROCFILE_FLAG_DEFAULT);
- }
- if(!ff) return 1;
-
- ff = procfile_readall(ff);
- if(!ff) return 0; // we return 0, so that we will retry to open it next time
-
- uint32_t lines = procfile_lines(ff), l;
- uint32_t words;
-
- for(l = 0; l < lines ;l++) {
- if(strcmp(procfile_lineword(ff, l, 0), "IpExt") == 0) {
- l++; // we need the next line
-
- if(strcmp(procfile_lineword(ff, l, 0), "IpExt") != 0) {
- error("Cannot read IpExt line from /proc/net/netstat.");
- break;
- }
- words = procfile_linewords(ff, l);
- if(words < 12) {
- error("Cannot read /proc/net/netstat IpExt line. Expected 12 params, read %d.", words);
- continue;
- }
-
- unsigned long long
- InNoRoutes = 0, InTruncatedPkts = 0,
- InOctets = 0, InMcastPkts = 0, InBcastPkts = 0, InMcastOctets = 0, InBcastOctets = 0,
- OutOctets = 0, OutMcastPkts = 0, OutBcastPkts = 0, OutMcastOctets = 0, OutBcastOctets = 0;
-
- InNoRoutes = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
- InTruncatedPkts = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
- InMcastPkts = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
- OutMcastPkts = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
- InBcastPkts = strtoull(procfile_lineword(ff, l, 5), NULL, 10);
- OutBcastPkts = strtoull(procfile_lineword(ff, l, 6), NULL, 10);
- InOctets = strtoull(procfile_lineword(ff, l, 7), NULL, 10);
- OutOctets = strtoull(procfile_lineword(ff, l, 8), NULL, 10);
- InMcastOctets = strtoull(procfile_lineword(ff, l, 9), NULL, 10);
- OutMcastOctets = strtoull(procfile_lineword(ff, l, 10), NULL, 10);
- InBcastOctets = strtoull(procfile_lineword(ff, l, 11), NULL, 10);
- OutBcastOctets = strtoull(procfile_lineword(ff, l, 12), NULL, 10);
-
- RRDSET *st;
-
- // --------------------------------------------------------------------
-
- if(do_bandwidth == CONFIG_ONDEMAND_YES || (do_bandwidth == CONFIG_ONDEMAND_ONDEMAND && (InOctets || OutOctets))) {
- do_bandwidth = CONFIG_ONDEMAND_YES;
- st = rrdset_find("system.ipv4");
- if(!st) {
- st = rrdset_create("system", "ipv4", NULL, "network", NULL, "IPv4 Bandwidth", "kilobits/s", 500, update_every, RRDSET_TYPE_AREA);
-
- rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL);
- rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "sent", OutOctets);
- rrddim_set(st, "received", InOctets);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(do_inerrors == CONFIG_ONDEMAND_YES || (do_inerrors == CONFIG_ONDEMAND_ONDEMAND && (InNoRoutes || InTruncatedPkts))) {
- do_inerrors = CONFIG_ONDEMAND_YES;
- st = rrdset_find("ipv4.inerrors");
- if(!st) {
- st = rrdset_create("ipv4", "inerrors", NULL, "errors", NULL, "IPv4 Input Errors", "packets/s", 4000, update_every, RRDSET_TYPE_LINE);
- st->isdetail = 1;
-
- rrddim_add(st, "noroutes", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "truncated", NULL, 1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "noroutes", InNoRoutes);
- rrddim_set(st, "truncated", InTruncatedPkts);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(do_mcast == CONFIG_ONDEMAND_YES || (do_mcast == CONFIG_ONDEMAND_ONDEMAND && (InMcastOctets || OutMcastOctets))) {
- do_mcast = CONFIG_ONDEMAND_YES;
- st = rrdset_find("ipv4.mcast");
- if(!st) {
- st = rrdset_create("ipv4", "mcast", NULL, "multicast", NULL, "IPv4 Multicast Bandwidth", "kilobits/s", 9000, update_every, RRDSET_TYPE_AREA);
- st->isdetail = 1;
-
- rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL);
- rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "sent", OutMcastOctets);
- rrddim_set(st, "received", InMcastOctets);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(do_bcast == CONFIG_ONDEMAND_YES || (do_bcast == CONFIG_ONDEMAND_ONDEMAND && (InBcastOctets || OutBcastOctets))) {
- do_bcast = CONFIG_ONDEMAND_YES;
- st = rrdset_find("ipv4.bcast");
- if(!st) {
- st = rrdset_create("ipv4", "bcast", NULL, "broadcast", NULL, "IPv4 Broadcast Bandwidth", "kilobits/s", 8000, update_every, RRDSET_TYPE_AREA);
- st->isdetail = 1;
-
- rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL);
- rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "sent", OutBcastOctets);
- rrddim_set(st, "received", InBcastOctets);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(do_mcast_p == CONFIG_ONDEMAND_YES || (do_mcast_p == CONFIG_ONDEMAND_ONDEMAND && (InMcastPkts || OutMcastPkts))) {
- do_mcast_p = CONFIG_ONDEMAND_YES;
- st = rrdset_find("ipv4.mcastpkts");
- if(!st) {
- st = rrdset_create("ipv4", "mcastpkts", NULL, "multicast", NULL, "IPv4 Multicast Packets", "packets/s", 9500, update_every, RRDSET_TYPE_LINE);
- st->isdetail = 1;
-
- rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "sent", OutMcastPkts);
- rrddim_set(st, "received", InMcastPkts);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(do_bcast_p == CONFIG_ONDEMAND_YES || (do_bcast_p == CONFIG_ONDEMAND_ONDEMAND && (InBcastPkts || OutBcastPkts))) {
- do_bcast_p = CONFIG_ONDEMAND_YES;
- st = rrdset_find("ipv4.bcastpkts");
- if(!st) {
- st = rrdset_create("ipv4", "bcastpkts", NULL, "broadcast", NULL, "IPv4 Broadcast Packets", "packets/s", 8500, update_every, RRDSET_TYPE_LINE);
- st->isdetail = 1;
-
- rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "sent", OutBcastPkts);
- rrddim_set(st, "received", InBcastPkts);
- rrdset_done(st);
- }
- }
- }
-
- return 0;
+ static int do_bandwidth = -1, do_inerrors = -1, do_mcast = -1, do_bcast = -1, do_mcast_p = -1, do_bcast_p = -1;
+ static procfile *ff = NULL;
+
+ if(do_bandwidth == -1) do_bandwidth = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "bandwidth", CONFIG_ONDEMAND_ONDEMAND);
+ if(do_inerrors == -1) do_inerrors = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "input errors", CONFIG_ONDEMAND_ONDEMAND);
+ if(do_mcast == -1) do_mcast = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "multicast bandwidth", CONFIG_ONDEMAND_ONDEMAND);
+ if(do_bcast == -1) do_bcast = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "broadcast bandwidth", CONFIG_ONDEMAND_ONDEMAND);
+ if(do_mcast_p == -1) do_mcast_p = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "multicast packets", CONFIG_ONDEMAND_ONDEMAND);
+ if(do_bcast_p == -1) do_bcast_p = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "broadcast packets", CONFIG_ONDEMAND_ONDEMAND);
+
+ if(dt) {};
+
+ if(!ff) {
+ char filename[FILENAME_MAX + 1];
+ snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/net/netstat");
+ ff = procfile_open(config_get("plugin:proc:/proc/net/netstat", "filename to monitor", filename), " \t:", PROCFILE_FLAG_DEFAULT);
+ }
+ if(!ff) return 1;
+
+ ff = procfile_readall(ff);
+ if(!ff) return 0; // we return 0, so that we will retry to open it next time
+
+ uint32_t lines = procfile_lines(ff), l;
+ uint32_t words;
+
+ for(l = 0; l < lines ;l++) {
+ if(strcmp(procfile_lineword(ff, l, 0), "IpExt") == 0) {
+ l++; // we need the next line
+
+ if(strcmp(procfile_lineword(ff, l, 0), "IpExt") != 0) {
+ error("Cannot read IpExt line from /proc/net/netstat.");
+ break;
+ }
+ words = procfile_linewords(ff, l);
+ if(words < 12) {
+ error("Cannot read /proc/net/netstat IpExt line. Expected 12 params, read %u.", words);
+ continue;
+ }
+
+ unsigned long long
+ InNoRoutes = 0, InTruncatedPkts = 0,
+ InOctets = 0, InMcastPkts = 0, InBcastPkts = 0, InMcastOctets = 0, InBcastOctets = 0,
+ OutOctets = 0, OutMcastPkts = 0, OutBcastPkts = 0, OutMcastOctets = 0, OutBcastOctets = 0;
+
+ InNoRoutes = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
+ InTruncatedPkts = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
+ InMcastPkts = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
+ OutMcastPkts = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
+ InBcastPkts = strtoull(procfile_lineword(ff, l, 5), NULL, 10);
+ OutBcastPkts = strtoull(procfile_lineword(ff, l, 6), NULL, 10);
+ InOctets = strtoull(procfile_lineword(ff, l, 7), NULL, 10);
+ OutOctets = strtoull(procfile_lineword(ff, l, 8), NULL, 10);
+ InMcastOctets = strtoull(procfile_lineword(ff, l, 9), NULL, 10);
+ OutMcastOctets = strtoull(procfile_lineword(ff, l, 10), NULL, 10);
+ InBcastOctets = strtoull(procfile_lineword(ff, l, 11), NULL, 10);
+ OutBcastOctets = strtoull(procfile_lineword(ff, l, 12), NULL, 10);
+
+ RRDSET *st;
+
+ // --------------------------------------------------------------------
+
+ if(do_bandwidth == CONFIG_ONDEMAND_YES || (do_bandwidth == CONFIG_ONDEMAND_ONDEMAND && (InOctets || OutOctets))) {
+ do_bandwidth = CONFIG_ONDEMAND_YES;
+ st = rrdset_find("system.ipv4");
+ if(!st) {
+ st = rrdset_create("system", "ipv4", NULL, "network", NULL, "IPv4 Bandwidth", "kilobits/s", 500, update_every, RRDSET_TYPE_AREA);
+
+ rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "sent", OutOctets);
+ rrddim_set(st, "received", InOctets);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_inerrors == CONFIG_ONDEMAND_YES || (do_inerrors == CONFIG_ONDEMAND_ONDEMAND && (InNoRoutes || InTruncatedPkts))) {
+ do_inerrors = CONFIG_ONDEMAND_YES;
+ st = rrdset_find("ipv4.inerrors");
+ if(!st) {
+ st = rrdset_create("ipv4", "inerrors", NULL, "errors", NULL, "IPv4 Input Errors", "packets/s", 4000, update_every, RRDSET_TYPE_LINE);
+ st->isdetail = 1;
+
+ rrddim_add(st, "noroutes", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "truncated", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "noroutes", InNoRoutes);
+ rrddim_set(st, "truncated", InTruncatedPkts);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_mcast == CONFIG_ONDEMAND_YES || (do_mcast == CONFIG_ONDEMAND_ONDEMAND && (InMcastOctets || OutMcastOctets))) {
+ do_mcast = CONFIG_ONDEMAND_YES;
+ st = rrdset_find("ipv4.mcast");
+ if(!st) {
+ st = rrdset_create("ipv4", "mcast", NULL, "multicast", NULL, "IPv4 Multicast Bandwidth", "kilobits/s", 9000, update_every, RRDSET_TYPE_AREA);
+ st->isdetail = 1;
+
+ rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "sent", OutMcastOctets);
+ rrddim_set(st, "received", InMcastOctets);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_bcast == CONFIG_ONDEMAND_YES || (do_bcast == CONFIG_ONDEMAND_ONDEMAND && (InBcastOctets || OutBcastOctets))) {
+ do_bcast = CONFIG_ONDEMAND_YES;
+ st = rrdset_find("ipv4.bcast");
+ if(!st) {
+ st = rrdset_create("ipv4", "bcast", NULL, "broadcast", NULL, "IPv4 Broadcast Bandwidth", "kilobits/s", 8000, update_every, RRDSET_TYPE_AREA);
+ st->isdetail = 1;
+
+ rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "sent", OutBcastOctets);
+ rrddim_set(st, "received", InBcastOctets);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_mcast_p == CONFIG_ONDEMAND_YES || (do_mcast_p == CONFIG_ONDEMAND_ONDEMAND && (InMcastPkts || OutMcastPkts))) {
+ do_mcast_p = CONFIG_ONDEMAND_YES;
+ st = rrdset_find("ipv4.mcastpkts");
+ if(!st) {
+ st = rrdset_create("ipv4", "mcastpkts", NULL, "multicast", NULL, "IPv4 Multicast Packets", "packets/s", 9500, update_every, RRDSET_TYPE_LINE);
+ st->isdetail = 1;
+
+ rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "sent", OutMcastPkts);
+ rrddim_set(st, "received", InMcastPkts);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_bcast_p == CONFIG_ONDEMAND_YES || (do_bcast_p == CONFIG_ONDEMAND_ONDEMAND && (InBcastPkts || OutBcastPkts))) {
+ do_bcast_p = CONFIG_ONDEMAND_YES;
+ st = rrdset_find("ipv4.bcastpkts");
+ if(!st) {
+ st = rrdset_create("ipv4", "bcastpkts", NULL, "broadcast", NULL, "IPv4 Broadcast Packets", "packets/s", 8500, update_every, RRDSET_TYPE_LINE);
+ st->isdetail = 1;
+
+ rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "sent", OutBcastPkts);
+ rrddim_set(st, "received", InBcastPkts);
+ rrdset_done(st);
+ }
+ }
+ }
+
+ return 0;
}
diff --git a/src/proc_net_rpc_nfsd.c b/src/proc_net_rpc_nfsd.c
index 6c6dd7066..0323b4dfb 100644
--- a/src/proc_net_rpc_nfsd.c
+++ b/src/proc_net_rpc_nfsd.c
@@ -1,693 +1,681 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
#include "common.h"
-#include "log.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "rrd.h"
-#include "plugin_proc.h"
struct nfsd_procs {
- char name[30];
- unsigned long long proc2;
- unsigned long long proc3;
- unsigned long long proc4;
- int present2;
- int present3;
- int present4;
+ char name[30];
+ unsigned long long proc2;
+ unsigned long long proc3;
+ unsigned long long proc4;
+ int present2;
+ int present3;
+ int present4;
};
struct nfsd_procs nfsd_proc_values[] = {
- { "null", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
- { "getattr", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
- { "setattr", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
- { "lookup", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
- { "access", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
- { "readlink", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
- { "read", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
- { "write", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
- { "create", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
- { "mkdir", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
- { "symlink", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
- { "mknod", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
- { "remove", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
- { "rmdir", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
- { "rename", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
- { "link", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
- { "readdir", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
- { "readdirplus", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
- { "fsstat", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
- { "fsinfo", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
- { "pathconf", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
- { "commit", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
- { "", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
+ { "null", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
+ { "getattr", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
+ { "setattr", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
+ { "lookup", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
+ { "access", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
+ { "readlink", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
+ { "read", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
+ { "write", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
+ { "create", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
+ { "mkdir", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
+ { "symlink", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
+ { "mknod", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
+ { "remove", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
+ { "rmdir", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
+ { "rename", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
+ { "link", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
+ { "readdir", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
+ { "readdirplus", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
+ { "fsstat", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
+ { "fsinfo", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
+ { "pathconf", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
+ { "commit", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
+ { "", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
};
struct nfsd4_ops {
- char name[30];
- unsigned long long value;
- int present;
+ char name[30];
+ unsigned long long value;
+ int present;
};
struct nfsd4_ops nfsd4_ops_values[] = {
- { "access", 0ULL, 0},
- { "close", 0ULL, 0},
- { "commit", 0ULL, 0},
- { "create", 0ULL, 0},
- { "delegpurge", 0ULL, 0},
- { "delegreturn", 0ULL, 0},
- { "getattr", 0ULL, 0},
- { "getfh", 0ULL, 0},
- { "link", 0ULL, 0},
- { "lock", 0ULL, 0},
- { "lockt", 0ULL, 0},
- { "locku", 0ULL, 0},
- { "lookup", 0ULL, 0},
- { "lookupp", 0ULL, 0},
- { "nverify", 0ULL, 0},
- { "open", 0ULL, 0},
- { "openattr", 0ULL, 0},
- { "open_confirm", 0ULL, 0},
- { "open_downgrade", 0ULL, 0},
- { "putfh", 0ULL, 0},
- { "putpubfh", 0ULL, 0},
- { "putrootfh", 0ULL, 0},
- { "read", 0ULL, 0},
- { "readdir", 0ULL, 0},
- { "readlink", 0ULL, 0},
- { "remove", 0ULL, 0},
- { "rename", 0ULL, 0},
- { "renew", 0ULL, 0},
- { "restorefh", 0ULL, 0},
- { "savefh", 0ULL, 0},
- { "secinfo", 0ULL, 0},
- { "setattr", 0ULL, 0},
- { "setclientid", 0ULL, 0},
- { "setclientid_confirm", 0ULL, 0},
- { "verify", 0ULL, 0},
- { "write", 0ULL, 0},
- { "release_lockowner", 0ULL, 0},
-
- /* nfs41 */
- { "backchannel_ctl", 0ULL, 0},
- { "bind_conn_to_session", 0ULL, 0},
- { "exchange_id", 0ULL, 0},
- { "create_session", 0ULL, 0},
- { "destroy_session", 0ULL, 0},
- { "free_stateid", 0ULL, 0},
- { "get_dir_delegation", 0ULL, 0},
- { "getdeviceinfo", 0ULL, 0},
- { "getdevicelist", 0ULL, 0},
- { "layoutcommit", 0ULL, 0},
- { "layoutget", 0ULL, 0},
- { "layoutreturn", 0ULL, 0},
- { "secinfo_no_name", 0ULL, 0},
- { "sequence", 0ULL, 0},
- { "set_ssv", 0ULL, 0},
- { "test_stateid", 0ULL, 0},
- { "want_delegation", 0ULL, 0},
- { "destroy_clientid", 0ULL, 0},
- { "reclaim_complete", 0ULL, 0},
-
- /* nfs42 */
- { "allocate", 0ULL, 0},
- { "copy", 0ULL, 0},
- { "copy_notify", 0ULL, 0},
- { "deallocate", 0ULL, 0},
- { "io_advise", 0ULL, 0},
- { "layouterror", 0ULL, 0},
- { "layoutstats", 0ULL, 0},
- { "offload_cancel", 0ULL, 0},
- { "offload_status", 0ULL, 0},
- { "read_plus", 0ULL, 0},
- { "seek", 0ULL, 0},
- { "write_same", 0ULL, 0},
-
- /* termination */
- { "", 0ULL, 0 }
+ { "access", 0ULL, 0},
+ { "close", 0ULL, 0},
+ { "commit", 0ULL, 0},
+ { "create", 0ULL, 0},
+ { "delegpurge", 0ULL, 0},
+ { "delegreturn", 0ULL, 0},
+ { "getattr", 0ULL, 0},
+ { "getfh", 0ULL, 0},
+ { "link", 0ULL, 0},
+ { "lock", 0ULL, 0},
+ { "lockt", 0ULL, 0},
+ { "locku", 0ULL, 0},
+ { "lookup", 0ULL, 0},
+ { "lookupp", 0ULL, 0},
+ { "nverify", 0ULL, 0},
+ { "open", 0ULL, 0},
+ { "openattr", 0ULL, 0},
+ { "open_confirm", 0ULL, 0},
+ { "open_downgrade", 0ULL, 0},
+ { "putfh", 0ULL, 0},
+ { "putpubfh", 0ULL, 0},
+ { "putrootfh", 0ULL, 0},
+ { "read", 0ULL, 0},
+ { "readdir", 0ULL, 0},
+ { "readlink", 0ULL, 0},
+ { "remove", 0ULL, 0},
+ { "rename", 0ULL, 0},
+ { "renew", 0ULL, 0},
+ { "restorefh", 0ULL, 0},
+ { "savefh", 0ULL, 0},
+ { "secinfo", 0ULL, 0},
+ { "setattr", 0ULL, 0},
+ { "setclientid", 0ULL, 0},
+ { "setclientid_confirm", 0ULL, 0},
+ { "verify", 0ULL, 0},
+ { "write", 0ULL, 0},
+ { "release_lockowner", 0ULL, 0},
+
+ /* nfs41 */
+ { "backchannel_ctl", 0ULL, 0},
+ { "bind_conn_to_session", 0ULL, 0},
+ { "exchange_id", 0ULL, 0},
+ { "create_session", 0ULL, 0},
+ { "destroy_session", 0ULL, 0},
+ { "free_stateid", 0ULL, 0},
+ { "get_dir_delegation", 0ULL, 0},
+ { "getdeviceinfo", 0ULL, 0},
+ { "getdevicelist", 0ULL, 0},
+ { "layoutcommit", 0ULL, 0},
+ { "layoutget", 0ULL, 0},
+ { "layoutreturn", 0ULL, 0},
+ { "secinfo_no_name", 0ULL, 0},
+ { "sequence", 0ULL, 0},
+ { "set_ssv", 0ULL, 0},
+ { "test_stateid", 0ULL, 0},
+ { "want_delegation", 0ULL, 0},
+ { "destroy_clientid", 0ULL, 0},
+ { "reclaim_complete", 0ULL, 0},
+
+ /* nfs42 */
+ { "allocate", 0ULL, 0},
+ { "copy", 0ULL, 0},
+ { "copy_notify", 0ULL, 0},
+ { "deallocate", 0ULL, 0},
+ { "io_advise", 0ULL, 0},
+ { "layouterror", 0ULL, 0},
+ { "layoutstats", 0ULL, 0},
+ { "offload_cancel", 0ULL, 0},
+ { "offload_status", 0ULL, 0},
+ { "read_plus", 0ULL, 0},
+ { "seek", 0ULL, 0},
+ { "write_same", 0ULL, 0},
+
+ /* termination */
+ { "", 0ULL, 0 }
};
int do_proc_net_rpc_nfsd(int update_every, unsigned long long dt) {
- static procfile *ff = NULL;
- static int do_rc = -1, do_fh = -1, do_io = -1, do_th = -1, do_ra = -1, do_net = -1, do_rpc = -1, do_proc2 = -1, do_proc3 = -1, do_proc4 = -1, do_proc4ops = -1;
- static int ra_warning = 0, th_warning = 0, proc2_warning = 0, proc3_warning = 0, proc4_warning = 0, proc4ops_warning = 0;
-
- if(dt) {};
-
- if(!ff) {
- char filename[FILENAME_MAX + 1];
- snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/net/rpc/nfsd");
- ff = procfile_open(config_get("plugin:proc:/proc/net/rpc/nfsd", "filename to monitor", filename), " \t", PROCFILE_FLAG_DEFAULT);
- }
- if(!ff) return 1;
-
- ff = procfile_readall(ff);
- if(!ff) return 0; // we return 0, so that we will retry to open it next time
-
- if(do_rc == -1) do_rc = config_get_boolean("plugin:proc:/proc/net/rpc/nfsd", "read cache", 1);
- if(do_fh == -1) do_fh = config_get_boolean("plugin:proc:/proc/net/rpc/nfsd", "file handles", 1);
- if(do_io == -1) do_io = config_get_boolean("plugin:proc:/proc/net/rpc/nfsd", "I/O", 1);
- if(do_th == -1) do_th = config_get_boolean("plugin:proc:/proc/net/rpc/nfsd", "threads", 1);
- if(do_ra == -1) do_ra = config_get_boolean("plugin:proc:/proc/net/rpc/nfsd", "read ahead", 1);
- if(do_net == -1) do_net = config_get_boolean("plugin:proc:/proc/net/rpc/nfsd", "network", 1);
- if(do_rpc == -1) do_rpc = config_get_boolean("plugin:proc:/proc/net/rpc/nfsd", "rpc", 1);
- if(do_proc2 == -1) do_proc2 = config_get_boolean("plugin:proc:/proc/net/rpc/nfsd", "NFS v2 procedures", 1);
- if(do_proc3 == -1) do_proc3 = config_get_boolean("plugin:proc:/proc/net/rpc/nfsd", "NFS v3 procedures", 1);
- if(do_proc4 == -1) do_proc4 = config_get_boolean("plugin:proc:/proc/net/rpc/nfsd", "NFS v4 procedures", 1);
- if(do_proc4ops == -1) do_proc4ops = config_get_boolean("plugin:proc:/proc/net/rpc/nfsd", "NFS v4 operations", 1);
-
- // if they are enabled, reset them to 1
- // later we do them =2 to avoid doing strcmp for all lines
- if(do_rc) do_rc = 1;
- if(do_fh) do_fh = 1;
- if(do_io) do_io = 1;
- if(do_th) do_th = 1;
- if(do_ra) do_ra = 1;
- if(do_net) do_net = 1;
- if(do_rpc) do_rpc = 1;
- if(do_proc2) do_proc2 = 1;
- if(do_proc3) do_proc3 = 1;
- if(do_proc4) do_proc4 = 1;
- if(do_proc4ops) do_proc4ops = 1;
-
- uint32_t lines = procfile_lines(ff), l;
- uint32_t words;
-
- char *type;
- unsigned long long rc_hits = 0, rc_misses = 0, rc_nocache = 0;
- unsigned long long fh_stale = 0, fh_total_lookups = 0, fh_anonymous_lookups = 0, fh_dir_not_in_dcache = 0, fh_non_dir_not_in_dcache = 0;
- unsigned long long io_read = 0, io_write = 0;
- unsigned long long th_threads = 0, th_fullcnt = 0, th_hist10 = 0, th_hist20 = 0, th_hist30 = 0, th_hist40 = 0, th_hist50 = 0, th_hist60 = 0, th_hist70 = 0, th_hist80 = 0, th_hist90 = 0, th_hist100 = 0;
- unsigned long long ra_size = 0, ra_hist10 = 0, ra_hist20 = 0, ra_hist30 = 0, ra_hist40 = 0, ra_hist50 = 0, ra_hist60 = 0, ra_hist70 = 0, ra_hist80 = 0, ra_hist90 = 0, ra_hist100 = 0, ra_none = 0;
- unsigned long long net_count = 0, net_udp_count = 0, net_tcp_count = 0, net_tcp_connections = 0;
- unsigned long long rpc_count = 0, rpc_bad_format = 0, rpc_bad_auth = 0, rpc_bad_client = 0;
-
- for(l = 0; l < lines ;l++) {
- words = procfile_linewords(ff, l);
- if(!words) continue;
-
- type = procfile_lineword(ff, l, 0);
-
- if(do_rc == 1 && strcmp(type, "rc") == 0) {
- if(words < 4) {
- error("%s line of /proc/net/rpc/nfsd has %d words, expected %d", type, words, 4);
- continue;
- }
-
- rc_hits = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
- rc_misses = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
- rc_nocache = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
-
- unsigned long long sum = rc_hits + rc_misses + rc_nocache;
- if(sum == 0ULL) do_rc = -1;
- else do_rc = 2;
- }
- else if(do_fh == 1 && strcmp(type, "fh") == 0) {
- if(words < 6) {
- error("%s line of /proc/net/rpc/nfsd has %d words, expected %d", type, words, 6);
- continue;
- }
-
- fh_stale = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
- fh_total_lookups = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
- fh_anonymous_lookups = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
- fh_dir_not_in_dcache = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
- fh_non_dir_not_in_dcache = strtoull(procfile_lineword(ff, l, 5), NULL, 10);
-
- unsigned long long sum = fh_stale + fh_total_lookups + fh_anonymous_lookups + fh_dir_not_in_dcache + fh_non_dir_not_in_dcache;
- if(sum == 0ULL) do_fh = -1;
- else do_fh = 2;
- }
- else if(do_io == 1 && strcmp(type, "io") == 0) {
- if(words < 3) {
- error("%s line of /proc/net/rpc/nfsd has %d words, expected %d", type, words, 3);
- continue;
- }
-
- io_read = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
- io_write = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
-
- unsigned long long sum = io_read + io_write;
- if(sum == 0ULL) do_io = -1;
- else do_io = 2;
- }
- else if(do_th == 1 && strcmp(type, "th") == 0) {
- if(words < 13) {
- error("%s line of /proc/net/rpc/nfsd has %d words, expected %d", type, words, 13);
- continue;
- }
-
- th_threads = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
- th_fullcnt = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
- th_hist10 = (unsigned long long)(atof(procfile_lineword(ff, l, 3)) * 1000.0);
- th_hist20 = (unsigned long long)(atof(procfile_lineword(ff, l, 4)) * 1000.0);
- th_hist30 = (unsigned long long)(atof(procfile_lineword(ff, l, 5)) * 1000.0);
- th_hist40 = (unsigned long long)(atof(procfile_lineword(ff, l, 6)) * 1000.0);
- th_hist50 = (unsigned long long)(atof(procfile_lineword(ff, l, 7)) * 1000.0);
- th_hist60 = (unsigned long long)(atof(procfile_lineword(ff, l, 8)) * 1000.0);
- th_hist70 = (unsigned long long)(atof(procfile_lineword(ff, l, 9)) * 1000.0);
- th_hist80 = (unsigned long long)(atof(procfile_lineword(ff, l, 10)) * 1000.0);
- th_hist90 = (unsigned long long)(atof(procfile_lineword(ff, l, 11)) * 1000.0);
- th_hist100 = (unsigned long long)(atof(procfile_lineword(ff, l, 12)) * 1000.0);
-
- // threads histogram has been disabled on recent kernels
- // http://permalink.gmane.org/gmane.linux.nfs/24528
- unsigned long long sum = th_hist10 + th_hist20 + th_hist30 + th_hist40 + th_hist50 + th_hist60 + th_hist70 + th_hist80 + th_hist90 + th_hist100;
- if(sum == 0ULL) {
- if(!th_warning) {
- info("Disabling /proc/net/rpc/nfsd threads histogram. It seems unused on this machine. It will be enabled automatically when found with data in it.");
- th_warning = 1;
- }
- do_th = -1;
- }
- else do_th = 2;
- }
- else if(do_ra == 1 && strcmp(type, "ra") == 0) {
- if(words < 13) {
- error("%s line of /proc/net/rpc/nfsd has %d words, expected %d", type, words, 13);
- continue;
- }
-
- ra_size = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
- ra_hist10 = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
- ra_hist20 = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
- ra_hist30 = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
- ra_hist40 = strtoull(procfile_lineword(ff, l, 5), NULL, 10);
- ra_hist50 = strtoull(procfile_lineword(ff, l, 6), NULL, 10);
- ra_hist60 = strtoull(procfile_lineword(ff, l, 7), NULL, 10);
- ra_hist70 = strtoull(procfile_lineword(ff, l, 8), NULL, 10);
- ra_hist80 = strtoull(procfile_lineword(ff, l, 9), NULL, 10);
- ra_hist90 = strtoull(procfile_lineword(ff, l, 10), NULL, 10);
- ra_hist100 = strtoull(procfile_lineword(ff, l, 11), NULL, 10);
- ra_none = strtoull(procfile_lineword(ff, l, 12), NULL, 10);
-
- unsigned long long sum = ra_hist10 + ra_hist20 + ra_hist30 + ra_hist40 + ra_hist50 + ra_hist60 + ra_hist70 + ra_hist80 + ra_hist90 + ra_hist100 + ra_none;
- if(sum == 0ULL) {
- if(!ra_warning) {
- info("Disabling /proc/net/rpc/nfsd read ahead histogram. It seems unused on this machine. It will be enabled automatically when found with data in it.");
- ra_warning = 1;
- }
- do_ra = -1;
- }
- else do_ra = 2;
- }
- else if(do_net == 1 && strcmp(type, "net") == 0) {
- if(words < 5) {
- error("%s line of /proc/net/rpc/nfsd has %d words, expected %d", type, words, 5);
- continue;
- }
-
- net_count = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
- net_udp_count = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
- net_tcp_count = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
- net_tcp_connections = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
-
- unsigned long long sum = net_count + net_udp_count + net_tcp_count + net_tcp_connections;
- if(sum == 0ULL) do_net = -1;
- else do_net = 2;
- }
- else if(do_rpc == 1 && strcmp(type, "rpc") == 0) {
- if(words < 6) {
- error("%s line of /proc/net/rpc/nfsd has %d words, expected %d", type, words, 6);
- continue;
- }
-
- rpc_count = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
- rpc_bad_format = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
- rpc_bad_auth = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
- rpc_bad_client = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
-
- unsigned long long sum = rpc_count + rpc_bad_format + rpc_bad_auth + rpc_bad_client;
- if(sum == 0ULL) do_rpc = -1;
- else do_rpc = 2;
- }
- else if(do_proc2 == 1 && strcmp(type, "proc2") == 0) {
- // the first number is the count of numbers present
- // so we start for word 2
-
- unsigned long long sum = 0;
- unsigned int i, j;
- for(i = 0, j = 2; j < words && nfsd_proc_values[i].name[0] ; i++, j++) {
- nfsd_proc_values[i].proc2 = strtoull(procfile_lineword(ff, l, j), NULL, 10);
- nfsd_proc_values[i].present2 = 1;
- sum += nfsd_proc_values[i].proc2;
- }
-
- if(sum == 0ULL) {
- if(!proc2_warning) {
- error("Disabling /proc/net/rpc/nfsd v2 procedure calls chart. It seems unused on this machine. It will be enabled automatically when found with data in it.");
- proc2_warning = 1;
- }
- do_proc2 = 0;
- }
- else do_proc2 = 2;
- }
- else if(do_proc3 == 1 && strcmp(type, "proc3") == 0) {
- // the first number is the count of numbers present
- // so we start for word 2
-
- unsigned long long sum = 0;
- unsigned int i, j;
- for(i = 0, j = 2; j < words && nfsd_proc_values[i].name[0] ; i++, j++) {
- nfsd_proc_values[i].proc3 = strtoull(procfile_lineword(ff, l, j), NULL, 10);
- nfsd_proc_values[i].present3 = 1;
- sum += nfsd_proc_values[i].proc3;
- }
-
- if(sum == 0ULL) {
- if(!proc3_warning) {
- info("Disabling /proc/net/rpc/nfsd v3 procedure calls chart. It seems unused on this machine. It will be enabled automatically when found with data in it.");
- proc3_warning = 1;
- }
- do_proc3 = 0;
- }
- else do_proc3 = 2;
- }
- else if(do_proc4 == 1 && strcmp(type, "proc4") == 0) {
- // the first number is the count of numbers present
- // so we start for word 2
-
- unsigned long long sum = 0;
- unsigned int i, j;
- for(i = 0, j = 2; j < words && nfsd_proc_values[i].name[0] ; i++, j++) {
- nfsd_proc_values[i].proc4 = strtoull(procfile_lineword(ff, l, j), NULL, 10);
- nfsd_proc_values[i].present4 = 1;
- sum += nfsd_proc_values[i].proc4;
- }
-
- if(sum == 0ULL) {
- if(!proc4_warning) {
- info("Disabling /proc/net/rpc/nfsd v4 procedure calls chart. It seems unused on this machine. It will be enabled automatically when found with data in it.");
- proc4_warning = 1;
- }
- do_proc4 = 0;
- }
- else do_proc4 = 2;
- }
- else if(do_proc4ops == 1 && strcmp(type, "proc4ops") == 0) {
- // the first number is the count of numbers present
- // so we start for word 2
-
- unsigned long long sum = 0;
- unsigned int i, j;
- for(i = 0, j = 2; j < words && nfsd4_ops_values[i].name[0] ; i++, j++) {
- nfsd4_ops_values[i].value = strtoull(procfile_lineword(ff, l, j), NULL, 10);
- nfsd4_ops_values[i].present = 1;
- sum += nfsd4_ops_values[i].value;
- }
-
- if(sum == 0ULL) {
- if(!proc4ops_warning) {
- info("Disabling /proc/net/rpc/nfsd v4 operations chart. It seems unused on this machine. It will be enabled automatically when found with data in it.");
- proc4ops_warning = 1;
- }
- do_proc4ops = 0;
- }
- else do_proc4ops = 2;
- }
- }
-
- RRDSET *st;
-
- // --------------------------------------------------------------------
-
- if(do_rc == 2) {
- st = rrdset_find_bytype("nfsd", "readcache");
- if(!st) {
- st = rrdset_create("nfsd", "readcache", NULL, "nfsd", NULL, "Read Cache", "reads/s", 5000, update_every, RRDSET_TYPE_STACKED);
-
- rrddim_add(st, "hits", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "misses", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "nocache", NULL, 1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "hits", rc_hits);
- rrddim_set(st, "misses", rc_misses);
- rrddim_set(st, "nocache", rc_nocache);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(do_fh == 2) {
- st = rrdset_find_bytype("nfsd", "filehandles");
- if(!st) {
- st = rrdset_create("nfsd", "filehandles", NULL, "nfsd", NULL, "File Handles", "handles/s", 5001, update_every, RRDSET_TYPE_LINE);
- st->isdetail = 1;
-
- rrddim_add(st, "stale", NULL, 1, 1, RRDDIM_ABSOLUTE);
- rrddim_add(st, "total_lookups", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "anonymous_lookups", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "dir_not_in_dcache", NULL, -1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "non_dir_not_in_dcache", NULL, -1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "stale", fh_stale);
- rrddim_set(st, "total_lookups", fh_total_lookups);
- rrddim_set(st, "anonymous_lookups", fh_anonymous_lookups);
- rrddim_set(st, "dir_not_in_dcache", fh_dir_not_in_dcache);
- rrddim_set(st, "non_dir_not_in_dcache", fh_non_dir_not_in_dcache);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(do_io == 2) {
- st = rrdset_find_bytype("nfsd", "io");
- if(!st) {
- st = rrdset_create("nfsd", "io", NULL, "nfsd", NULL, "I/O", "kilobytes/s", 5002, update_every, RRDSET_TYPE_AREA);
-
- rrddim_add(st, "read", NULL, 1, 1000, RRDDIM_INCREMENTAL);
- rrddim_add(st, "write", NULL, -1, 1000, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "read", io_read);
- rrddim_set(st, "write", io_write);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(do_th == 2) {
- st = rrdset_find_bytype("nfsd", "threads");
- if(!st) {
- st = rrdset_create("nfsd", "threads", NULL, "nfsd", NULL, "Threads", "threads", 5003, update_every, RRDSET_TYPE_LINE);
-
- rrddim_add(st, "threads", NULL, 1, 1, RRDDIM_ABSOLUTE);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "threads", th_threads);
- rrdset_done(st);
-
- st = rrdset_find_bytype("nfsd", "threads_fullcnt");
- if(!st) {
- st = rrdset_create("nfsd", "threads_fullcnt", NULL, "nfsd", NULL, "Threads Full Count", "ops/s", 5004, update_every, RRDSET_TYPE_LINE);
-
- rrddim_add(st, "full_count", NULL, 1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "full_count", th_fullcnt);
- rrdset_done(st);
-
- st = rrdset_find_bytype("nfsd", "threads_histogram");
- if(!st) {
- st = rrdset_create("nfsd", "threads_histogram", NULL, "nfsd", NULL, "Threads Usage Histogram", "percentage", 5005, update_every, RRDSET_TYPE_LINE);
-
- rrddim_add(st, "0%-10%", NULL, 1, 1000, RRDDIM_ABSOLUTE);
- rrddim_add(st, "10%-20%", NULL, 1, 1000, RRDDIM_ABSOLUTE);
- rrddim_add(st, "20%-30%", NULL, 1, 1000, RRDDIM_ABSOLUTE);
- rrddim_add(st, "30%-40%", NULL, 1, 1000, RRDDIM_ABSOLUTE);
- rrddim_add(st, "40%-50%", NULL, 1, 1000, RRDDIM_ABSOLUTE);
- rrddim_add(st, "50%-60%", NULL, 1, 1000, RRDDIM_ABSOLUTE);
- rrddim_add(st, "60%-70%", NULL, 1, 1000, RRDDIM_ABSOLUTE);
- rrddim_add(st, "70%-80%", NULL, 1, 1000, RRDDIM_ABSOLUTE);
- rrddim_add(st, "80%-90%", NULL, 1, 1000, RRDDIM_ABSOLUTE);
- rrddim_add(st, "90%-100%", NULL, 1, 1000, RRDDIM_ABSOLUTE);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "0%-10%", th_hist10);
- rrddim_set(st, "10%-20%", th_hist20);
- rrddim_set(st, "20%-30%", th_hist30);
- rrddim_set(st, "30%-40%", th_hist40);
- rrddim_set(st, "40%-50%", th_hist50);
- rrddim_set(st, "50%-60%", th_hist60);
- rrddim_set(st, "60%-70%", th_hist70);
- rrddim_set(st, "70%-80%", th_hist80);
- rrddim_set(st, "80%-90%", th_hist90);
- rrddim_set(st, "90%-100%", th_hist100);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(do_ra == 2) {
- st = rrdset_find_bytype("nfsd", "readahead");
- if(!st) {
- st = rrdset_create("nfsd", "readahead", NULL, "nfsd", NULL, "Read Ahead Depth", "percentage", 5005, update_every, RRDSET_TYPE_STACKED);
-
- rrddim_add(st, "10%", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
- rrddim_add(st, "20%", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
- rrddim_add(st, "30%", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
- rrddim_add(st, "40%", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
- rrddim_add(st, "50%", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
- rrddim_add(st, "60%", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
- rrddim_add(st, "70%", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
- rrddim_add(st, "80%", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
- rrddim_add(st, "90%", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
- rrddim_add(st, "100%", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
- rrddim_add(st, "misses", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
- }
- else rrdset_next(st);
-
- // ignore ra_size
- if(ra_size) {};
-
- rrddim_set(st, "10%", ra_hist10);
- rrddim_set(st, "20%", ra_hist20);
- rrddim_set(st, "30%", ra_hist30);
- rrddim_set(st, "40%", ra_hist40);
- rrddim_set(st, "50%", ra_hist50);
- rrddim_set(st, "60%", ra_hist60);
- rrddim_set(st, "70%", ra_hist70);
- rrddim_set(st, "80%", ra_hist80);
- rrddim_set(st, "90%", ra_hist90);
- rrddim_set(st, "100%", ra_hist100);
- rrddim_set(st, "misses", ra_none);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(do_net == 2) {
- st = rrdset_find_bytype("nfsd", "net");
- if(!st) {
- st = rrdset_create("nfsd", "net", NULL, "nfsd", NULL, "Network Reads", "reads/s", 5007, update_every, RRDSET_TYPE_STACKED);
- st->isdetail = 1;
-
- rrddim_add(st, "udp", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "tcp", NULL, 1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- // ignore net_count, net_tcp_connections
- if(net_count) {};
- if(net_tcp_connections) {};
-
- rrddim_set(st, "udp", net_udp_count);
- rrddim_set(st, "tcp", net_tcp_count);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(do_rpc == 2) {
- st = rrdset_find_bytype("nfsd", "rpc");
- if(!st) {
- st = rrdset_create("nfsd", "rpc", NULL, "nfsd", NULL, "Remote Procedure Calls", "calls/s", 5008, update_every, RRDSET_TYPE_LINE);
- st->isdetail = 1;
-
- rrddim_add(st, "all", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "bad_format", NULL, -1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "bad_auth", NULL, -1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- // ignore rpc_bad_client
- if(rpc_bad_client) {};
-
- rrddim_set(st, "all", rpc_count);
- rrddim_set(st, "bad_format", rpc_bad_format);
- rrddim_set(st, "bad_auth", rpc_bad_auth);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(do_proc2 == 2) {
- unsigned int i;
- st = rrdset_find_bytype("nfsd", "proc2");
- if(!st) {
- st = rrdset_create("nfsd", "proc2", NULL, "nfsd", NULL, "NFS v2 Calls", "calls/s", 5009, update_every, RRDSET_TYPE_STACKED);
-
- for(i = 0; nfsd_proc_values[i].present2 ; i++)
- rrddim_add(st, nfsd_proc_values[i].name, NULL, 1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- for(i = 0; nfsd_proc_values[i].present2 ; i++)
- rrddim_set(st, nfsd_proc_values[i].name, nfsd_proc_values[i].proc2);
-
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(do_proc3 == 2) {
- unsigned int i;
- st = rrdset_find_bytype("nfsd", "proc3");
- if(!st) {
- st = rrdset_create("nfsd", "proc3", NULL, "nfsd", NULL, "NFS v3 Calls", "calls/s", 5010, update_every, RRDSET_TYPE_STACKED);
-
- for(i = 0; nfsd_proc_values[i].present3 ; i++)
- rrddim_add(st, nfsd_proc_values[i].name, NULL, 1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- for(i = 0; nfsd_proc_values[i].present3 ; i++)
- rrddim_set(st, nfsd_proc_values[i].name, nfsd_proc_values[i].proc3);
-
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(do_proc4 == 2) {
- unsigned int i;
- st = rrdset_find_bytype("nfsd", "proc4");
- if(!st) {
- st = rrdset_create("nfsd", "proc4", NULL, "nfsd", NULL, "NFS v4 Calls", "calls/s", 5011, update_every, RRDSET_TYPE_STACKED);
-
- for(i = 0; nfsd_proc_values[i].present4 ; i++)
- rrddim_add(st, nfsd_proc_values[i].name, NULL, 1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
+ static procfile *ff = NULL;
+ static int do_rc = -1, do_fh = -1, do_io = -1, do_th = -1, do_ra = -1, do_net = -1, do_rpc = -1, do_proc2 = -1, do_proc3 = -1, do_proc4 = -1, do_proc4ops = -1;
+ static int ra_warning = 0, th_warning = 0, proc2_warning = 0, proc3_warning = 0, proc4_warning = 0, proc4ops_warning = 0;
+
+ if(dt) {};
+
+ if(!ff) {
+ char filename[FILENAME_MAX + 1];
+ snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/net/rpc/nfsd");
+ ff = procfile_open(config_get("plugin:proc:/proc/net/rpc/nfsd", "filename to monitor", filename), " \t", PROCFILE_FLAG_DEFAULT);
+ }
+ if(!ff) return 1;
+
+ ff = procfile_readall(ff);
+ if(!ff) return 0; // we return 0, so that we will retry to open it next time
+
+ if(do_rc == -1) do_rc = config_get_boolean("plugin:proc:/proc/net/rpc/nfsd", "read cache", 1);
+ if(do_fh == -1) do_fh = config_get_boolean("plugin:proc:/proc/net/rpc/nfsd", "file handles", 1);
+ if(do_io == -1) do_io = config_get_boolean("plugin:proc:/proc/net/rpc/nfsd", "I/O", 1);
+ if(do_th == -1) do_th = config_get_boolean("plugin:proc:/proc/net/rpc/nfsd", "threads", 1);
+ if(do_ra == -1) do_ra = config_get_boolean("plugin:proc:/proc/net/rpc/nfsd", "read ahead", 1);
+ if(do_net == -1) do_net = config_get_boolean("plugin:proc:/proc/net/rpc/nfsd", "network", 1);
+ if(do_rpc == -1) do_rpc = config_get_boolean("plugin:proc:/proc/net/rpc/nfsd", "rpc", 1);
+ if(do_proc2 == -1) do_proc2 = config_get_boolean("plugin:proc:/proc/net/rpc/nfsd", "NFS v2 procedures", 1);
+ if(do_proc3 == -1) do_proc3 = config_get_boolean("plugin:proc:/proc/net/rpc/nfsd", "NFS v3 procedures", 1);
+ if(do_proc4 == -1) do_proc4 = config_get_boolean("plugin:proc:/proc/net/rpc/nfsd", "NFS v4 procedures", 1);
+ if(do_proc4ops == -1) do_proc4ops = config_get_boolean("plugin:proc:/proc/net/rpc/nfsd", "NFS v4 operations", 1);
+
+ // if they are enabled, reset them to 1
+ // later we do them =2 to avoid doing strcmp for all lines
+ if(do_rc) do_rc = 1;
+ if(do_fh) do_fh = 1;
+ if(do_io) do_io = 1;
+ if(do_th) do_th = 1;
+ if(do_ra) do_ra = 1;
+ if(do_net) do_net = 1;
+ if(do_rpc) do_rpc = 1;
+ if(do_proc2) do_proc2 = 1;
+ if(do_proc3) do_proc3 = 1;
+ if(do_proc4) do_proc4 = 1;
+ if(do_proc4ops) do_proc4ops = 1;
+
+ uint32_t lines = procfile_lines(ff), l;
+ uint32_t words;
+
+ char *type;
+ unsigned long long rc_hits = 0, rc_misses = 0, rc_nocache = 0;
+ unsigned long long fh_stale = 0, fh_total_lookups = 0, fh_anonymous_lookups = 0, fh_dir_not_in_dcache = 0, fh_non_dir_not_in_dcache = 0;
+ unsigned long long io_read = 0, io_write = 0;
+ unsigned long long th_threads = 0, th_fullcnt = 0, th_hist10 = 0, th_hist20 = 0, th_hist30 = 0, th_hist40 = 0, th_hist50 = 0, th_hist60 = 0, th_hist70 = 0, th_hist80 = 0, th_hist90 = 0, th_hist100 = 0;
+ unsigned long long ra_size = 0, ra_hist10 = 0, ra_hist20 = 0, ra_hist30 = 0, ra_hist40 = 0, ra_hist50 = 0, ra_hist60 = 0, ra_hist70 = 0, ra_hist80 = 0, ra_hist90 = 0, ra_hist100 = 0, ra_none = 0;
+ unsigned long long net_count = 0, net_udp_count = 0, net_tcp_count = 0, net_tcp_connections = 0;
+ unsigned long long rpc_count = 0, rpc_bad_format = 0, rpc_bad_auth = 0, rpc_bad_client = 0;
+
+ for(l = 0; l < lines ;l++) {
+ words = procfile_linewords(ff, l);
+ if(!words) continue;
+
+ type = procfile_lineword(ff, l, 0);
+
+ if(do_rc == 1 && strcmp(type, "rc") == 0) {
+ if(words < 4) {
+ error("%s line of /proc/net/rpc/nfsd has %u words, expected %d", type, words, 4);
+ continue;
+ }
+
+ rc_hits = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
+ rc_misses = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
+ rc_nocache = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
+
+ unsigned long long sum = rc_hits + rc_misses + rc_nocache;
+ if(sum == 0ULL) do_rc = -1;
+ else do_rc = 2;
+ }
+ else if(do_fh == 1 && strcmp(type, "fh") == 0) {
+ if(words < 6) {
+ error("%s line of /proc/net/rpc/nfsd has %u words, expected %d", type, words, 6);
+ continue;
+ }
+
+ fh_stale = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
+ fh_total_lookups = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
+ fh_anonymous_lookups = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
+ fh_dir_not_in_dcache = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
+ fh_non_dir_not_in_dcache = strtoull(procfile_lineword(ff, l, 5), NULL, 10);
+
+ unsigned long long sum = fh_stale + fh_total_lookups + fh_anonymous_lookups + fh_dir_not_in_dcache + fh_non_dir_not_in_dcache;
+ if(sum == 0ULL) do_fh = -1;
+ else do_fh = 2;
+ }
+ else if(do_io == 1 && strcmp(type, "io") == 0) {
+ if(words < 3) {
+ error("%s line of /proc/net/rpc/nfsd has %u words, expected %d", type, words, 3);
+ continue;
+ }
+
+ io_read = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
+ io_write = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
+
+ unsigned long long sum = io_read + io_write;
+ if(sum == 0ULL) do_io = -1;
+ else do_io = 2;
+ }
+ else if(do_th == 1 && strcmp(type, "th") == 0) {
+ if(words < 13) {
+ error("%s line of /proc/net/rpc/nfsd has %u words, expected %d", type, words, 13);
+ continue;
+ }
+
+ th_threads = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
+ th_fullcnt = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
+ th_hist10 = (unsigned long long)(atof(procfile_lineword(ff, l, 3)) * 1000.0);
+ th_hist20 = (unsigned long long)(atof(procfile_lineword(ff, l, 4)) * 1000.0);
+ th_hist30 = (unsigned long long)(atof(procfile_lineword(ff, l, 5)) * 1000.0);
+ th_hist40 = (unsigned long long)(atof(procfile_lineword(ff, l, 6)) * 1000.0);
+ th_hist50 = (unsigned long long)(atof(procfile_lineword(ff, l, 7)) * 1000.0);
+ th_hist60 = (unsigned long long)(atof(procfile_lineword(ff, l, 8)) * 1000.0);
+ th_hist70 = (unsigned long long)(atof(procfile_lineword(ff, l, 9)) * 1000.0);
+ th_hist80 = (unsigned long long)(atof(procfile_lineword(ff, l, 10)) * 1000.0);
+ th_hist90 = (unsigned long long)(atof(procfile_lineword(ff, l, 11)) * 1000.0);
+ th_hist100 = (unsigned long long)(atof(procfile_lineword(ff, l, 12)) * 1000.0);
+
+ // threads histogram has been disabled on recent kernels
+ // http://permalink.gmane.org/gmane.linux.nfs/24528
+ unsigned long long sum = th_hist10 + th_hist20 + th_hist30 + th_hist40 + th_hist50 + th_hist60 + th_hist70 + th_hist80 + th_hist90 + th_hist100;
+ if(sum == 0ULL) {
+ if(!th_warning) {
+ info("Disabling /proc/net/rpc/nfsd threads histogram. It seems unused on this machine. It will be enabled automatically when found with data in it.");
+ th_warning = 1;
+ }
+ do_th = -1;
+ }
+ else do_th = 2;
+ }
+ else if(do_ra == 1 && strcmp(type, "ra") == 0) {
+ if(words < 13) {
+ error("%s line of /proc/net/rpc/nfsd has %u words, expected %d", type, words, 13);
+ continue;
+ }
+
+ ra_size = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
+ ra_hist10 = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
+ ra_hist20 = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
+ ra_hist30 = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
+ ra_hist40 = strtoull(procfile_lineword(ff, l, 5), NULL, 10);
+ ra_hist50 = strtoull(procfile_lineword(ff, l, 6), NULL, 10);
+ ra_hist60 = strtoull(procfile_lineword(ff, l, 7), NULL, 10);
+ ra_hist70 = strtoull(procfile_lineword(ff, l, 8), NULL, 10);
+ ra_hist80 = strtoull(procfile_lineword(ff, l, 9), NULL, 10);
+ ra_hist90 = strtoull(procfile_lineword(ff, l, 10), NULL, 10);
+ ra_hist100 = strtoull(procfile_lineword(ff, l, 11), NULL, 10);
+ ra_none = strtoull(procfile_lineword(ff, l, 12), NULL, 10);
+
+ unsigned long long sum = ra_hist10 + ra_hist20 + ra_hist30 + ra_hist40 + ra_hist50 + ra_hist60 + ra_hist70 + ra_hist80 + ra_hist90 + ra_hist100 + ra_none;
+ if(sum == 0ULL) {
+ if(!ra_warning) {
+ info("Disabling /proc/net/rpc/nfsd read ahead histogram. It seems unused on this machine. It will be enabled automatically when found with data in it.");
+ ra_warning = 1;
+ }
+ do_ra = -1;
+ }
+ else do_ra = 2;
+ }
+ else if(do_net == 1 && strcmp(type, "net") == 0) {
+ if(words < 5) {
+ error("%s line of /proc/net/rpc/nfsd has %u words, expected %d", type, words, 5);
+ continue;
+ }
+
+ net_count = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
+ net_udp_count = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
+ net_tcp_count = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
+ net_tcp_connections = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
+
+ unsigned long long sum = net_count + net_udp_count + net_tcp_count + net_tcp_connections;
+ if(sum == 0ULL) do_net = -1;
+ else do_net = 2;
+ }
+ else if(do_rpc == 1 && strcmp(type, "rpc") == 0) {
+ if(words < 6) {
+ error("%s line of /proc/net/rpc/nfsd has %u words, expected %d", type, words, 6);
+ continue;
+ }
+
+ rpc_count = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
+ rpc_bad_format = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
+ rpc_bad_auth = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
+ rpc_bad_client = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
+
+ unsigned long long sum = rpc_count + rpc_bad_format + rpc_bad_auth + rpc_bad_client;
+ if(sum == 0ULL) do_rpc = -1;
+ else do_rpc = 2;
+ }
+ else if(do_proc2 == 1 && strcmp(type, "proc2") == 0) {
+ // the first number is the count of numbers present
+ // so we start for word 2
+
+ unsigned long long sum = 0;
+ unsigned int i, j;
+ for(i = 0, j = 2; j < words && nfsd_proc_values[i].name[0] ; i++, j++) {
+ nfsd_proc_values[i].proc2 = strtoull(procfile_lineword(ff, l, j), NULL, 10);
+ nfsd_proc_values[i].present2 = 1;
+ sum += nfsd_proc_values[i].proc2;
+ }
+
+ if(sum == 0ULL) {
+ if(!proc2_warning) {
+ error("Disabling /proc/net/rpc/nfsd v2 procedure calls chart. It seems unused on this machine. It will be enabled automatically when found with data in it.");
+ proc2_warning = 1;
+ }
+ do_proc2 = 0;
+ }
+ else do_proc2 = 2;
+ }
+ else if(do_proc3 == 1 && strcmp(type, "proc3") == 0) {
+ // the first number is the count of numbers present
+ // so we start for word 2
+
+ unsigned long long sum = 0;
+ unsigned int i, j;
+ for(i = 0, j = 2; j < words && nfsd_proc_values[i].name[0] ; i++, j++) {
+ nfsd_proc_values[i].proc3 = strtoull(procfile_lineword(ff, l, j), NULL, 10);
+ nfsd_proc_values[i].present3 = 1;
+ sum += nfsd_proc_values[i].proc3;
+ }
+
+ if(sum == 0ULL) {
+ if(!proc3_warning) {
+ info("Disabling /proc/net/rpc/nfsd v3 procedure calls chart. It seems unused on this machine. It will be enabled automatically when found with data in it.");
+ proc3_warning = 1;
+ }
+ do_proc3 = 0;
+ }
+ else do_proc3 = 2;
+ }
+ else if(do_proc4 == 1 && strcmp(type, "proc4") == 0) {
+ // the first number is the count of numbers present
+ // so we start for word 2
+
+ unsigned long long sum = 0;
+ unsigned int i, j;
+ for(i = 0, j = 2; j < words && nfsd_proc_values[i].name[0] ; i++, j++) {
+ nfsd_proc_values[i].proc4 = strtoull(procfile_lineword(ff, l, j), NULL, 10);
+ nfsd_proc_values[i].present4 = 1;
+ sum += nfsd_proc_values[i].proc4;
+ }
+
+ if(sum == 0ULL) {
+ if(!proc4_warning) {
+ info("Disabling /proc/net/rpc/nfsd v4 procedure calls chart. It seems unused on this machine. It will be enabled automatically when found with data in it.");
+ proc4_warning = 1;
+ }
+ do_proc4 = 0;
+ }
+ else do_proc4 = 2;
+ }
+ else if(do_proc4ops == 1 && strcmp(type, "proc4ops") == 0) {
+ // the first number is the count of numbers present
+ // so we start for word 2
+
+ unsigned long long sum = 0;
+ unsigned int i, j;
+ for(i = 0, j = 2; j < words && nfsd4_ops_values[i].name[0] ; i++, j++) {
+ nfsd4_ops_values[i].value = strtoull(procfile_lineword(ff, l, j), NULL, 10);
+ nfsd4_ops_values[i].present = 1;
+ sum += nfsd4_ops_values[i].value;
+ }
+
+ if(sum == 0ULL) {
+ if(!proc4ops_warning) {
+ info("Disabling /proc/net/rpc/nfsd v4 operations chart. It seems unused on this machine. It will be enabled automatically when found with data in it.");
+ proc4ops_warning = 1;
+ }
+ do_proc4ops = 0;
+ }
+ else do_proc4ops = 2;
+ }
+ }
+
+ RRDSET *st;
+
+ // --------------------------------------------------------------------
+
+ if(do_rc == 2) {
+ st = rrdset_find_bytype("nfsd", "readcache");
+ if(!st) {
+ st = rrdset_create("nfsd", "readcache", NULL, "nfsd", NULL, "Read Cache", "reads/s", 5000, update_every, RRDSET_TYPE_STACKED);
+
+ rrddim_add(st, "hits", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "misses", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "nocache", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "hits", rc_hits);
+ rrddim_set(st, "misses", rc_misses);
+ rrddim_set(st, "nocache", rc_nocache);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_fh == 2) {
+ st = rrdset_find_bytype("nfsd", "filehandles");
+ if(!st) {
+ st = rrdset_create("nfsd", "filehandles", NULL, "nfsd", NULL, "File Handles", "handles/s", 5001, update_every, RRDSET_TYPE_LINE);
+ st->isdetail = 1;
+
+ rrddim_add(st, "stale", NULL, 1, 1, RRDDIM_ABSOLUTE);
+ rrddim_add(st, "total_lookups", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "anonymous_lookups", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "dir_not_in_dcache", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "non_dir_not_in_dcache", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "stale", fh_stale);
+ rrddim_set(st, "total_lookups", fh_total_lookups);
+ rrddim_set(st, "anonymous_lookups", fh_anonymous_lookups);
+ rrddim_set(st, "dir_not_in_dcache", fh_dir_not_in_dcache);
+ rrddim_set(st, "non_dir_not_in_dcache", fh_non_dir_not_in_dcache);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_io == 2) {
+ st = rrdset_find_bytype("nfsd", "io");
+ if(!st) {
+ st = rrdset_create("nfsd", "io", NULL, "nfsd", NULL, "I/O", "kilobytes/s", 5002, update_every, RRDSET_TYPE_AREA);
+
+ rrddim_add(st, "read", NULL, 1, 1000, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "write", NULL, -1, 1000, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "read", io_read);
+ rrddim_set(st, "write", io_write);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_th == 2) {
+ st = rrdset_find_bytype("nfsd", "threads");
+ if(!st) {
+ st = rrdset_create("nfsd", "threads", NULL, "nfsd", NULL, "Threads", "threads", 5003, update_every, RRDSET_TYPE_LINE);
+
+ rrddim_add(st, "threads", NULL, 1, 1, RRDDIM_ABSOLUTE);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "threads", th_threads);
+ rrdset_done(st);
+
+ st = rrdset_find_bytype("nfsd", "threads_fullcnt");
+ if(!st) {
+ st = rrdset_create("nfsd", "threads_fullcnt", NULL, "nfsd", NULL, "Threads Full Count", "ops/s", 5004, update_every, RRDSET_TYPE_LINE);
+
+ rrddim_add(st, "full_count", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "full_count", th_fullcnt);
+ rrdset_done(st);
+
+ st = rrdset_find_bytype("nfsd", "threads_histogram");
+ if(!st) {
+ st = rrdset_create("nfsd", "threads_histogram", NULL, "nfsd", NULL, "Threads Usage Histogram", "percentage", 5005, update_every, RRDSET_TYPE_LINE);
+
+ rrddim_add(st, "0%-10%", NULL, 1, 1000, RRDDIM_ABSOLUTE);
+ rrddim_add(st, "10%-20%", NULL, 1, 1000, RRDDIM_ABSOLUTE);
+ rrddim_add(st, "20%-30%", NULL, 1, 1000, RRDDIM_ABSOLUTE);
+ rrddim_add(st, "30%-40%", NULL, 1, 1000, RRDDIM_ABSOLUTE);
+ rrddim_add(st, "40%-50%", NULL, 1, 1000, RRDDIM_ABSOLUTE);
+ rrddim_add(st, "50%-60%", NULL, 1, 1000, RRDDIM_ABSOLUTE);
+ rrddim_add(st, "60%-70%", NULL, 1, 1000, RRDDIM_ABSOLUTE);
+ rrddim_add(st, "70%-80%", NULL, 1, 1000, RRDDIM_ABSOLUTE);
+ rrddim_add(st, "80%-90%", NULL, 1, 1000, RRDDIM_ABSOLUTE);
+ rrddim_add(st, "90%-100%", NULL, 1, 1000, RRDDIM_ABSOLUTE);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "0%-10%", th_hist10);
+ rrddim_set(st, "10%-20%", th_hist20);
+ rrddim_set(st, "20%-30%", th_hist30);
+ rrddim_set(st, "30%-40%", th_hist40);
+ rrddim_set(st, "40%-50%", th_hist50);
+ rrddim_set(st, "50%-60%", th_hist60);
+ rrddim_set(st, "60%-70%", th_hist70);
+ rrddim_set(st, "70%-80%", th_hist80);
+ rrddim_set(st, "80%-90%", th_hist90);
+ rrddim_set(st, "90%-100%", th_hist100);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_ra == 2) {
+ st = rrdset_find_bytype("nfsd", "readahead");
+ if(!st) {
+ st = rrdset_create("nfsd", "readahead", NULL, "nfsd", NULL, "Read Ahead Depth", "percentage", 5005, update_every, RRDSET_TYPE_STACKED);
+
+ rrddim_add(st, "10%", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
+ rrddim_add(st, "20%", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
+ rrddim_add(st, "30%", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
+ rrddim_add(st, "40%", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
+ rrddim_add(st, "50%", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
+ rrddim_add(st, "60%", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
+ rrddim_add(st, "70%", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
+ rrddim_add(st, "80%", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
+ rrddim_add(st, "90%", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
+ rrddim_add(st, "100%", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
+ rrddim_add(st, "misses", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
+ }
+ else rrdset_next(st);
+
+ // ignore ra_size
+ if(ra_size) {};
+
+ rrddim_set(st, "10%", ra_hist10);
+ rrddim_set(st, "20%", ra_hist20);
+ rrddim_set(st, "30%", ra_hist30);
+ rrddim_set(st, "40%", ra_hist40);
+ rrddim_set(st, "50%", ra_hist50);
+ rrddim_set(st, "60%", ra_hist60);
+ rrddim_set(st, "70%", ra_hist70);
+ rrddim_set(st, "80%", ra_hist80);
+ rrddim_set(st, "90%", ra_hist90);
+ rrddim_set(st, "100%", ra_hist100);
+ rrddim_set(st, "misses", ra_none);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_net == 2) {
+ st = rrdset_find_bytype("nfsd", "net");
+ if(!st) {
+ st = rrdset_create("nfsd", "net", NULL, "nfsd", NULL, "Network Reads", "reads/s", 5007, update_every, RRDSET_TYPE_STACKED);
+ st->isdetail = 1;
+
+ rrddim_add(st, "udp", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "tcp", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ // ignore net_count, net_tcp_connections
+ if(net_count) {};
+ if(net_tcp_connections) {};
+
+ rrddim_set(st, "udp", net_udp_count);
+ rrddim_set(st, "tcp", net_tcp_count);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_rpc == 2) {
+ st = rrdset_find_bytype("nfsd", "rpc");
+ if(!st) {
+ st = rrdset_create("nfsd", "rpc", NULL, "nfsd", NULL, "Remote Procedure Calls", "calls/s", 5008, update_every, RRDSET_TYPE_LINE);
+ st->isdetail = 1;
+
+ rrddim_add(st, "all", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "bad_format", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "bad_auth", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ // ignore rpc_bad_client
+ if(rpc_bad_client) {};
+
+ rrddim_set(st, "all", rpc_count);
+ rrddim_set(st, "bad_format", rpc_bad_format);
+ rrddim_set(st, "bad_auth", rpc_bad_auth);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_proc2 == 2) {
+ unsigned int i;
+ st = rrdset_find_bytype("nfsd", "proc2");
+ if(!st) {
+ st = rrdset_create("nfsd", "proc2", NULL, "nfsd", NULL, "NFS v2 Calls", "calls/s", 5009, update_every, RRDSET_TYPE_STACKED);
+
+ for(i = 0; nfsd_proc_values[i].present2 ; i++)
+ rrddim_add(st, nfsd_proc_values[i].name, NULL, 1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ for(i = 0; nfsd_proc_values[i].present2 ; i++)
+ rrddim_set(st, nfsd_proc_values[i].name, nfsd_proc_values[i].proc2);
+
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_proc3 == 2) {
+ unsigned int i;
+ st = rrdset_find_bytype("nfsd", "proc3");
+ if(!st) {
+ st = rrdset_create("nfsd", "proc3", NULL, "nfsd", NULL, "NFS v3 Calls", "calls/s", 5010, update_every, RRDSET_TYPE_STACKED);
+
+ for(i = 0; nfsd_proc_values[i].present3 ; i++)
+ rrddim_add(st, nfsd_proc_values[i].name, NULL, 1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ for(i = 0; nfsd_proc_values[i].present3 ; i++)
+ rrddim_set(st, nfsd_proc_values[i].name, nfsd_proc_values[i].proc3);
+
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_proc4 == 2) {
+ unsigned int i;
+ st = rrdset_find_bytype("nfsd", "proc4");
+ if(!st) {
+ st = rrdset_create("nfsd", "proc4", NULL, "nfsd", NULL, "NFS v4 Calls", "calls/s", 5011, update_every, RRDSET_TYPE_STACKED);
+
+ for(i = 0; nfsd_proc_values[i].present4 ; i++)
+ rrddim_add(st, nfsd_proc_values[i].name, NULL, 1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
- for(i = 0; nfsd_proc_values[i].present4 ; i++)
- rrddim_set(st, nfsd_proc_values[i].name, nfsd_proc_values[i].proc4);
+ for(i = 0; nfsd_proc_values[i].present4 ; i++)
+ rrddim_set(st, nfsd_proc_values[i].name, nfsd_proc_values[i].proc4);
- rrdset_done(st);
- }
+ rrdset_done(st);
+ }
- // --------------------------------------------------------------------
+ // --------------------------------------------------------------------
- if(do_proc4ops == 2) {
- unsigned int i;
- st = rrdset_find_bytype("nfsd", "proc4ops");
- if(!st) {
- st = rrdset_create("nfsd", "proc4ops", NULL, "nfsd", NULL, "NFS v4 Operations", "operations/s", 5012, update_every, RRDSET_TYPE_STACKED);
+ if(do_proc4ops == 2) {
+ unsigned int i;
+ st = rrdset_find_bytype("nfsd", "proc4ops");
+ if(!st) {
+ st = rrdset_create("nfsd", "proc4ops", NULL, "nfsd", NULL, "NFS v4 Operations", "operations/s", 5012, update_every, RRDSET_TYPE_STACKED);
- for(i = 0; nfsd4_ops_values[i].present ; i++)
- rrddim_add(st, nfsd4_ops_values[i].name, NULL, 1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
+ for(i = 0; nfsd4_ops_values[i].present ; i++)
+ rrddim_add(st, nfsd4_ops_values[i].name, NULL, 1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
- for(i = 0; nfsd4_ops_values[i].present ; i++)
- rrddim_set(st, nfsd4_ops_values[i].name, nfsd4_ops_values[i].value);
+ for(i = 0; nfsd4_ops_values[i].present ; i++)
+ rrddim_set(st, nfsd4_ops_values[i].name, nfsd4_ops_values[i].value);
- rrdset_done(st);
- }
+ rrdset_done(st);
+ }
- return 0;
+ return 0;
}
diff --git a/src/proc_net_snmp.c b/src/proc_net_snmp.c
index e0ac6a263..a773f55f6 100644
--- a/src/proc_net_snmp.c
+++ b/src/proc_net_snmp.c
@@ -1,366 +1,354 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
#include "common.h"
-#include "log.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "rrd.h"
-#include "plugin_proc.h"
-#define RRD_TYPE_NET_SNMP "ipv4"
-#define RRD_TYPE_NET_SNMP_LEN strlen(RRD_TYPE_NET_SNMP)
+#define RRD_TYPE_NET_SNMP "ipv4"
+#define RRD_TYPE_NET_SNMP_LEN strlen(RRD_TYPE_NET_SNMP)
int do_proc_net_snmp(int update_every, unsigned long long dt) {
- static procfile *ff = NULL;
- static int do_ip_packets = -1, do_ip_fragsout = -1, do_ip_fragsin = -1, do_ip_errors = -1,
- do_tcp_sockets = -1, do_tcp_packets = -1, do_tcp_errors = -1, do_tcp_handshake = -1,
- do_udp_packets = -1, do_udp_errors = -1;
-
- if(do_ip_packets == -1) do_ip_packets = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 packets", 1);
- if(do_ip_fragsout == -1) do_ip_fragsout = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 fragments sent", 1);
- if(do_ip_fragsin == -1) do_ip_fragsin = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 fragments assembly", 1);
- if(do_ip_errors == -1) do_ip_errors = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 errors", 1);
- if(do_tcp_sockets == -1) do_tcp_sockets = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 TCP connections", 1);
- if(do_tcp_packets == -1) do_tcp_packets = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 TCP packets", 1);
- if(do_tcp_errors == -1) do_tcp_errors = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 TCP errors", 1);
- if(do_tcp_handshake == -1) do_tcp_handshake = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 TCP handshake issues", 1);
- if(do_udp_packets == -1) do_udp_packets = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 UDP packets", 1);
- if(do_udp_errors == -1) do_udp_errors = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 UDP errors", 1);
-
- if(dt) {};
-
- if(!ff) {
- char filename[FILENAME_MAX + 1];
- snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/net/snmp");
- ff = procfile_open(config_get("plugin:proc:/proc/net/snmp", "filename to monitor", filename), " \t:", PROCFILE_FLAG_DEFAULT);
- }
- if(!ff) return 1;
-
- ff = procfile_readall(ff);
- if(!ff) return 0; // we return 0, so that we will retry to open it next time
-
- uint32_t lines = procfile_lines(ff), l;
- uint32_t words;
-
- RRDSET *st;
-
- for(l = 0; l < lines ;l++) {
- if(strcmp(procfile_lineword(ff, l, 0), "Ip") == 0) {
- l++;
-
- if(strcmp(procfile_lineword(ff, l, 0), "Ip") != 0) {
- error("Cannot read Ip line from /proc/net/snmp.");
- break;
- }
-
- words = procfile_linewords(ff, l);
- if(words < 20) {
- error("Cannot read /proc/net/snmp Ip line. Expected 20 params, read %d.", words);
- continue;
- }
-
- // see also http://net-snmp.sourceforge.net/docs/mibs/ip.html
- unsigned long long Forwarding, DefaultTTL, InReceives, InHdrErrors, InAddrErrors, ForwDatagrams, InUnknownProtos, InDiscards, InDelivers,
- OutRequests, OutDiscards, OutNoRoutes, ReasmTimeout, ReasmReqds, ReasmOKs, ReasmFails, FragOKs, FragFails, FragCreates;
-
- Forwarding = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
- DefaultTTL = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
- InReceives = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
- InHdrErrors = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
- InAddrErrors = strtoull(procfile_lineword(ff, l, 5), NULL, 10);
- ForwDatagrams = strtoull(procfile_lineword(ff, l, 6), NULL, 10);
- InUnknownProtos = strtoull(procfile_lineword(ff, l, 7), NULL, 10);
- InDiscards = strtoull(procfile_lineword(ff, l, 8), NULL, 10);
- InDelivers = strtoull(procfile_lineword(ff, l, 9), NULL, 10);
- OutRequests = strtoull(procfile_lineword(ff, l, 10), NULL, 10);
- OutDiscards = strtoull(procfile_lineword(ff, l, 11), NULL, 10);
- OutNoRoutes = strtoull(procfile_lineword(ff, l, 12), NULL, 10);
- ReasmTimeout = strtoull(procfile_lineword(ff, l, 13), NULL, 10);
- ReasmReqds = strtoull(procfile_lineword(ff, l, 14), NULL, 10);
- ReasmOKs = strtoull(procfile_lineword(ff, l, 15), NULL, 10);
- ReasmFails = strtoull(procfile_lineword(ff, l, 16), NULL, 10);
- FragOKs = strtoull(procfile_lineword(ff, l, 17), NULL, 10);
- FragFails = strtoull(procfile_lineword(ff, l, 18), NULL, 10);
- FragCreates = strtoull(procfile_lineword(ff, l, 19), NULL, 10);
-
- // these are not counters
- if(Forwarding) {}; // is forwarding enabled?
- if(DefaultTTL) {}; // the default ttl on packets
- if(ReasmTimeout) {}; // Reassembly timeout
-
- // this counter is not used
- if(InDelivers) {}; // total number of packets delivered to IP user-protocols
-
- // --------------------------------------------------------------------
-
- if(do_ip_packets) {
- st = rrdset_find(RRD_TYPE_NET_SNMP ".packets");
- if(!st) {
- st = rrdset_create(RRD_TYPE_NET_SNMP, "packets", NULL, "packets", NULL, "IPv4 Packets", "packets/s", 3000, update_every, RRDSET_TYPE_LINE);
-
- rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "forwarded", NULL, 1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "sent", OutRequests);
- rrddim_set(st, "received", InReceives);
- rrddim_set(st, "forwarded", ForwDatagrams);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(do_ip_fragsout) {
- st = rrdset_find(RRD_TYPE_NET_SNMP ".fragsout");
- if(!st) {
- st = rrdset_create(RRD_TYPE_NET_SNMP, "fragsout", NULL, "fragments", NULL, "IPv4 Fragments Sent", "packets/s", 3010, update_every, RRDSET_TYPE_LINE);
- st->isdetail = 1;
-
- rrddim_add(st, "ok", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "failed", NULL, -1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "all", NULL, 1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "ok", FragOKs);
- rrddim_set(st, "failed", FragFails);
- rrddim_set(st, "all", FragCreates);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(do_ip_fragsin) {
- st = rrdset_find(RRD_TYPE_NET_SNMP ".fragsin");
- if(!st) {
- st = rrdset_create(RRD_TYPE_NET_SNMP, "fragsin", NULL, "fragments", NULL, "IPv4 Fragments Reassembly", "packets/s", 3011, update_every, RRDSET_TYPE_LINE);
- st->isdetail = 1;
-
- rrddim_add(st, "ok", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "failed", NULL, -1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "all", NULL, 1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "ok", ReasmOKs);
- rrddim_set(st, "failed", ReasmFails);
- rrddim_set(st, "all", ReasmReqds);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(do_ip_errors) {
- st = rrdset_find(RRD_TYPE_NET_SNMP ".errors");
- if(!st) {
- st = rrdset_create(RRD_TYPE_NET_SNMP, "errors", NULL, "errors", NULL, "IPv4 Errors", "packets/s", 3002, update_every, RRDSET_TYPE_LINE);
- st->isdetail = 1;
-
- rrddim_add(st, "InDiscards", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "OutDiscards", NULL, -1, 1, RRDDIM_INCREMENTAL);
-
- rrddim_add(st, "InHdrErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "InAddrErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "InUnknownProtos", NULL, 1, 1, RRDDIM_INCREMENTAL);
-
- rrddim_add(st, "OutNoRoutes", NULL, -1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "InDiscards", InDiscards);
- rrddim_set(st, "OutDiscards", OutDiscards);
- rrddim_set(st, "InHdrErrors", InHdrErrors);
- rrddim_set(st, "InAddrErrors", InAddrErrors);
- rrddim_set(st, "InUnknownProtos", InUnknownProtos);
- rrddim_set(st, "OutNoRoutes", OutNoRoutes);
- rrdset_done(st);
- }
- }
- else if(strcmp(procfile_lineword(ff, l, 0), "Tcp") == 0) {
- l++;
-
- if(strcmp(procfile_lineword(ff, l, 0), "Tcp") != 0) {
- error("Cannot read Tcp line from /proc/net/snmp.");
- break;
- }
-
- words = procfile_linewords(ff, l);
- if(words < 15) {
- error("Cannot read /proc/net/snmp Tcp line. Expected 15 params, read %d.", words);
- continue;
- }
-
- unsigned long long RtoAlgorithm, RtoMin, RtoMax, MaxConn, ActiveOpens, PassiveOpens, AttemptFails, EstabResets,
- CurrEstab, InSegs, OutSegs, RetransSegs, InErrs, OutRsts;
-
- RtoAlgorithm = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
- RtoMin = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
- RtoMax = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
- MaxConn = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
- ActiveOpens = strtoull(procfile_lineword(ff, l, 5), NULL, 10);
- PassiveOpens = strtoull(procfile_lineword(ff, l, 6), NULL, 10);
- AttemptFails = strtoull(procfile_lineword(ff, l, 7), NULL, 10);
- EstabResets = strtoull(procfile_lineword(ff, l, 8), NULL, 10);
- CurrEstab = strtoull(procfile_lineword(ff, l, 9), NULL, 10);
- InSegs = strtoull(procfile_lineword(ff, l, 10), NULL, 10);
- OutSegs = strtoull(procfile_lineword(ff, l, 11), NULL, 10);
- RetransSegs = strtoull(procfile_lineword(ff, l, 12), NULL, 10);
- InErrs = strtoull(procfile_lineword(ff, l, 13), NULL, 10);
- OutRsts = strtoull(procfile_lineword(ff, l, 14), NULL, 10);
-
- // these are not counters
- if(RtoAlgorithm) {};
- if(RtoMin) {};
- if(RtoMax) {};
- if(MaxConn) {};
-
- // --------------------------------------------------------------------
-
- // see http://net-snmp.sourceforge.net/docs/mibs/tcp.html
- if(do_tcp_sockets) {
- st = rrdset_find(RRD_TYPE_NET_SNMP ".tcpsock");
- if(!st) {
- st = rrdset_create(RRD_TYPE_NET_SNMP, "tcpsock", NULL, "tcp", NULL, "IPv4 TCP Connections", "active connections", 2500, update_every, RRDSET_TYPE_LINE);
-
- rrddim_add(st, "connections", NULL, 1, 1, RRDDIM_ABSOLUTE);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "connections", CurrEstab);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(do_tcp_packets) {
- st = rrdset_find(RRD_TYPE_NET_SNMP ".tcppackets");
- if(!st) {
- st = rrdset_create(RRD_TYPE_NET_SNMP, "tcppackets", NULL, "tcp", NULL, "IPv4 TCP Packets", "packets/s", 2600, update_every, RRDSET_TYPE_LINE);
-
- rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "received", InSegs);
- rrddim_set(st, "sent", OutSegs);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(do_tcp_errors) {
- st = rrdset_find(RRD_TYPE_NET_SNMP ".tcperrors");
- if(!st) {
- st = rrdset_create(RRD_TYPE_NET_SNMP, "tcperrors", NULL, "tcp", NULL, "IPv4 TCP Errors", "packets/s", 2700, update_every, RRDSET_TYPE_LINE);
- st->isdetail = 1;
-
- rrddim_add(st, "InErrs", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "RetransSegs", NULL, -1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "InErrs", InErrs);
- rrddim_set(st, "RetransSegs", RetransSegs);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(do_tcp_handshake) {
- st = rrdset_find(RRD_TYPE_NET_SNMP ".tcphandshake");
- if(!st) {
- st = rrdset_create(RRD_TYPE_NET_SNMP, "tcphandshake", NULL, "tcp", NULL, "IPv4 TCP Handshake Issues", "events/s", 2900, update_every, RRDSET_TYPE_LINE);
- st->isdetail = 1;
-
- rrddim_add(st, "EstabResets", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "OutRsts", NULL, -1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "ActiveOpens", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "PassiveOpens", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "AttemptFails", NULL, 1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "EstabResets", EstabResets);
- rrddim_set(st, "OutRsts", OutRsts);
- rrddim_set(st, "ActiveOpens", ActiveOpens);
- rrddim_set(st, "PassiveOpens", PassiveOpens);
- rrddim_set(st, "AttemptFails", AttemptFails);
- rrdset_done(st);
- }
- }
- else if(strcmp(procfile_lineword(ff, l, 0), "Udp") == 0) {
- l++;
-
- if(strcmp(procfile_lineword(ff, l, 0), "Udp") != 0) {
- error("Cannot read Udp line from /proc/net/snmp.");
- break;
- }
-
- words = procfile_linewords(ff, l);
- if(words < 7) {
- error("Cannot read /proc/net/snmp Udp line. Expected 7 params, read %d.", words);
- continue;
- }
-
- unsigned long long InDatagrams, NoPorts, InErrors, OutDatagrams, RcvbufErrors, SndbufErrors;
-
- InDatagrams = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
- NoPorts = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
- InErrors = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
- OutDatagrams = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
- RcvbufErrors = strtoull(procfile_lineword(ff, l, 5), NULL, 10);
- SndbufErrors = strtoull(procfile_lineword(ff, l, 6), NULL, 10);
-
- // --------------------------------------------------------------------
-
- // see http://net-snmp.sourceforge.net/docs/mibs/udp.html
- if(do_udp_packets) {
- st = rrdset_find(RRD_TYPE_NET_SNMP ".udppackets");
- if(!st) {
- st = rrdset_create(RRD_TYPE_NET_SNMP, "udppackets", NULL, "udp", NULL, "IPv4 UDP Packets", "packets/s", 2601, update_every, RRDSET_TYPE_LINE);
-
- rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "received", InDatagrams);
- rrddim_set(st, "sent", OutDatagrams);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(do_udp_errors) {
- st = rrdset_find(RRD_TYPE_NET_SNMP ".udperrors");
- if(!st) {
- st = rrdset_create(RRD_TYPE_NET_SNMP, "udperrors", NULL, "udp", NULL, "IPv4 UDP Errors", "events/s", 2701, update_every, RRDSET_TYPE_LINE);
- st->isdetail = 1;
-
- rrddim_add(st, "RcvbufErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "SndbufErrors", NULL, -1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "InErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "NoPorts", NULL, 1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "InErrors", InErrors);
- rrddim_set(st, "NoPorts", NoPorts);
- rrddim_set(st, "RcvbufErrors", RcvbufErrors);
- rrddim_set(st, "SndbufErrors", SndbufErrors);
- rrdset_done(st);
- }
- }
- }
-
- return 0;
+ static procfile *ff = NULL;
+ static int do_ip_packets = -1, do_ip_fragsout = -1, do_ip_fragsin = -1, do_ip_errors = -1,
+ do_tcp_sockets = -1, do_tcp_packets = -1, do_tcp_errors = -1, do_tcp_handshake = -1,
+ do_udp_packets = -1, do_udp_errors = -1;
+
+ if(do_ip_packets == -1) do_ip_packets = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 packets", 1);
+ if(do_ip_fragsout == -1) do_ip_fragsout = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 fragments sent", 1);
+ if(do_ip_fragsin == -1) do_ip_fragsin = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 fragments assembly", 1);
+ if(do_ip_errors == -1) do_ip_errors = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 errors", 1);
+ if(do_tcp_sockets == -1) do_tcp_sockets = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 TCP connections", 1);
+ if(do_tcp_packets == -1) do_tcp_packets = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 TCP packets", 1);
+ if(do_tcp_errors == -1) do_tcp_errors = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 TCP errors", 1);
+ if(do_tcp_handshake == -1) do_tcp_handshake = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 TCP handshake issues", 1);
+ if(do_udp_packets == -1) do_udp_packets = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 UDP packets", 1);
+ if(do_udp_errors == -1) do_udp_errors = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 UDP errors", 1);
+
+ if(dt) {};
+
+ if(!ff) {
+ char filename[FILENAME_MAX + 1];
+ snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/net/snmp");
+ ff = procfile_open(config_get("plugin:proc:/proc/net/snmp", "filename to monitor", filename), " \t:", PROCFILE_FLAG_DEFAULT);
+ }
+ if(!ff) return 1;
+
+ ff = procfile_readall(ff);
+ if(!ff) return 0; // we return 0, so that we will retry to open it next time
+
+ uint32_t lines = procfile_lines(ff), l;
+ uint32_t words;
+
+ RRDSET *st;
+
+ for(l = 0; l < lines ;l++) {
+ if(strcmp(procfile_lineword(ff, l, 0), "Ip") == 0) {
+ l++;
+
+ if(strcmp(procfile_lineword(ff, l, 0), "Ip") != 0) {
+ error("Cannot read Ip line from /proc/net/snmp.");
+ break;
+ }
+
+ words = procfile_linewords(ff, l);
+ if(words < 20) {
+ error("Cannot read /proc/net/snmp Ip line. Expected 20 params, read %u.", words);
+ continue;
+ }
+
+ // see also http://net-snmp.sourceforge.net/docs/mibs/ip.html
+ unsigned long long Forwarding, DefaultTTL, InReceives, InHdrErrors, InAddrErrors, ForwDatagrams, InUnknownProtos, InDiscards, InDelivers,
+ OutRequests, OutDiscards, OutNoRoutes, ReasmTimeout, ReasmReqds, ReasmOKs, ReasmFails, FragOKs, FragFails, FragCreates;
+
+ Forwarding = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
+ DefaultTTL = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
+ InReceives = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
+ InHdrErrors = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
+ InAddrErrors = strtoull(procfile_lineword(ff, l, 5), NULL, 10);
+ ForwDatagrams = strtoull(procfile_lineword(ff, l, 6), NULL, 10);
+ InUnknownProtos = strtoull(procfile_lineword(ff, l, 7), NULL, 10);
+ InDiscards = strtoull(procfile_lineword(ff, l, 8), NULL, 10);
+ InDelivers = strtoull(procfile_lineword(ff, l, 9), NULL, 10);
+ OutRequests = strtoull(procfile_lineword(ff, l, 10), NULL, 10);
+ OutDiscards = strtoull(procfile_lineword(ff, l, 11), NULL, 10);
+ OutNoRoutes = strtoull(procfile_lineword(ff, l, 12), NULL, 10);
+ ReasmTimeout = strtoull(procfile_lineword(ff, l, 13), NULL, 10);
+ ReasmReqds = strtoull(procfile_lineword(ff, l, 14), NULL, 10);
+ ReasmOKs = strtoull(procfile_lineword(ff, l, 15), NULL, 10);
+ ReasmFails = strtoull(procfile_lineword(ff, l, 16), NULL, 10);
+ FragOKs = strtoull(procfile_lineword(ff, l, 17), NULL, 10);
+ FragFails = strtoull(procfile_lineword(ff, l, 18), NULL, 10);
+ FragCreates = strtoull(procfile_lineword(ff, l, 19), NULL, 10);
+
+ // these are not counters
+ if(Forwarding) {}; // is forwarding enabled?
+ if(DefaultTTL) {}; // the default ttl on packets
+ if(ReasmTimeout) {}; // Reassembly timeout
+
+ // this counter is not used
+ if(InDelivers) {}; // total number of packets delivered to IP user-protocols
+
+ // --------------------------------------------------------------------
+
+ if(do_ip_packets) {
+ st = rrdset_find(RRD_TYPE_NET_SNMP ".packets");
+ if(!st) {
+ st = rrdset_create(RRD_TYPE_NET_SNMP, "packets", NULL, "packets", NULL, "IPv4 Packets", "packets/s", 3000, update_every, RRDSET_TYPE_LINE);
+
+ rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "forwarded", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "sent", OutRequests);
+ rrddim_set(st, "received", InReceives);
+ rrddim_set(st, "forwarded", ForwDatagrams);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_ip_fragsout) {
+ st = rrdset_find(RRD_TYPE_NET_SNMP ".fragsout");
+ if(!st) {
+ st = rrdset_create(RRD_TYPE_NET_SNMP, "fragsout", NULL, "fragments", NULL, "IPv4 Fragments Sent", "packets/s", 3010, update_every, RRDSET_TYPE_LINE);
+ st->isdetail = 1;
+
+ rrddim_add(st, "ok", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "failed", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "all", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "ok", FragOKs);
+ rrddim_set(st, "failed", FragFails);
+ rrddim_set(st, "all", FragCreates);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_ip_fragsin) {
+ st = rrdset_find(RRD_TYPE_NET_SNMP ".fragsin");
+ if(!st) {
+ st = rrdset_create(RRD_TYPE_NET_SNMP, "fragsin", NULL, "fragments", NULL, "IPv4 Fragments Reassembly", "packets/s", 3011, update_every, RRDSET_TYPE_LINE);
+ st->isdetail = 1;
+
+ rrddim_add(st, "ok", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "failed", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "all", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "ok", ReasmOKs);
+ rrddim_set(st, "failed", ReasmFails);
+ rrddim_set(st, "all", ReasmReqds);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_ip_errors) {
+ st = rrdset_find(RRD_TYPE_NET_SNMP ".errors");
+ if(!st) {
+ st = rrdset_create(RRD_TYPE_NET_SNMP, "errors", NULL, "errors", NULL, "IPv4 Errors", "packets/s", 3002, update_every, RRDSET_TYPE_LINE);
+ st->isdetail = 1;
+
+ rrddim_add(st, "InDiscards", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "OutDiscards", NULL, -1, 1, RRDDIM_INCREMENTAL);
+
+ rrddim_add(st, "InHdrErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "InAddrErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "InUnknownProtos", NULL, 1, 1, RRDDIM_INCREMENTAL);
+
+ rrddim_add(st, "OutNoRoutes", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "InDiscards", InDiscards);
+ rrddim_set(st, "OutDiscards", OutDiscards);
+ rrddim_set(st, "InHdrErrors", InHdrErrors);
+ rrddim_set(st, "InAddrErrors", InAddrErrors);
+ rrddim_set(st, "InUnknownProtos", InUnknownProtos);
+ rrddim_set(st, "OutNoRoutes", OutNoRoutes);
+ rrdset_done(st);
+ }
+ }
+ else if(strcmp(procfile_lineword(ff, l, 0), "Tcp") == 0) {
+ l++;
+
+ if(strcmp(procfile_lineword(ff, l, 0), "Tcp") != 0) {
+ error("Cannot read Tcp line from /proc/net/snmp.");
+ break;
+ }
+
+ words = procfile_linewords(ff, l);
+ if(words < 15) {
+ error("Cannot read /proc/net/snmp Tcp line. Expected 15 params, read %u.", words);
+ continue;
+ }
+
+ unsigned long long RtoAlgorithm, RtoMin, RtoMax, MaxConn, ActiveOpens, PassiveOpens, AttemptFails, EstabResets,
+ CurrEstab, InSegs, OutSegs, RetransSegs, InErrs, OutRsts;
+
+ RtoAlgorithm = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
+ RtoMin = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
+ RtoMax = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
+ MaxConn = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
+ ActiveOpens = strtoull(procfile_lineword(ff, l, 5), NULL, 10);
+ PassiveOpens = strtoull(procfile_lineword(ff, l, 6), NULL, 10);
+ AttemptFails = strtoull(procfile_lineword(ff, l, 7), NULL, 10);
+ EstabResets = strtoull(procfile_lineword(ff, l, 8), NULL, 10);
+ CurrEstab = strtoull(procfile_lineword(ff, l, 9), NULL, 10);
+ InSegs = strtoull(procfile_lineword(ff, l, 10), NULL, 10);
+ OutSegs = strtoull(procfile_lineword(ff, l, 11), NULL, 10);
+ RetransSegs = strtoull(procfile_lineword(ff, l, 12), NULL, 10);
+ InErrs = strtoull(procfile_lineword(ff, l, 13), NULL, 10);
+ OutRsts = strtoull(procfile_lineword(ff, l, 14), NULL, 10);
+
+ // these are not counters
+ if(RtoAlgorithm) {};
+ if(RtoMin) {};
+ if(RtoMax) {};
+ if(MaxConn) {};
+
+ // --------------------------------------------------------------------
+
+ // see http://net-snmp.sourceforge.net/docs/mibs/tcp.html
+ if(do_tcp_sockets) {
+ st = rrdset_find(RRD_TYPE_NET_SNMP ".tcpsock");
+ if(!st) {
+ st = rrdset_create(RRD_TYPE_NET_SNMP, "tcpsock", NULL, "tcp", NULL, "IPv4 TCP Connections", "active connections", 2500, update_every, RRDSET_TYPE_LINE);
+
+ rrddim_add(st, "connections", NULL, 1, 1, RRDDIM_ABSOLUTE);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "connections", CurrEstab);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_tcp_packets) {
+ st = rrdset_find(RRD_TYPE_NET_SNMP ".tcppackets");
+ if(!st) {
+ st = rrdset_create(RRD_TYPE_NET_SNMP, "tcppackets", NULL, "tcp", NULL, "IPv4 TCP Packets", "packets/s", 2600, update_every, RRDSET_TYPE_LINE);
+
+ rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "received", InSegs);
+ rrddim_set(st, "sent", OutSegs);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_tcp_errors) {
+ st = rrdset_find(RRD_TYPE_NET_SNMP ".tcperrors");
+ if(!st) {
+ st = rrdset_create(RRD_TYPE_NET_SNMP, "tcperrors", NULL, "tcp", NULL, "IPv4 TCP Errors", "packets/s", 2700, update_every, RRDSET_TYPE_LINE);
+ st->isdetail = 1;
+
+ rrddim_add(st, "InErrs", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "RetransSegs", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "InErrs", InErrs);
+ rrddim_set(st, "RetransSegs", RetransSegs);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_tcp_handshake) {
+ st = rrdset_find(RRD_TYPE_NET_SNMP ".tcphandshake");
+ if(!st) {
+ st = rrdset_create(RRD_TYPE_NET_SNMP, "tcphandshake", NULL, "tcp", NULL, "IPv4 TCP Handshake Issues", "events/s", 2900, update_every, RRDSET_TYPE_LINE);
+ st->isdetail = 1;
+
+ rrddim_add(st, "EstabResets", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "OutRsts", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "ActiveOpens", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "PassiveOpens", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "AttemptFails", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "EstabResets", EstabResets);
+ rrddim_set(st, "OutRsts", OutRsts);
+ rrddim_set(st, "ActiveOpens", ActiveOpens);
+ rrddim_set(st, "PassiveOpens", PassiveOpens);
+ rrddim_set(st, "AttemptFails", AttemptFails);
+ rrdset_done(st);
+ }
+ }
+ else if(strcmp(procfile_lineword(ff, l, 0), "Udp") == 0) {
+ l++;
+
+ if(strcmp(procfile_lineword(ff, l, 0), "Udp") != 0) {
+ error("Cannot read Udp line from /proc/net/snmp.");
+ break;
+ }
+
+ words = procfile_linewords(ff, l);
+ if(words < 7) {
+ error("Cannot read /proc/net/snmp Udp line. Expected 7 params, read %u.", words);
+ continue;
+ }
+
+ unsigned long long InDatagrams, NoPorts, InErrors, OutDatagrams, RcvbufErrors, SndbufErrors;
+
+ InDatagrams = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
+ NoPorts = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
+ InErrors = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
+ OutDatagrams = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
+ RcvbufErrors = strtoull(procfile_lineword(ff, l, 5), NULL, 10);
+ SndbufErrors = strtoull(procfile_lineword(ff, l, 6), NULL, 10);
+
+ // --------------------------------------------------------------------
+
+ // see http://net-snmp.sourceforge.net/docs/mibs/udp.html
+ if(do_udp_packets) {
+ st = rrdset_find(RRD_TYPE_NET_SNMP ".udppackets");
+ if(!st) {
+ st = rrdset_create(RRD_TYPE_NET_SNMP, "udppackets", NULL, "udp", NULL, "IPv4 UDP Packets", "packets/s", 2601, update_every, RRDSET_TYPE_LINE);
+
+ rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "received", InDatagrams);
+ rrddim_set(st, "sent", OutDatagrams);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_udp_errors) {
+ st = rrdset_find(RRD_TYPE_NET_SNMP ".udperrors");
+ if(!st) {
+ st = rrdset_create(RRD_TYPE_NET_SNMP, "udperrors", NULL, "udp", NULL, "IPv4 UDP Errors", "events/s", 2701, update_every, RRDSET_TYPE_LINE);
+ st->isdetail = 1;
+
+ rrddim_add(st, "RcvbufErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "SndbufErrors", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "InErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "NoPorts", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "InErrors", InErrors);
+ rrddim_set(st, "NoPorts", NoPorts);
+ rrddim_set(st, "RcvbufErrors", RcvbufErrors);
+ rrddim_set(st, "SndbufErrors", SndbufErrors);
+ rrdset_done(st);
+ }
+ }
+ }
+
+ return 0;
}
diff --git a/src/proc_net_snmp6.c b/src/proc_net_snmp6.c
index 885835a8c..97dc20edd 100644
--- a/src/proc_net_snmp6.c
+++ b/src/proc_net_snmp6.c
@@ -1,1067 +1,1054 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
#include "common.h"
-#include "log.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "rrd.h"
-#include "plugin_proc.h"
-#define RRD_TYPE_NET_SNMP6 "ipv6"
-#define RRD_TYPE_NET_SNMP6_LEN strlen(RRD_TYPE_NET_SNMP6)
+#define RRD_TYPE_NET_SNMP6 "ipv6"
+#define RRD_TYPE_NET_SNMP6_LEN strlen(RRD_TYPE_NET_SNMP6)
int do_proc_net_snmp6(int update_every, unsigned long long dt) {
- static procfile *ff = NULL;
- static int gen_hashes = -1;
-
- static int do_ip_packets = -1, do_ip_fragsout = -1, do_ip_fragsin = -1, do_ip_errors = -1,
- do_udplite_packets = -1, do_udplite_errors = -1,
- do_udp_packets = -1, do_udp_errors = -1,
- do_bandwidth = -1, do_mcast = -1, do_bcast = -1, do_mcast_p = -1,
- do_icmp = -1, do_icmp_redir = -1, do_icmp_errors = -1, do_icmp_echos = -1, do_icmp_groupmemb = -1,
- do_icmp_router = -1, do_icmp_neighbor = -1, do_icmp_mldv2 = -1, do_icmp_types = -1, do_ect = -1;
-
- static uint32_t hash_Ip6InReceives = -1;
-
- static uint32_t hash_Ip6InHdrErrors = -1;
- static uint32_t hash_Ip6InTooBigErrors = -1;
- static uint32_t hash_Ip6InNoRoutes = -1;
- static uint32_t hash_Ip6InAddrErrors = -1;
- static uint32_t hash_Ip6InUnknownProtos = -1;
- static uint32_t hash_Ip6InTruncatedPkts = -1;
- static uint32_t hash_Ip6InDiscards = -1;
- static uint32_t hash_Ip6InDelivers = -1;
-
- static uint32_t hash_Ip6OutForwDatagrams = -1;
- static uint32_t hash_Ip6OutRequests = -1;
- static uint32_t hash_Ip6OutDiscards = -1;
- static uint32_t hash_Ip6OutNoRoutes = -1;
-
- static uint32_t hash_Ip6ReasmTimeout = -1;
- static uint32_t hash_Ip6ReasmReqds = -1;
- static uint32_t hash_Ip6ReasmOKs = -1;
- static uint32_t hash_Ip6ReasmFails = -1;
-
- static uint32_t hash_Ip6FragOKs = -1;
- static uint32_t hash_Ip6FragFails = -1;
- static uint32_t hash_Ip6FragCreates = -1;
-
- static uint32_t hash_Ip6InMcastPkts = -1;
- static uint32_t hash_Ip6OutMcastPkts = -1;
-
- static uint32_t hash_Ip6InOctets = -1;
- static uint32_t hash_Ip6OutOctets = -1;
-
- static uint32_t hash_Ip6InMcastOctets = -1;
- static uint32_t hash_Ip6OutMcastOctets = -1;
- static uint32_t hash_Ip6InBcastOctets = -1;
- static uint32_t hash_Ip6OutBcastOctets = -1;
-
- static uint32_t hash_Ip6InNoECTPkts = -1;
- static uint32_t hash_Ip6InECT1Pkts = -1;
- static uint32_t hash_Ip6InECT0Pkts = -1;
- static uint32_t hash_Ip6InCEPkts = -1;
-
- static uint32_t hash_Icmp6InMsgs = -1;
- static uint32_t hash_Icmp6InErrors = -1;
- static uint32_t hash_Icmp6OutMsgs = -1;
- static uint32_t hash_Icmp6OutErrors = -1;
- static uint32_t hash_Icmp6InCsumErrors = -1;
- static uint32_t hash_Icmp6InDestUnreachs = -1;
- static uint32_t hash_Icmp6InPktTooBigs = -1;
- static uint32_t hash_Icmp6InTimeExcds = -1;
- static uint32_t hash_Icmp6InParmProblems = -1;
- static uint32_t hash_Icmp6InEchos = -1;
- static uint32_t hash_Icmp6InEchoReplies = -1;
- static uint32_t hash_Icmp6InGroupMembQueries = -1;
- static uint32_t hash_Icmp6InGroupMembResponses = -1;
- static uint32_t hash_Icmp6InGroupMembReductions = -1;
- static uint32_t hash_Icmp6InRouterSolicits = -1;
- static uint32_t hash_Icmp6InRouterAdvertisements = -1;
- static uint32_t hash_Icmp6InNeighborSolicits = -1;
- static uint32_t hash_Icmp6InNeighborAdvertisements = -1;
- static uint32_t hash_Icmp6InRedirects = -1;
- static uint32_t hash_Icmp6InMLDv2Reports = -1;
- static uint32_t hash_Icmp6OutDestUnreachs = -1;
- static uint32_t hash_Icmp6OutPktTooBigs = -1;
- static uint32_t hash_Icmp6OutTimeExcds = -1;
- static uint32_t hash_Icmp6OutParmProblems = -1;
- static uint32_t hash_Icmp6OutEchos = -1;
- static uint32_t hash_Icmp6OutEchoReplies = -1;
- static uint32_t hash_Icmp6OutGroupMembQueries = -1;
- static uint32_t hash_Icmp6OutGroupMembResponses = -1;
- static uint32_t hash_Icmp6OutGroupMembReductions = -1;
- static uint32_t hash_Icmp6OutRouterSolicits = -1;
- static uint32_t hash_Icmp6OutRouterAdvertisements = -1;
- static uint32_t hash_Icmp6OutNeighborSolicits = -1;
- static uint32_t hash_Icmp6OutNeighborAdvertisements = -1;
- static uint32_t hash_Icmp6OutRedirects = -1;
- static uint32_t hash_Icmp6OutMLDv2Reports = -1;
- static uint32_t hash_Icmp6InType1 = -1;
- static uint32_t hash_Icmp6InType128 = -1;
- static uint32_t hash_Icmp6InType129 = -1;
- static uint32_t hash_Icmp6InType136 = -1;
- static uint32_t hash_Icmp6OutType1 = -1;
- static uint32_t hash_Icmp6OutType128 = -1;
- static uint32_t hash_Icmp6OutType129 = -1;
- static uint32_t hash_Icmp6OutType133 = -1;
- static uint32_t hash_Icmp6OutType135 = -1;
- static uint32_t hash_Icmp6OutType143 = -1;
-
- static uint32_t hash_Udp6InDatagrams = -1;
- static uint32_t hash_Udp6NoPorts = -1;
- static uint32_t hash_Udp6InErrors = -1;
- static uint32_t hash_Udp6OutDatagrams = -1;
- static uint32_t hash_Udp6RcvbufErrors = -1;
- static uint32_t hash_Udp6SndbufErrors = -1;
- static uint32_t hash_Udp6InCsumErrors = -1;
- static uint32_t hash_Udp6IgnoredMulti = -1;
-
- static uint32_t hash_UdpLite6InDatagrams = -1;
- static uint32_t hash_UdpLite6NoPorts = -1;
- static uint32_t hash_UdpLite6InErrors = -1;
- static uint32_t hash_UdpLite6OutDatagrams = -1;
- static uint32_t hash_UdpLite6RcvbufErrors = -1;
- static uint32_t hash_UdpLite6SndbufErrors = -1;
- static uint32_t hash_UdpLite6InCsumErrors = -1;
-
- if(gen_hashes != 1) {
- gen_hashes = 1;
- hash_Ip6InReceives = simple_hash("Ip6InReceives");
- hash_Ip6InHdrErrors = simple_hash("Ip6InHdrErrors");
- hash_Ip6InTooBigErrors = simple_hash("Ip6InTooBigErrors");
- hash_Ip6InNoRoutes = simple_hash("Ip6InNoRoutes");
- hash_Ip6InAddrErrors = simple_hash("Ip6InAddrErrors");
- hash_Ip6InUnknownProtos = simple_hash("Ip6InUnknownProtos");
- hash_Ip6InTruncatedPkts = simple_hash("Ip6InTruncatedPkts");
- hash_Ip6InDiscards = simple_hash("Ip6InDiscards");
- hash_Ip6InDelivers = simple_hash("Ip6InDelivers");
- hash_Ip6OutForwDatagrams = simple_hash("Ip6OutForwDatagrams");
- hash_Ip6OutRequests = simple_hash("Ip6OutRequests");
- hash_Ip6OutDiscards = simple_hash("Ip6OutDiscards");
- hash_Ip6OutNoRoutes = simple_hash("Ip6OutNoRoutes");
- hash_Ip6ReasmTimeout = simple_hash("Ip6ReasmTimeout");
- hash_Ip6ReasmReqds = simple_hash("Ip6ReasmReqds");
- hash_Ip6ReasmOKs = simple_hash("Ip6ReasmOKs");
- hash_Ip6ReasmFails = simple_hash("Ip6ReasmFails");
- hash_Ip6FragOKs = simple_hash("Ip6FragOKs");
- hash_Ip6FragFails = simple_hash("Ip6FragFails");
- hash_Ip6FragCreates = simple_hash("Ip6FragCreates");
- hash_Ip6InMcastPkts = simple_hash("Ip6InMcastPkts");
- hash_Ip6OutMcastPkts = simple_hash("Ip6OutMcastPkts");
- hash_Ip6InOctets = simple_hash("Ip6InOctets");
- hash_Ip6OutOctets = simple_hash("Ip6OutOctets");
- hash_Ip6InMcastOctets = simple_hash("Ip6InMcastOctets");
- hash_Ip6OutMcastOctets = simple_hash("Ip6OutMcastOctets");
- hash_Ip6InBcastOctets = simple_hash("Ip6InBcastOctets");
- hash_Ip6OutBcastOctets = simple_hash("Ip6OutBcastOctets");
- hash_Ip6InNoECTPkts = simple_hash("Ip6InNoECTPkts");
- hash_Ip6InECT1Pkts = simple_hash("Ip6InECT1Pkts");
- hash_Ip6InECT0Pkts = simple_hash("Ip6InECT0Pkts");
- hash_Ip6InCEPkts = simple_hash("Ip6InCEPkts");
- hash_Icmp6InMsgs = simple_hash("Icmp6InMsgs");
- hash_Icmp6InErrors = simple_hash("Icmp6InErrors");
- hash_Icmp6OutMsgs = simple_hash("Icmp6OutMsgs");
- hash_Icmp6OutErrors = simple_hash("Icmp6OutErrors");
- hash_Icmp6InCsumErrors = simple_hash("Icmp6InCsumErrors");
- hash_Icmp6InDestUnreachs = simple_hash("Icmp6InDestUnreachs");
- hash_Icmp6InPktTooBigs = simple_hash("Icmp6InPktTooBigs");
- hash_Icmp6InTimeExcds = simple_hash("Icmp6InTimeExcds");
- hash_Icmp6InParmProblems = simple_hash("Icmp6InParmProblems");
- hash_Icmp6InEchos = simple_hash("Icmp6InEchos");
- hash_Icmp6InEchoReplies = simple_hash("Icmp6InEchoReplies");
- hash_Icmp6InGroupMembQueries = simple_hash("Icmp6InGroupMembQueries");
- hash_Icmp6InGroupMembResponses = simple_hash("Icmp6InGroupMembResponses");
- hash_Icmp6InGroupMembReductions = simple_hash("Icmp6InGroupMembReductions");
- hash_Icmp6InRouterSolicits = simple_hash("Icmp6InRouterSolicits");
- hash_Icmp6InRouterAdvertisements = simple_hash("Icmp6InRouterAdvertisements");
- hash_Icmp6InNeighborSolicits = simple_hash("Icmp6InNeighborSolicits");
- hash_Icmp6InNeighborAdvertisements = simple_hash("Icmp6InNeighborAdvertisements");
- hash_Icmp6InRedirects = simple_hash("Icmp6InRedirects");
- hash_Icmp6InMLDv2Reports = simple_hash("Icmp6InMLDv2Reports");
- hash_Icmp6OutDestUnreachs = simple_hash("Icmp6OutDestUnreachs");
- hash_Icmp6OutPktTooBigs = simple_hash("Icmp6OutPktTooBigs");
- hash_Icmp6OutTimeExcds = simple_hash("Icmp6OutTimeExcds");
- hash_Icmp6OutParmProblems = simple_hash("Icmp6OutParmProblems");
- hash_Icmp6OutEchos = simple_hash("Icmp6OutEchos");
- hash_Icmp6OutEchoReplies = simple_hash("Icmp6OutEchoReplies");
- hash_Icmp6OutGroupMembQueries = simple_hash("Icmp6OutGroupMembQueries");
- hash_Icmp6OutGroupMembResponses = simple_hash("Icmp6OutGroupMembResponses");
- hash_Icmp6OutGroupMembReductions = simple_hash("Icmp6OutGroupMembReductions");
- hash_Icmp6OutRouterSolicits = simple_hash("Icmp6OutRouterSolicits");
- hash_Icmp6OutRouterAdvertisements = simple_hash("Icmp6OutRouterAdvertisements");
- hash_Icmp6OutNeighborSolicits = simple_hash("Icmp6OutNeighborSolicits");
- hash_Icmp6OutNeighborAdvertisements = simple_hash("Icmp6OutNeighborAdvertisements");
- hash_Icmp6OutRedirects = simple_hash("Icmp6OutRedirects");
- hash_Icmp6OutMLDv2Reports = simple_hash("Icmp6OutMLDv2Reports");
- hash_Icmp6InType1 = simple_hash("Icmp6InType1");
- hash_Icmp6InType128 = simple_hash("Icmp6InType128");
- hash_Icmp6InType129 = simple_hash("Icmp6InType129");
- hash_Icmp6InType136 = simple_hash("Icmp6InType136");
- hash_Icmp6OutType1 = simple_hash("Icmp6OutType1");
- hash_Icmp6OutType128 = simple_hash("Icmp6OutType128");
- hash_Icmp6OutType129 = simple_hash("Icmp6OutType129");
- hash_Icmp6OutType133 = simple_hash("Icmp6OutType133");
- hash_Icmp6OutType135 = simple_hash("Icmp6OutType135");
- hash_Icmp6OutType143 = simple_hash("Icmp6OutType143");
- hash_Udp6InDatagrams = simple_hash("Udp6InDatagrams");
- hash_Udp6NoPorts = simple_hash("Udp6NoPorts");
- hash_Udp6InErrors = simple_hash("Udp6InErrors");
- hash_Udp6OutDatagrams = simple_hash("Udp6OutDatagrams");
- hash_Udp6RcvbufErrors = simple_hash("Udp6RcvbufErrors");
- hash_Udp6SndbufErrors = simple_hash("Udp6SndbufErrors");
- hash_Udp6InCsumErrors = simple_hash("Udp6InCsumErrors");
- hash_Udp6IgnoredMulti = simple_hash("Udp6IgnoredMulti");
- hash_UdpLite6InDatagrams = simple_hash("UdpLite6InDatagrams");
- hash_UdpLite6NoPorts = simple_hash("UdpLite6NoPorts");
- hash_UdpLite6InErrors = simple_hash("UdpLite6InErrors");
- hash_UdpLite6OutDatagrams = simple_hash("UdpLite6OutDatagrams");
- hash_UdpLite6RcvbufErrors = simple_hash("UdpLite6RcvbufErrors");
- hash_UdpLite6SndbufErrors = simple_hash("UdpLite6SndbufErrors");
- hash_UdpLite6InCsumErrors = simple_hash("UdpLite6InCsumErrors");
- }
-
- if(do_ip_packets == -1) do_ip_packets = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 packets", CONFIG_ONDEMAND_ONDEMAND);
- if(do_ip_fragsout == -1) do_ip_fragsout = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 fragments sent", CONFIG_ONDEMAND_ONDEMAND);
- if(do_ip_fragsin == -1) do_ip_fragsin = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 fragments assembly", CONFIG_ONDEMAND_ONDEMAND);
- if(do_ip_errors == -1) do_ip_errors = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 errors", CONFIG_ONDEMAND_ONDEMAND);
- if(do_udp_packets == -1) do_udp_packets = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 UDP packets", CONFIG_ONDEMAND_ONDEMAND);
- if(do_udp_errors == -1) do_udp_errors = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 UDP errors", CONFIG_ONDEMAND_ONDEMAND);
- if(do_udplite_packets == -1) do_udplite_packets = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 UDPlite packets", CONFIG_ONDEMAND_ONDEMAND);
- if(do_udplite_errors == -1) do_udplite_errors = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 UDPlite errors", CONFIG_ONDEMAND_ONDEMAND);
- if(do_bandwidth == -1) do_bandwidth = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "bandwidth", CONFIG_ONDEMAND_ONDEMAND);
- if(do_mcast == -1) do_mcast = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "multicast bandwidth", CONFIG_ONDEMAND_ONDEMAND);
- if(do_bcast == -1) do_bcast = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "broadcast bandwidth", CONFIG_ONDEMAND_ONDEMAND);
- if(do_mcast_p == -1) do_mcast_p = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "multicast packets", CONFIG_ONDEMAND_ONDEMAND);
- if(do_icmp == -1) do_icmp = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp", CONFIG_ONDEMAND_ONDEMAND);
- if(do_icmp_redir == -1) do_icmp_redir = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp redirects", CONFIG_ONDEMAND_ONDEMAND);
- if(do_icmp_errors == -1) do_icmp_errors = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp errors", CONFIG_ONDEMAND_ONDEMAND);
- if(do_icmp_echos == -1) do_icmp_echos = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp echos", CONFIG_ONDEMAND_ONDEMAND);
- if(do_icmp_groupmemb == -1) do_icmp_groupmemb = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp group membership", CONFIG_ONDEMAND_ONDEMAND);
- if(do_icmp_router == -1) do_icmp_router = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp router", CONFIG_ONDEMAND_ONDEMAND);
- if(do_icmp_neighbor == -1) do_icmp_neighbor = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp neighbor", CONFIG_ONDEMAND_ONDEMAND);
- if(do_icmp_mldv2 == -1) do_icmp_mldv2 = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp mldv2", CONFIG_ONDEMAND_ONDEMAND);
- if(do_icmp_types == -1) do_icmp_types = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp types", CONFIG_ONDEMAND_ONDEMAND);
- if(do_ect == -1) do_ect = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ect", CONFIG_ONDEMAND_ONDEMAND);
-
- if(dt) {};
-
- if(!ff) {
- char filename[FILENAME_MAX + 1];
- snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/net/snmp6");
- ff = procfile_open(config_get("plugin:proc:/proc/net/snmp6", "filename to monitor", filename), " \t:", PROCFILE_FLAG_DEFAULT);
- }
- if(!ff) return 1;
-
- ff = procfile_readall(ff);
- if(!ff) return 0; // we return 0, so that we will retry to open it next time
-
- uint32_t lines = procfile_lines(ff), l;
- uint32_t words;
-
- unsigned long long Ip6InReceives = 0ULL;
- unsigned long long Ip6InHdrErrors = 0ULL;
- unsigned long long Ip6InTooBigErrors = 0ULL;
- unsigned long long Ip6InNoRoutes = 0ULL;
- unsigned long long Ip6InAddrErrors = 0ULL;
- unsigned long long Ip6InUnknownProtos = 0ULL;
- unsigned long long Ip6InTruncatedPkts = 0ULL;
- unsigned long long Ip6InDiscards = 0ULL;
- unsigned long long Ip6InDelivers = 0ULL;
- unsigned long long Ip6OutForwDatagrams = 0ULL;
- unsigned long long Ip6OutRequests = 0ULL;
- unsigned long long Ip6OutDiscards = 0ULL;
- unsigned long long Ip6OutNoRoutes = 0ULL;
- unsigned long long Ip6ReasmTimeout = 0ULL;
- unsigned long long Ip6ReasmReqds = 0ULL;
- unsigned long long Ip6ReasmOKs = 0ULL;
- unsigned long long Ip6ReasmFails = 0ULL;
- unsigned long long Ip6FragOKs = 0ULL;
- unsigned long long Ip6FragFails = 0ULL;
- unsigned long long Ip6FragCreates = 0ULL;
- unsigned long long Ip6InMcastPkts = 0ULL;
- unsigned long long Ip6OutMcastPkts = 0ULL;
- unsigned long long Ip6InOctets = 0ULL;
- unsigned long long Ip6OutOctets = 0ULL;
- unsigned long long Ip6InMcastOctets = 0ULL;
- unsigned long long Ip6OutMcastOctets = 0ULL;
- unsigned long long Ip6InBcastOctets = 0ULL;
- unsigned long long Ip6OutBcastOctets = 0ULL;
- unsigned long long Ip6InNoECTPkts = 0ULL;
- unsigned long long Ip6InECT1Pkts = 0ULL;
- unsigned long long Ip6InECT0Pkts = 0ULL;
- unsigned long long Ip6InCEPkts = 0ULL;
- unsigned long long Icmp6InMsgs = 0ULL;
- unsigned long long Icmp6InErrors = 0ULL;
- unsigned long long Icmp6OutMsgs = 0ULL;
- unsigned long long Icmp6OutErrors = 0ULL;
- unsigned long long Icmp6InCsumErrors = 0ULL;
- unsigned long long Icmp6InDestUnreachs = 0ULL;
- unsigned long long Icmp6InPktTooBigs = 0ULL;
- unsigned long long Icmp6InTimeExcds = 0ULL;
- unsigned long long Icmp6InParmProblems = 0ULL;
- unsigned long long Icmp6InEchos = 0ULL;
- unsigned long long Icmp6InEchoReplies = 0ULL;
- unsigned long long Icmp6InGroupMembQueries = 0ULL;
- unsigned long long Icmp6InGroupMembResponses = 0ULL;
- unsigned long long Icmp6InGroupMembReductions = 0ULL;
- unsigned long long Icmp6InRouterSolicits = 0ULL;
- unsigned long long Icmp6InRouterAdvertisements = 0ULL;
- unsigned long long Icmp6InNeighborSolicits = 0ULL;
- unsigned long long Icmp6InNeighborAdvertisements = 0ULL;
- unsigned long long Icmp6InRedirects = 0ULL;
- unsigned long long Icmp6InMLDv2Reports = 0ULL;
- unsigned long long Icmp6OutDestUnreachs = 0ULL;
- unsigned long long Icmp6OutPktTooBigs = 0ULL;
- unsigned long long Icmp6OutTimeExcds = 0ULL;
- unsigned long long Icmp6OutParmProblems = 0ULL;
- unsigned long long Icmp6OutEchos = 0ULL;
- unsigned long long Icmp6OutEchoReplies = 0ULL;
- unsigned long long Icmp6OutGroupMembQueries = 0ULL;
- unsigned long long Icmp6OutGroupMembResponses = 0ULL;
- unsigned long long Icmp6OutGroupMembReductions = 0ULL;
- unsigned long long Icmp6OutRouterSolicits = 0ULL;
- unsigned long long Icmp6OutRouterAdvertisements = 0ULL;
- unsigned long long Icmp6OutNeighborSolicits = 0ULL;
- unsigned long long Icmp6OutNeighborAdvertisements = 0ULL;
- unsigned long long Icmp6OutRedirects = 0ULL;
- unsigned long long Icmp6OutMLDv2Reports = 0ULL;
- unsigned long long Icmp6InType1 = 0ULL;
- unsigned long long Icmp6InType128 = 0ULL;
- unsigned long long Icmp6InType129 = 0ULL;
- unsigned long long Icmp6InType136 = 0ULL;
- unsigned long long Icmp6OutType1 = 0ULL;
- unsigned long long Icmp6OutType128 = 0ULL;
- unsigned long long Icmp6OutType129 = 0ULL;
- unsigned long long Icmp6OutType133 = 0ULL;
- unsigned long long Icmp6OutType135 = 0ULL;
- unsigned long long Icmp6OutType143 = 0ULL;
- unsigned long long Udp6InDatagrams = 0ULL;
- unsigned long long Udp6NoPorts = 0ULL;
- unsigned long long Udp6InErrors = 0ULL;
- unsigned long long Udp6OutDatagrams = 0ULL;
- unsigned long long Udp6RcvbufErrors = 0ULL;
- unsigned long long Udp6SndbufErrors = 0ULL;
- unsigned long long Udp6InCsumErrors = 0ULL;
- unsigned long long Udp6IgnoredMulti = 0ULL;
- unsigned long long UdpLite6InDatagrams = 0ULL;
- unsigned long long UdpLite6NoPorts = 0ULL;
- unsigned long long UdpLite6InErrors = 0ULL;
- unsigned long long UdpLite6OutDatagrams = 0ULL;
- unsigned long long UdpLite6RcvbufErrors = 0ULL;
- unsigned long long UdpLite6SndbufErrors = 0ULL;
- unsigned long long UdpLite6InCsumErrors = 0ULL;
-
- for(l = 0; l < lines ;l++) {
- words = procfile_linewords(ff, l);
- if(words < 2) {
- if(words) error("Cannot read /proc/net/snmp6 line %d. Expected 2 params, read %d.", l, words);
- continue;
- }
-
- char *name = procfile_lineword(ff, l, 0);
- char * value = procfile_lineword(ff, l, 1);
- if(!name || !*name || !value || !*value) continue;
-
- uint32_t hash = simple_hash(name);
-
- if(0) ;
- else if(hash == hash_Ip6InReceives && strcmp(name, "Ip6InReceives") == 0) Ip6InReceives = strtoull(value, NULL, 10);
- else if(hash == hash_Ip6InHdrErrors && strcmp(name, "Ip6InHdrErrors") == 0) Ip6InHdrErrors = strtoull(value, NULL, 10);
- else if(hash == hash_Ip6InTooBigErrors && strcmp(name, "Ip6InTooBigErrors") == 0) Ip6InTooBigErrors = strtoull(value, NULL, 10);
- else if(hash == hash_Ip6InNoRoutes && strcmp(name, "Ip6InNoRoutes") == 0) Ip6InNoRoutes = strtoull(value, NULL, 10);
- else if(hash == hash_Ip6InAddrErrors && strcmp(name, "Ip6InAddrErrors") == 0) Ip6InAddrErrors = strtoull(value, NULL, 10);
- else if(hash == hash_Ip6InUnknownProtos && strcmp(name, "Ip6InUnknownProtos") == 0) Ip6InUnknownProtos = strtoull(value, NULL, 10);
- else if(hash == hash_Ip6InTruncatedPkts && strcmp(name, "Ip6InTruncatedPkts") == 0) Ip6InTruncatedPkts = strtoull(value, NULL, 10);
- else if(hash == hash_Ip6InDiscards && strcmp(name, "Ip6InDiscards") == 0) Ip6InDiscards = strtoull(value, NULL, 10);
- else if(hash == hash_Ip6InDelivers && strcmp(name, "Ip6InDelivers") == 0) Ip6InDelivers = strtoull(value, NULL, 10);
- else if(hash == hash_Ip6OutForwDatagrams && strcmp(name, "Ip6OutForwDatagrams") == 0) Ip6OutForwDatagrams = strtoull(value, NULL, 10);
- else if(hash == hash_Ip6OutRequests && strcmp(name, "Ip6OutRequests") == 0) Ip6OutRequests = strtoull(value, NULL, 10);
- else if(hash == hash_Ip6OutDiscards && strcmp(name, "Ip6OutDiscards") == 0) Ip6OutDiscards = strtoull(value, NULL, 10);
- else if(hash == hash_Ip6OutNoRoutes && strcmp(name, "Ip6OutNoRoutes") == 0) Ip6OutNoRoutes = strtoull(value, NULL, 10);
- else if(hash == hash_Ip6ReasmTimeout && strcmp(name, "Ip6ReasmTimeout") == 0) Ip6ReasmTimeout = strtoull(value, NULL, 10);
- else if(hash == hash_Ip6ReasmReqds && strcmp(name, "Ip6ReasmReqds") == 0) Ip6ReasmReqds = strtoull(value, NULL, 10);
- else if(hash == hash_Ip6ReasmOKs && strcmp(name, "Ip6ReasmOKs") == 0) Ip6ReasmOKs = strtoull(value, NULL, 10);
- else if(hash == hash_Ip6ReasmFails && strcmp(name, "Ip6ReasmFails") == 0) Ip6ReasmFails = strtoull(value, NULL, 10);
- else if(hash == hash_Ip6FragOKs && strcmp(name, "Ip6FragOKs") == 0) Ip6FragOKs = strtoull(value, NULL, 10);
- else if(hash == hash_Ip6FragFails && strcmp(name, "Ip6FragFails") == 0) Ip6FragFails = strtoull(value, NULL, 10);
- else if(hash == hash_Ip6FragCreates && strcmp(name, "Ip6FragCreates") == 0) Ip6FragCreates = strtoull(value, NULL, 10);
- else if(hash == hash_Ip6InMcastPkts && strcmp(name, "Ip6InMcastPkts") == 0) Ip6InMcastPkts = strtoull(value, NULL, 10);
- else if(hash == hash_Ip6OutMcastPkts && strcmp(name, "Ip6OutMcastPkts") == 0) Ip6OutMcastPkts = strtoull(value, NULL, 10);
- else if(hash == hash_Ip6InOctets && strcmp(name, "Ip6InOctets") == 0) Ip6InOctets = strtoull(value, NULL, 10);
- else if(hash == hash_Ip6OutOctets && strcmp(name, "Ip6OutOctets") == 0) Ip6OutOctets = strtoull(value, NULL, 10);
- else if(hash == hash_Ip6InMcastOctets && strcmp(name, "Ip6InMcastOctets") == 0) Ip6InMcastOctets = strtoull(value, NULL, 10);
- else if(hash == hash_Ip6OutMcastOctets && strcmp(name, "Ip6OutMcastOctets") == 0) Ip6OutMcastOctets = strtoull(value, NULL, 10);
- else if(hash == hash_Ip6InBcastOctets && strcmp(name, "Ip6InBcastOctets") == 0) Ip6InBcastOctets = strtoull(value, NULL, 10);
- else if(hash == hash_Ip6OutBcastOctets && strcmp(name, "Ip6OutBcastOctets") == 0) Ip6OutBcastOctets = strtoull(value, NULL, 10);
- else if(hash == hash_Ip6InNoECTPkts && strcmp(name, "Ip6InNoECTPkts") == 0) Ip6InNoECTPkts = strtoull(value, NULL, 10);
- else if(hash == hash_Ip6InECT1Pkts && strcmp(name, "Ip6InECT1Pkts") == 0) Ip6InECT1Pkts = strtoull(value, NULL, 10);
- else if(hash == hash_Ip6InECT0Pkts && strcmp(name, "Ip6InECT0Pkts") == 0) Ip6InECT0Pkts = strtoull(value, NULL, 10);
- else if(hash == hash_Ip6InCEPkts && strcmp(name, "Ip6InCEPkts") == 0) Ip6InCEPkts = strtoull(value, NULL, 10);
- else if(hash == hash_Icmp6InMsgs && strcmp(name, "Icmp6InMsgs") == 0) Icmp6InMsgs = strtoull(value, NULL, 10);
- else if(hash == hash_Icmp6InErrors && strcmp(name, "Icmp6InErrors") == 0) Icmp6InErrors = strtoull(value, NULL, 10);
- else if(hash == hash_Icmp6OutMsgs && strcmp(name, "Icmp6OutMsgs") == 0) Icmp6OutMsgs = strtoull(value, NULL, 10);
- else if(hash == hash_Icmp6OutErrors && strcmp(name, "Icmp6OutErrors") == 0) Icmp6OutErrors = strtoull(value, NULL, 10);
- else if(hash == hash_Icmp6InCsumErrors && strcmp(name, "Icmp6InCsumErrors") == 0) Icmp6InCsumErrors = strtoull(value, NULL, 10);
- else if(hash == hash_Icmp6InDestUnreachs && strcmp(name, "Icmp6InDestUnreachs") == 0) Icmp6InDestUnreachs = strtoull(value, NULL, 10);
- else if(hash == hash_Icmp6InPktTooBigs && strcmp(name, "Icmp6InPktTooBigs") == 0) Icmp6InPktTooBigs = strtoull(value, NULL, 10);
- else if(hash == hash_Icmp6InTimeExcds && strcmp(name, "Icmp6InTimeExcds") == 0) Icmp6InTimeExcds = strtoull(value, NULL, 10);
- else if(hash == hash_Icmp6InParmProblems && strcmp(name, "Icmp6InParmProblems") == 0) Icmp6InParmProblems = strtoull(value, NULL, 10);
- else if(hash == hash_Icmp6InEchos && strcmp(name, "Icmp6InEchos") == 0) Icmp6InEchos = strtoull(value, NULL, 10);
- else if(hash == hash_Icmp6InEchoReplies && strcmp(name, "Icmp6InEchoReplies") == 0) Icmp6InEchoReplies = strtoull(value, NULL, 10);
- else if(hash == hash_Icmp6InGroupMembQueries && strcmp(name, "Icmp6InGroupMembQueries") == 0) Icmp6InGroupMembQueries = strtoull(value, NULL, 10);
- else if(hash == hash_Icmp6InGroupMembResponses && strcmp(name, "Icmp6InGroupMembResponses") == 0) Icmp6InGroupMembResponses = strtoull(value, NULL, 10);
- else if(hash == hash_Icmp6InGroupMembReductions && strcmp(name, "Icmp6InGroupMembReductions") == 0) Icmp6InGroupMembReductions = strtoull(value, NULL, 10);
- else if(hash == hash_Icmp6InRouterSolicits && strcmp(name, "Icmp6InRouterSolicits") == 0) Icmp6InRouterSolicits = strtoull(value, NULL, 10);
- else if(hash == hash_Icmp6InRouterAdvertisements && strcmp(name, "Icmp6InRouterAdvertisements") == 0) Icmp6InRouterAdvertisements = strtoull(value, NULL, 10);
- else if(hash == hash_Icmp6InNeighborSolicits && strcmp(name, "Icmp6InNeighborSolicits") == 0) Icmp6InNeighborSolicits = strtoull(value, NULL, 10);
- else if(hash == hash_Icmp6InNeighborAdvertisements && strcmp(name, "Icmp6InNeighborAdvertisements") == 0) Icmp6InNeighborAdvertisements = strtoull(value, NULL, 10);
- else if(hash == hash_Icmp6InRedirects && strcmp(name, "Icmp6InRedirects") == 0) Icmp6InRedirects = strtoull(value, NULL, 10);
- else if(hash == hash_Icmp6InMLDv2Reports && strcmp(name, "Icmp6InMLDv2Reports") == 0) Icmp6InMLDv2Reports = strtoull(value, NULL, 10);
- else if(hash == hash_Icmp6OutDestUnreachs && strcmp(name, "Icmp6OutDestUnreachs") == 0) Icmp6OutDestUnreachs = strtoull(value, NULL, 10);
- else if(hash == hash_Icmp6OutPktTooBigs && strcmp(name, "Icmp6OutPktTooBigs") == 0) Icmp6OutPktTooBigs = strtoull(value, NULL, 10);
- else if(hash == hash_Icmp6OutTimeExcds && strcmp(name, "Icmp6OutTimeExcds") == 0) Icmp6OutTimeExcds = strtoull(value, NULL, 10);
- else if(hash == hash_Icmp6OutParmProblems && strcmp(name, "Icmp6OutParmProblems") == 0) Icmp6OutParmProblems = strtoull(value, NULL, 10);
- else if(hash == hash_Icmp6OutEchos && strcmp(name, "Icmp6OutEchos") == 0) Icmp6OutEchos = strtoull(value, NULL, 10);
- else if(hash == hash_Icmp6OutEchoReplies && strcmp(name, "Icmp6OutEchoReplies") == 0) Icmp6OutEchoReplies = strtoull(value, NULL, 10);
- else if(hash == hash_Icmp6OutGroupMembQueries && strcmp(name, "Icmp6OutGroupMembQueries") == 0) Icmp6OutGroupMembQueries = strtoull(value, NULL, 10);
- else if(hash == hash_Icmp6OutGroupMembResponses && strcmp(name, "Icmp6OutGroupMembResponses") == 0) Icmp6OutGroupMembResponses = strtoull(value, NULL, 10);
- else if(hash == hash_Icmp6OutGroupMembReductions && strcmp(name, "Icmp6OutGroupMembReductions") == 0) Icmp6OutGroupMembReductions = strtoull(value, NULL, 10);
- else if(hash == hash_Icmp6OutRouterSolicits && strcmp(name, "Icmp6OutRouterSolicits") == 0) Icmp6OutRouterSolicits = strtoull(value, NULL, 10);
- else if(hash == hash_Icmp6OutRouterAdvertisements && strcmp(name, "Icmp6OutRouterAdvertisements") == 0) Icmp6OutRouterAdvertisements = strtoull(value, NULL, 10);
- else if(hash == hash_Icmp6OutNeighborSolicits && strcmp(name, "Icmp6OutNeighborSolicits") == 0) Icmp6OutNeighborSolicits = strtoull(value, NULL, 10);
- else if(hash == hash_Icmp6OutNeighborAdvertisements && strcmp(name, "Icmp6OutNeighborAdvertisements") == 0) Icmp6OutNeighborAdvertisements = strtoull(value, NULL, 10);
- else if(hash == hash_Icmp6OutRedirects && strcmp(name, "Icmp6OutRedirects") == 0) Icmp6OutRedirects = strtoull(value, NULL, 10);
- else if(hash == hash_Icmp6OutMLDv2Reports && strcmp(name, "Icmp6OutMLDv2Reports") == 0) Icmp6OutMLDv2Reports = strtoull(value, NULL, 10);
- else if(hash == hash_Icmp6InType1 && strcmp(name, "Icmp6InType1") == 0) Icmp6InType1 = strtoull(value, NULL, 10);
- else if(hash == hash_Icmp6InType128 && strcmp(name, "Icmp6InType128") == 0) Icmp6InType128 = strtoull(value, NULL, 10);
- else if(hash == hash_Icmp6InType129 && strcmp(name, "Icmp6InType129") == 0) Icmp6InType129 = strtoull(value, NULL, 10);
- else if(hash == hash_Icmp6InType136 && strcmp(name, "Icmp6InType136") == 0) Icmp6InType136 = strtoull(value, NULL, 10);
- else if(hash == hash_Icmp6OutType1 && strcmp(name, "Icmp6OutType1") == 0) Icmp6OutType1 = strtoull(value, NULL, 10);
- else if(hash == hash_Icmp6OutType128 && strcmp(name, "Icmp6OutType128") == 0) Icmp6OutType128 = strtoull(value, NULL, 10);
- else if(hash == hash_Icmp6OutType129 && strcmp(name, "Icmp6OutType129") == 0) Icmp6OutType129 = strtoull(value, NULL, 10);
- else if(hash == hash_Icmp6OutType133 && strcmp(name, "Icmp6OutType133") == 0) Icmp6OutType133 = strtoull(value, NULL, 10);
- else if(hash == hash_Icmp6OutType135 && strcmp(name, "Icmp6OutType135") == 0) Icmp6OutType135 = strtoull(value, NULL, 10);
- else if(hash == hash_Icmp6OutType143 && strcmp(name, "Icmp6OutType143") == 0) Icmp6OutType143 = strtoull(value, NULL, 10);
- else if(hash == hash_Udp6InDatagrams && strcmp(name, "Udp6InDatagrams") == 0) Udp6InDatagrams = strtoull(value, NULL, 10);
- else if(hash == hash_Udp6NoPorts && strcmp(name, "Udp6NoPorts") == 0) Udp6NoPorts = strtoull(value, NULL, 10);
- else if(hash == hash_Udp6InErrors && strcmp(name, "Udp6InErrors") == 0) Udp6InErrors = strtoull(value, NULL, 10);
- else if(hash == hash_Udp6OutDatagrams && strcmp(name, "Udp6OutDatagrams") == 0) Udp6OutDatagrams = strtoull(value, NULL, 10);
- else if(hash == hash_Udp6RcvbufErrors && strcmp(name, "Udp6RcvbufErrors") == 0) Udp6RcvbufErrors = strtoull(value, NULL, 10);
- else if(hash == hash_Udp6SndbufErrors && strcmp(name, "Udp6SndbufErrors") == 0) Udp6SndbufErrors = strtoull(value, NULL, 10);
- else if(hash == hash_Udp6InCsumErrors && strcmp(name, "Udp6InCsumErrors") == 0) Udp6InCsumErrors = strtoull(value, NULL, 10);
- else if(hash == hash_Udp6IgnoredMulti && strcmp(name, "Udp6IgnoredMulti") == 0) Udp6IgnoredMulti = strtoull(value, NULL, 10);
- else if(hash == hash_UdpLite6InDatagrams && strcmp(name, "UdpLite6InDatagrams") == 0) UdpLite6InDatagrams = strtoull(value, NULL, 10);
- else if(hash == hash_UdpLite6NoPorts && strcmp(name, "UdpLite6NoPorts") == 0) UdpLite6NoPorts = strtoull(value, NULL, 10);
- else if(hash == hash_UdpLite6InErrors && strcmp(name, "UdpLite6InErrors") == 0) UdpLite6InErrors = strtoull(value, NULL, 10);
- else if(hash == hash_UdpLite6OutDatagrams && strcmp(name, "UdpLite6OutDatagrams") == 0) UdpLite6OutDatagrams = strtoull(value, NULL, 10);
- else if(hash == hash_UdpLite6RcvbufErrors && strcmp(name, "UdpLite6RcvbufErrors") == 0) UdpLite6RcvbufErrors = strtoull(value, NULL, 10);
- else if(hash == hash_UdpLite6SndbufErrors && strcmp(name, "UdpLite6SndbufErrors") == 0) UdpLite6SndbufErrors = strtoull(value, NULL, 10);
- else if(hash == hash_UdpLite6InCsumErrors && strcmp(name, "UdpLite6InCsumErrors") == 0) UdpLite6InCsumErrors = strtoull(value, NULL, 10);
- }
-
- RRDSET *st;
-
- // --------------------------------------------------------------------
-
- if(do_bandwidth == CONFIG_ONDEMAND_YES || (do_bandwidth == CONFIG_ONDEMAND_ONDEMAND && (Ip6InOctets || Ip6OutOctets))) {
- do_bandwidth = CONFIG_ONDEMAND_YES;
- st = rrdset_find("system.ipv6");
- if(!st) {
- st = rrdset_create("system", "ipv6", NULL, "network", NULL, "IPv6 Bandwidth", "kilobits/s", 500, update_every, RRDSET_TYPE_AREA);
-
- rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL);
- rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "sent", Ip6OutOctets);
- rrddim_set(st, "received", Ip6InOctets);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(do_ip_packets == CONFIG_ONDEMAND_YES || (do_ip_packets == CONFIG_ONDEMAND_ONDEMAND && (Ip6InReceives || Ip6OutRequests || Ip6InDelivers || Ip6OutForwDatagrams))) {
- do_ip_packets = CONFIG_ONDEMAND_YES;
- st = rrdset_find(RRD_TYPE_NET_SNMP6 ".packets");
- if(!st) {
- st = rrdset_create(RRD_TYPE_NET_SNMP6, "packets", NULL, "packets", NULL, "IPv6 Packets", "packets/s", 3000, update_every, RRDSET_TYPE_LINE);
-
- rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "forwarded", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "delivers", NULL, -1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "sent", Ip6OutRequests);
- rrddim_set(st, "received", Ip6InReceives);
- rrddim_set(st, "forwarded", Ip6InDelivers);
- rrddim_set(st, "delivers", Ip6OutForwDatagrams);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(do_ip_fragsout == CONFIG_ONDEMAND_YES || (do_ip_fragsout == CONFIG_ONDEMAND_ONDEMAND && (Ip6FragOKs || Ip6FragFails || Ip6FragCreates))) {
- do_ip_fragsout = CONFIG_ONDEMAND_YES;
- st = rrdset_find(RRD_TYPE_NET_SNMP6 ".fragsout");
- if(!st) {
- st = rrdset_create(RRD_TYPE_NET_SNMP6, "fragsout", NULL, "fragments", NULL, "IPv6 Fragments Sent", "packets/s", 3010, update_every, RRDSET_TYPE_LINE);
- st->isdetail = 1;
-
- rrddim_add(st, "ok", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "failed", NULL, -1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "all", NULL, 1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "ok", Ip6FragOKs);
- rrddim_set(st, "failed", Ip6FragFails);
- rrddim_set(st, "all", Ip6FragCreates);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(do_ip_fragsin == CONFIG_ONDEMAND_YES || (do_ip_fragsin == CONFIG_ONDEMAND_ONDEMAND
- && (
- Ip6ReasmOKs
- || Ip6ReasmFails
- || Ip6ReasmTimeout
- || Ip6ReasmReqds
- ))) {
- do_ip_fragsin = CONFIG_ONDEMAND_YES;
- st = rrdset_find(RRD_TYPE_NET_SNMP6 ".fragsin");
- if(!st) {
- st = rrdset_create(RRD_TYPE_NET_SNMP6, "fragsin", NULL, "fragments", NULL, "IPv6 Fragments Reassembly", "packets/s", 3011, update_every, RRDSET_TYPE_LINE);
- st->isdetail = 1;
-
- rrddim_add(st, "ok", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "failed", NULL, -1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "timeout", NULL, -1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "all", NULL, 1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "ok", Ip6ReasmOKs);
- rrddim_set(st, "failed", Ip6ReasmFails);
- rrddim_set(st, "timeout", Ip6ReasmTimeout);
- rrddim_set(st, "all", Ip6ReasmReqds);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(do_ip_errors == CONFIG_ONDEMAND_YES || (do_ip_errors == CONFIG_ONDEMAND_ONDEMAND
- && (
- Ip6InDiscards
- || Ip6OutDiscards
- || Ip6InHdrErrors
- || Ip6InAddrErrors
- || Ip6InUnknownProtos
- || Ip6InTooBigErrors
- || Ip6InTruncatedPkts
- || Ip6InNoRoutes
- ))) {
- do_ip_errors = CONFIG_ONDEMAND_YES;
- st = rrdset_find(RRD_TYPE_NET_SNMP6 ".errors");
- if(!st) {
- st = rrdset_create(RRD_TYPE_NET_SNMP6, "errors", NULL, "errors", NULL, "IPv6 Errors", "packets/s", 3002, update_every, RRDSET_TYPE_LINE);
- st->isdetail = 1;
-
- rrddim_add(st, "InDiscards", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "OutDiscards", NULL, -1, 1, RRDDIM_INCREMENTAL);
-
- rrddim_add(st, "InHdrErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "InAddrErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "InUnknownProtos", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "InTooBigErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "InTruncatedPkts", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "InNoRoutes", NULL, 1, 1, RRDDIM_INCREMENTAL);
-
- rrddim_add(st, "OutNoRoutes", NULL, -1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "InDiscards", Ip6InDiscards);
- rrddim_set(st, "OutDiscards", Ip6OutDiscards);
-
- rrddim_set(st, "InHdrErrors", Ip6InHdrErrors);
- rrddim_set(st, "InAddrErrors", Ip6InAddrErrors);
- rrddim_set(st, "InUnknownProtos", Ip6InUnknownProtos);
- rrddim_set(st, "InTooBigErrors", Ip6InTooBigErrors);
- rrddim_set(st, "InTruncatedPkts", Ip6InTruncatedPkts);
- rrddim_set(st, "InNoRoutes", Ip6InNoRoutes);
-
- rrddim_set(st, "OutNoRoutes", Ip6OutNoRoutes);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(do_udp_packets == CONFIG_ONDEMAND_YES || (do_udp_packets == CONFIG_ONDEMAND_ONDEMAND && (Udp6InDatagrams || Udp6OutDatagrams))) {
- do_udp_packets = CONFIG_ONDEMAND_YES;
- st = rrdset_find(RRD_TYPE_NET_SNMP6 ".udppackets");
- if(!st) {
- st = rrdset_create(RRD_TYPE_NET_SNMP6, "udppackets", NULL, "udp", NULL, "IPv6 UDP Packets", "packets/s", 3601, update_every, RRDSET_TYPE_LINE);
-
- rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "received", Udp6InDatagrams);
- rrddim_set(st, "sent", Udp6OutDatagrams);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(do_udp_errors == CONFIG_ONDEMAND_YES || (do_udp_errors == CONFIG_ONDEMAND_ONDEMAND
- && (
- Udp6InErrors
- || Udp6NoPorts
- || Udp6RcvbufErrors
- || Udp6SndbufErrors
- || Udp6InCsumErrors
- || Udp6IgnoredMulti
- ))) {
- do_udp_errors = CONFIG_ONDEMAND_YES;
- st = rrdset_find(RRD_TYPE_NET_SNMP6 ".udperrors");
- if(!st) {
- st = rrdset_create(RRD_TYPE_NET_SNMP6, "udperrors", NULL, "udp", NULL, "IPv6 UDP Errors", "events/s", 3701, update_every, RRDSET_TYPE_LINE);
- st->isdetail = 1;
-
- rrddim_add(st, "RcvbufErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "SndbufErrors", NULL, -1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "InErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "NoPorts", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "IgnoredMulti", NULL, 1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "InErrors", Udp6InErrors);
- rrddim_set(st, "NoPorts", Udp6NoPorts);
- rrddim_set(st, "RcvbufErrors", Udp6RcvbufErrors);
- rrddim_set(st, "SndbufErrors", Udp6SndbufErrors);
- rrddim_set(st, "InCsumErrors", Udp6InCsumErrors);
- rrddim_set(st, "IgnoredMulti", Udp6IgnoredMulti);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(do_udplite_packets == CONFIG_ONDEMAND_YES || (do_udplite_packets == CONFIG_ONDEMAND_ONDEMAND && (UdpLite6InDatagrams || UdpLite6OutDatagrams))) {
- do_udplite_packets = CONFIG_ONDEMAND_YES;
- st = rrdset_find(RRD_TYPE_NET_SNMP6 ".udplitepackets");
- if(!st) {
- st = rrdset_create(RRD_TYPE_NET_SNMP6, "udplitepackets", NULL, "udplite", NULL, "IPv6 UDPlite Packets", "packets/s", 3601, update_every, RRDSET_TYPE_LINE);
-
- rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "received", UdpLite6InDatagrams);
- rrddim_set(st, "sent", UdpLite6OutDatagrams);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(do_udplite_errors == CONFIG_ONDEMAND_YES || (do_udplite_errors == CONFIG_ONDEMAND_ONDEMAND
- && (
- UdpLite6InErrors
- || UdpLite6NoPorts
- || UdpLite6RcvbufErrors
- || UdpLite6SndbufErrors
- || Udp6InCsumErrors
- || UdpLite6InCsumErrors
- ))) {
- do_udplite_errors = CONFIG_ONDEMAND_YES;
- st = rrdset_find(RRD_TYPE_NET_SNMP6 ".udpliteerrors");
- if(!st) {
- st = rrdset_create(RRD_TYPE_NET_SNMP6, "udpliteerrors", NULL, "udplite", NULL, "IPv6 UDP Lite Errors", "events/s", 3701, update_every, RRDSET_TYPE_LINE);
- st->isdetail = 1;
-
- rrddim_add(st, "RcvbufErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "SndbufErrors", NULL, -1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "InErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "NoPorts", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "InErrors", UdpLite6InErrors);
- rrddim_set(st, "NoPorts", UdpLite6NoPorts);
- rrddim_set(st, "RcvbufErrors", UdpLite6RcvbufErrors);
- rrddim_set(st, "SndbufErrors", UdpLite6SndbufErrors);
- rrddim_set(st, "InCsumErrors", UdpLite6InCsumErrors);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(do_mcast == CONFIG_ONDEMAND_YES || (do_mcast == CONFIG_ONDEMAND_ONDEMAND && (Ip6OutMcastOctets || Ip6InMcastOctets))) {
- do_mcast = CONFIG_ONDEMAND_YES;
- st = rrdset_find(RRD_TYPE_NET_SNMP6 ".mcast");
- if(!st) {
- st = rrdset_create(RRD_TYPE_NET_SNMP6, "mcast", NULL, "multicast", NULL, "IPv6 Multicast Bandwidth", "kilobits/s", 9000, update_every, RRDSET_TYPE_AREA);
- st->isdetail = 1;
-
- rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL);
- rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "sent", Ip6OutMcastOctets);
- rrddim_set(st, "received", Ip6InMcastOctets);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(do_bcast == CONFIG_ONDEMAND_YES || (do_bcast == CONFIG_ONDEMAND_ONDEMAND && (Ip6OutBcastOctets || Ip6InBcastOctets))) {
- do_bcast = CONFIG_ONDEMAND_YES;
- st = rrdset_find(RRD_TYPE_NET_SNMP6 ".bcast");
- if(!st) {
- st = rrdset_create(RRD_TYPE_NET_SNMP6, "bcast", NULL, "broadcast", NULL, "IPv6 Broadcast Bandwidth", "kilobits/s", 8000, update_every, RRDSET_TYPE_AREA);
- st->isdetail = 1;
-
- rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL);
- rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "sent", Ip6OutBcastOctets);
- rrddim_set(st, "received", Ip6InBcastOctets);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(do_mcast_p == CONFIG_ONDEMAND_YES || (do_mcast_p == CONFIG_ONDEMAND_ONDEMAND && (Ip6OutMcastPkts || Ip6InMcastPkts))) {
- do_mcast_p = CONFIG_ONDEMAND_YES;
- st = rrdset_find(RRD_TYPE_NET_SNMP6 ".mcastpkts");
- if(!st) {
- st = rrdset_create(RRD_TYPE_NET_SNMP6, "mcastpkts", NULL, "multicast", NULL, "IPv6 Multicast Packets", "packets/s", 9500, update_every, RRDSET_TYPE_LINE);
- st->isdetail = 1;
-
- rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "sent", Ip6OutMcastPkts);
- rrddim_set(st, "received", Ip6InMcastPkts);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(do_icmp == CONFIG_ONDEMAND_YES || (do_icmp == CONFIG_ONDEMAND_ONDEMAND && (Icmp6InMsgs || Icmp6OutMsgs))) {
- do_icmp = CONFIG_ONDEMAND_YES;
- st = rrdset_find(RRD_TYPE_NET_SNMP6 ".icmp");
- if(!st) {
- st = rrdset_create(RRD_TYPE_NET_SNMP6, "icmp", NULL, "icmp", NULL, "IPv6 ICMP Messages", "messages/s", 10000, update_every, RRDSET_TYPE_LINE);
-
- rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "sent", Icmp6InMsgs);
- rrddim_set(st, "received", Icmp6OutMsgs);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(do_icmp_redir == CONFIG_ONDEMAND_YES || (do_icmp_redir == CONFIG_ONDEMAND_ONDEMAND && (Icmp6InRedirects || Icmp6OutRedirects))) {
- do_icmp_redir = CONFIG_ONDEMAND_YES;
- st = rrdset_find(RRD_TYPE_NET_SNMP6 ".icmpredir");
- if(!st) {
- st = rrdset_create(RRD_TYPE_NET_SNMP6, "icmpredir", NULL, "icmp", NULL, "IPv6 ICMP Redirects", "redirects/s", 10050, update_every, RRDSET_TYPE_LINE);
-
- rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "sent", Icmp6InRedirects);
- rrddim_set(st, "received", Icmp6OutRedirects);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(do_icmp_errors == CONFIG_ONDEMAND_YES || (do_icmp_errors == CONFIG_ONDEMAND_ONDEMAND
- && (
- Icmp6InErrors
- || Icmp6OutErrors
- || Icmp6InCsumErrors
- || Icmp6InDestUnreachs
- || Icmp6InPktTooBigs
- || Icmp6InTimeExcds
- || Icmp6InParmProblems
- || Icmp6OutDestUnreachs
- || Icmp6OutPktTooBigs
- || Icmp6OutTimeExcds
- || Icmp6OutParmProblems
- ))) {
- do_icmp_errors = CONFIG_ONDEMAND_YES;
- st = rrdset_find(RRD_TYPE_NET_SNMP6 ".icmperrors");
- if(!st) {
- st = rrdset_create(RRD_TYPE_NET_SNMP6, "icmperrors", NULL, "icmp", NULL, "IPv6 ICMP Errors", "errors/s", 10100, update_every, RRDSET_TYPE_LINE);
-
- rrddim_add(st, "InErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "OutErrors", NULL, -1, 1, RRDDIM_INCREMENTAL);
-
- rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "InDestUnreachs", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "InPktTooBigs", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "InTimeExcds", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "InParmProblems", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "OutDestUnreachs", NULL, -1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "OutPktTooBigs", NULL, -1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "OutTimeExcds", NULL, -1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "OutParmProblems", NULL, -1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "InErrors", Icmp6InErrors);
- rrddim_set(st, "OutErrors", Icmp6OutErrors);
- rrddim_set(st, "InCsumErrors", Icmp6InCsumErrors);
- rrddim_set(st, "InDestUnreachs", Icmp6InDestUnreachs);
- rrddim_set(st, "InPktTooBigs", Icmp6InPktTooBigs);
- rrddim_set(st, "InTimeExcds", Icmp6InTimeExcds);
- rrddim_set(st, "InParmProblems", Icmp6InParmProblems);
- rrddim_set(st, "OutDestUnreachs", Icmp6OutDestUnreachs);
- rrddim_set(st, "OutPktTooBigs", Icmp6OutPktTooBigs);
- rrddim_set(st, "OutTimeExcds", Icmp6OutTimeExcds);
- rrddim_set(st, "OutParmProblems", Icmp6OutParmProblems);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(do_icmp_echos == CONFIG_ONDEMAND_YES || (do_icmp_echos == CONFIG_ONDEMAND_ONDEMAND
- && (
- Icmp6InEchos
- || Icmp6OutEchos
- || Icmp6InEchoReplies
- || Icmp6OutEchoReplies
- ))) {
- do_icmp_echos = CONFIG_ONDEMAND_YES;
- st = rrdset_find(RRD_TYPE_NET_SNMP6 ".icmpechos");
- if(!st) {
- st = rrdset_create(RRD_TYPE_NET_SNMP6, "icmpechos", NULL, "icmp", NULL, "IPv6 ICMP Echo", "messages/s", 10200, update_every, RRDSET_TYPE_LINE);
-
- rrddim_add(st, "InEchos", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "OutEchos", NULL, -1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "InEchoReplies", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "OutEchoReplies", NULL, -1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "InEchos", Icmp6InEchos);
- rrddim_set(st, "OutEchos", Icmp6OutEchos);
- rrddim_set(st, "InEchoReplies", Icmp6InEchoReplies);
- rrddim_set(st, "OutEchoReplies", Icmp6OutEchoReplies);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(do_icmp_groupmemb == CONFIG_ONDEMAND_YES || (do_icmp_groupmemb == CONFIG_ONDEMAND_ONDEMAND
- && (
- Icmp6InGroupMembQueries
- || Icmp6OutGroupMembQueries
- || Icmp6InGroupMembResponses
- || Icmp6OutGroupMembResponses
- || Icmp6InGroupMembReductions
- || Icmp6OutGroupMembReductions
- ))) {
- do_icmp_groupmemb = CONFIG_ONDEMAND_YES;
- st = rrdset_find(RRD_TYPE_NET_SNMP6 ".groupmemb");
- if(!st) {
- st = rrdset_create(RRD_TYPE_NET_SNMP6, "groupmemb", NULL, "icmp", NULL, "IPv6 ICMP Group Membership", "messages/s", 10300, update_every, RRDSET_TYPE_LINE);
-
- rrddim_add(st, "InQueries", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "OutQueries", NULL, -1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "InResponses", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "OutResponses", NULL, -1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "InReductions", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "OutReductions", NULL, -1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "InQueries", Icmp6InGroupMembQueries);
- rrddim_set(st, "OutQueries", Icmp6OutGroupMembQueries);
- rrddim_set(st, "InResponses", Icmp6InGroupMembResponses);
- rrddim_set(st, "OutResponses", Icmp6OutGroupMembResponses);
- rrddim_set(st, "InReductions", Icmp6InGroupMembReductions);
- rrddim_set(st, "OutReductions", Icmp6OutGroupMembReductions);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(do_icmp_router == CONFIG_ONDEMAND_YES || (do_icmp_router == CONFIG_ONDEMAND_ONDEMAND
- && (
- Icmp6InRouterSolicits
- || Icmp6OutRouterSolicits
- || Icmp6InRouterAdvertisements
- || Icmp6OutRouterAdvertisements
- ))) {
- do_icmp_router = CONFIG_ONDEMAND_YES;
- st = rrdset_find(RRD_TYPE_NET_SNMP6 ".icmprouter");
- if(!st) {
- st = rrdset_create(RRD_TYPE_NET_SNMP6, "icmprouter", NULL, "icmp", NULL, "IPv6 Router Messages", "messages/s", 10400, update_every, RRDSET_TYPE_LINE);
-
- rrddim_add(st, "InSolicits", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "OutSolicits", NULL, -1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "InAdvertisements", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "OutAdvertisements", NULL, -1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "InSolicits", Icmp6InRouterSolicits);
- rrddim_set(st, "OutSolicits", Icmp6OutRouterSolicits);
- rrddim_set(st, "InAdvertisements", Icmp6InRouterAdvertisements);
- rrddim_set(st, "OutAdvertisements", Icmp6OutRouterAdvertisements);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(do_icmp_neighbor == CONFIG_ONDEMAND_YES || (do_icmp_neighbor == CONFIG_ONDEMAND_ONDEMAND
- && (
- Icmp6InNeighborSolicits
- || Icmp6OutNeighborSolicits
- || Icmp6InNeighborAdvertisements
- || Icmp6OutNeighborAdvertisements
- ))) {
- do_icmp_neighbor = CONFIG_ONDEMAND_YES;
- st = rrdset_find(RRD_TYPE_NET_SNMP6 ".icmpneighbor");
- if(!st) {
- st = rrdset_create(RRD_TYPE_NET_SNMP6, "icmpneighbor", NULL, "icmp", NULL, "IPv6 Neighbor Messages", "messages/s", 10500, update_every, RRDSET_TYPE_LINE);
-
- rrddim_add(st, "InSolicits", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "OutSolicits", NULL, -1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "InAdvertisements", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "OutAdvertisements", NULL, -1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "InSolicits", Icmp6InNeighborSolicits);
- rrddim_set(st, "OutSolicits", Icmp6OutNeighborSolicits);
- rrddim_set(st, "InAdvertisements", Icmp6InNeighborAdvertisements);
- rrddim_set(st, "OutAdvertisements", Icmp6OutNeighborAdvertisements);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(do_icmp_mldv2 == CONFIG_ONDEMAND_YES || (do_icmp_mldv2 == CONFIG_ONDEMAND_ONDEMAND && (Icmp6InMLDv2Reports || Icmp6OutMLDv2Reports))) {
- do_icmp_mldv2 = CONFIG_ONDEMAND_YES;
- st = rrdset_find(RRD_TYPE_NET_SNMP6 ".icmpmldv2");
- if(!st) {
- st = rrdset_create(RRD_TYPE_NET_SNMP6, "icmpmldv2", NULL, "icmp", NULL, "IPv6 ICMP MLDv2 Reports", "reports/s", 10600, update_every, RRDSET_TYPE_LINE);
-
- rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "sent", Icmp6InMLDv2Reports);
- rrddim_set(st, "received", Icmp6OutMLDv2Reports);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(do_icmp_types == CONFIG_ONDEMAND_YES || (do_icmp_types == CONFIG_ONDEMAND_ONDEMAND
- && (
- Icmp6InType1
- || Icmp6InType128
- || Icmp6InType129
- || Icmp6InType136
- || Icmp6OutType1
- || Icmp6OutType128
- || Icmp6OutType129
- || Icmp6OutType133
- || Icmp6OutType135
- || Icmp6OutType143
- ))) {
- do_icmp_types = CONFIG_ONDEMAND_YES;
- st = rrdset_find(RRD_TYPE_NET_SNMP6 ".icmptypes");
- if(!st) {
- st = rrdset_create(RRD_TYPE_NET_SNMP6, "icmptypes", NULL, "icmp", NULL, "IPv6 ICMP Types", "messages/s", 10700, update_every, RRDSET_TYPE_LINE);
-
- rrddim_add(st, "InType1", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "InType128", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "InType129", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "InType136", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "OutType1", NULL, -1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "OutType128", NULL, -1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "OutType129", NULL, -1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "OutType133", NULL, -1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "OutType135", NULL, -1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "OutType143", NULL, -1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "InType1", Icmp6InType1);
- rrddim_set(st, "InType128", Icmp6InType128);
- rrddim_set(st, "InType129", Icmp6InType129);
- rrddim_set(st, "InType136", Icmp6InType136);
- rrddim_set(st, "OutType1", Icmp6OutType1);
- rrddim_set(st, "OutType128", Icmp6OutType128);
- rrddim_set(st, "OutType129", Icmp6OutType129);
- rrddim_set(st, "OutType133", Icmp6OutType133);
- rrddim_set(st, "OutType135", Icmp6OutType135);
- rrddim_set(st, "OutType143", Icmp6OutType143);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(do_ect == CONFIG_ONDEMAND_YES || (do_ect == CONFIG_ONDEMAND_ONDEMAND
- && (
- Ip6InNoECTPkts
- || Ip6InECT1Pkts
- || Ip6InECT0Pkts
- || Ip6InCEPkts
- ))) {
- do_ect = CONFIG_ONDEMAND_YES;
- st = rrdset_find(RRD_TYPE_NET_SNMP6 ".ect");
- if(!st) {
- st = rrdset_create(RRD_TYPE_NET_SNMP6, "ect", NULL, "packets", NULL, "IPv6 ECT Packets", "packets/s", 10800, update_every, RRDSET_TYPE_LINE);
-
- rrddim_add(st, "InNoECTPkts", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "InECT1Pkts", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "InECT0Pkts", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "InCEPkts", NULL, 1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "InNoECTPkts", Ip6InNoECTPkts);
- rrddim_set(st, "InECT1Pkts", Ip6InECT1Pkts);
- rrddim_set(st, "InECT0Pkts", Ip6InECT0Pkts);
- rrddim_set(st, "InCEPkts", Ip6InCEPkts);
- rrdset_done(st);
- }
-
- return 0;
+ static procfile *ff = NULL;
+ static int gen_hashes = -1;
+
+ static int do_ip_packets = -1, do_ip_fragsout = -1, do_ip_fragsin = -1, do_ip_errors = -1,
+ do_udplite_packets = -1, do_udplite_errors = -1,
+ do_udp_packets = -1, do_udp_errors = -1,
+ do_bandwidth = -1, do_mcast = -1, do_bcast = -1, do_mcast_p = -1,
+ do_icmp = -1, do_icmp_redir = -1, do_icmp_errors = -1, do_icmp_echos = -1, do_icmp_groupmemb = -1,
+ do_icmp_router = -1, do_icmp_neighbor = -1, do_icmp_mldv2 = -1, do_icmp_types = -1, do_ect = -1;
+
+ static uint32_t hash_Ip6InReceives = -1;
+
+ static uint32_t hash_Ip6InHdrErrors = -1;
+ static uint32_t hash_Ip6InTooBigErrors = -1;
+ static uint32_t hash_Ip6InNoRoutes = -1;
+ static uint32_t hash_Ip6InAddrErrors = -1;
+ static uint32_t hash_Ip6InUnknownProtos = -1;
+ static uint32_t hash_Ip6InTruncatedPkts = -1;
+ static uint32_t hash_Ip6InDiscards = -1;
+ static uint32_t hash_Ip6InDelivers = -1;
+
+ static uint32_t hash_Ip6OutForwDatagrams = -1;
+ static uint32_t hash_Ip6OutRequests = -1;
+ static uint32_t hash_Ip6OutDiscards = -1;
+ static uint32_t hash_Ip6OutNoRoutes = -1;
+
+ static uint32_t hash_Ip6ReasmTimeout = -1;
+ static uint32_t hash_Ip6ReasmReqds = -1;
+ static uint32_t hash_Ip6ReasmOKs = -1;
+ static uint32_t hash_Ip6ReasmFails = -1;
+
+ static uint32_t hash_Ip6FragOKs = -1;
+ static uint32_t hash_Ip6FragFails = -1;
+ static uint32_t hash_Ip6FragCreates = -1;
+
+ static uint32_t hash_Ip6InMcastPkts = -1;
+ static uint32_t hash_Ip6OutMcastPkts = -1;
+
+ static uint32_t hash_Ip6InOctets = -1;
+ static uint32_t hash_Ip6OutOctets = -1;
+
+ static uint32_t hash_Ip6InMcastOctets = -1;
+ static uint32_t hash_Ip6OutMcastOctets = -1;
+ static uint32_t hash_Ip6InBcastOctets = -1;
+ static uint32_t hash_Ip6OutBcastOctets = -1;
+
+ static uint32_t hash_Ip6InNoECTPkts = -1;
+ static uint32_t hash_Ip6InECT1Pkts = -1;
+ static uint32_t hash_Ip6InECT0Pkts = -1;
+ static uint32_t hash_Ip6InCEPkts = -1;
+
+ static uint32_t hash_Icmp6InMsgs = -1;
+ static uint32_t hash_Icmp6InErrors = -1;
+ static uint32_t hash_Icmp6OutMsgs = -1;
+ static uint32_t hash_Icmp6OutErrors = -1;
+ static uint32_t hash_Icmp6InCsumErrors = -1;
+ static uint32_t hash_Icmp6InDestUnreachs = -1;
+ static uint32_t hash_Icmp6InPktTooBigs = -1;
+ static uint32_t hash_Icmp6InTimeExcds = -1;
+ static uint32_t hash_Icmp6InParmProblems = -1;
+ static uint32_t hash_Icmp6InEchos = -1;
+ static uint32_t hash_Icmp6InEchoReplies = -1;
+ static uint32_t hash_Icmp6InGroupMembQueries = -1;
+ static uint32_t hash_Icmp6InGroupMembResponses = -1;
+ static uint32_t hash_Icmp6InGroupMembReductions = -1;
+ static uint32_t hash_Icmp6InRouterSolicits = -1;
+ static uint32_t hash_Icmp6InRouterAdvertisements = -1;
+ static uint32_t hash_Icmp6InNeighborSolicits = -1;
+ static uint32_t hash_Icmp6InNeighborAdvertisements = -1;
+ static uint32_t hash_Icmp6InRedirects = -1;
+ static uint32_t hash_Icmp6InMLDv2Reports = -1;
+ static uint32_t hash_Icmp6OutDestUnreachs = -1;
+ static uint32_t hash_Icmp6OutPktTooBigs = -1;
+ static uint32_t hash_Icmp6OutTimeExcds = -1;
+ static uint32_t hash_Icmp6OutParmProblems = -1;
+ static uint32_t hash_Icmp6OutEchos = -1;
+ static uint32_t hash_Icmp6OutEchoReplies = -1;
+ static uint32_t hash_Icmp6OutGroupMembQueries = -1;
+ static uint32_t hash_Icmp6OutGroupMembResponses = -1;
+ static uint32_t hash_Icmp6OutGroupMembReductions = -1;
+ static uint32_t hash_Icmp6OutRouterSolicits = -1;
+ static uint32_t hash_Icmp6OutRouterAdvertisements = -1;
+ static uint32_t hash_Icmp6OutNeighborSolicits = -1;
+ static uint32_t hash_Icmp6OutNeighborAdvertisements = -1;
+ static uint32_t hash_Icmp6OutRedirects = -1;
+ static uint32_t hash_Icmp6OutMLDv2Reports = -1;
+ static uint32_t hash_Icmp6InType1 = -1;
+ static uint32_t hash_Icmp6InType128 = -1;
+ static uint32_t hash_Icmp6InType129 = -1;
+ static uint32_t hash_Icmp6InType136 = -1;
+ static uint32_t hash_Icmp6OutType1 = -1;
+ static uint32_t hash_Icmp6OutType128 = -1;
+ static uint32_t hash_Icmp6OutType129 = -1;
+ static uint32_t hash_Icmp6OutType133 = -1;
+ static uint32_t hash_Icmp6OutType135 = -1;
+ static uint32_t hash_Icmp6OutType143 = -1;
+
+ static uint32_t hash_Udp6InDatagrams = -1;
+ static uint32_t hash_Udp6NoPorts = -1;
+ static uint32_t hash_Udp6InErrors = -1;
+ static uint32_t hash_Udp6OutDatagrams = -1;
+ static uint32_t hash_Udp6RcvbufErrors = -1;
+ static uint32_t hash_Udp6SndbufErrors = -1;
+ static uint32_t hash_Udp6InCsumErrors = -1;
+ static uint32_t hash_Udp6IgnoredMulti = -1;
+
+ static uint32_t hash_UdpLite6InDatagrams = -1;
+ static uint32_t hash_UdpLite6NoPorts = -1;
+ static uint32_t hash_UdpLite6InErrors = -1;
+ static uint32_t hash_UdpLite6OutDatagrams = -1;
+ static uint32_t hash_UdpLite6RcvbufErrors = -1;
+ static uint32_t hash_UdpLite6SndbufErrors = -1;
+ static uint32_t hash_UdpLite6InCsumErrors = -1;
+
+ if(gen_hashes != 1) {
+ gen_hashes = 1;
+ hash_Ip6InReceives = simple_hash("Ip6InReceives");
+ hash_Ip6InHdrErrors = simple_hash("Ip6InHdrErrors");
+ hash_Ip6InTooBigErrors = simple_hash("Ip6InTooBigErrors");
+ hash_Ip6InNoRoutes = simple_hash("Ip6InNoRoutes");
+ hash_Ip6InAddrErrors = simple_hash("Ip6InAddrErrors");
+ hash_Ip6InUnknownProtos = simple_hash("Ip6InUnknownProtos");
+ hash_Ip6InTruncatedPkts = simple_hash("Ip6InTruncatedPkts");
+ hash_Ip6InDiscards = simple_hash("Ip6InDiscards");
+ hash_Ip6InDelivers = simple_hash("Ip6InDelivers");
+ hash_Ip6OutForwDatagrams = simple_hash("Ip6OutForwDatagrams");
+ hash_Ip6OutRequests = simple_hash("Ip6OutRequests");
+ hash_Ip6OutDiscards = simple_hash("Ip6OutDiscards");
+ hash_Ip6OutNoRoutes = simple_hash("Ip6OutNoRoutes");
+ hash_Ip6ReasmTimeout = simple_hash("Ip6ReasmTimeout");
+ hash_Ip6ReasmReqds = simple_hash("Ip6ReasmReqds");
+ hash_Ip6ReasmOKs = simple_hash("Ip6ReasmOKs");
+ hash_Ip6ReasmFails = simple_hash("Ip6ReasmFails");
+ hash_Ip6FragOKs = simple_hash("Ip6FragOKs");
+ hash_Ip6FragFails = simple_hash("Ip6FragFails");
+ hash_Ip6FragCreates = simple_hash("Ip6FragCreates");
+ hash_Ip6InMcastPkts = simple_hash("Ip6InMcastPkts");
+ hash_Ip6OutMcastPkts = simple_hash("Ip6OutMcastPkts");
+ hash_Ip6InOctets = simple_hash("Ip6InOctets");
+ hash_Ip6OutOctets = simple_hash("Ip6OutOctets");
+ hash_Ip6InMcastOctets = simple_hash("Ip6InMcastOctets");
+ hash_Ip6OutMcastOctets = simple_hash("Ip6OutMcastOctets");
+ hash_Ip6InBcastOctets = simple_hash("Ip6InBcastOctets");
+ hash_Ip6OutBcastOctets = simple_hash("Ip6OutBcastOctets");
+ hash_Ip6InNoECTPkts = simple_hash("Ip6InNoECTPkts");
+ hash_Ip6InECT1Pkts = simple_hash("Ip6InECT1Pkts");
+ hash_Ip6InECT0Pkts = simple_hash("Ip6InECT0Pkts");
+ hash_Ip6InCEPkts = simple_hash("Ip6InCEPkts");
+ hash_Icmp6InMsgs = simple_hash("Icmp6InMsgs");
+ hash_Icmp6InErrors = simple_hash("Icmp6InErrors");
+ hash_Icmp6OutMsgs = simple_hash("Icmp6OutMsgs");
+ hash_Icmp6OutErrors = simple_hash("Icmp6OutErrors");
+ hash_Icmp6InCsumErrors = simple_hash("Icmp6InCsumErrors");
+ hash_Icmp6InDestUnreachs = simple_hash("Icmp6InDestUnreachs");
+ hash_Icmp6InPktTooBigs = simple_hash("Icmp6InPktTooBigs");
+ hash_Icmp6InTimeExcds = simple_hash("Icmp6InTimeExcds");
+ hash_Icmp6InParmProblems = simple_hash("Icmp6InParmProblems");
+ hash_Icmp6InEchos = simple_hash("Icmp6InEchos");
+ hash_Icmp6InEchoReplies = simple_hash("Icmp6InEchoReplies");
+ hash_Icmp6InGroupMembQueries = simple_hash("Icmp6InGroupMembQueries");
+ hash_Icmp6InGroupMembResponses = simple_hash("Icmp6InGroupMembResponses");
+ hash_Icmp6InGroupMembReductions = simple_hash("Icmp6InGroupMembReductions");
+ hash_Icmp6InRouterSolicits = simple_hash("Icmp6InRouterSolicits");
+ hash_Icmp6InRouterAdvertisements = simple_hash("Icmp6InRouterAdvertisements");
+ hash_Icmp6InNeighborSolicits = simple_hash("Icmp6InNeighborSolicits");
+ hash_Icmp6InNeighborAdvertisements = simple_hash("Icmp6InNeighborAdvertisements");
+ hash_Icmp6InRedirects = simple_hash("Icmp6InRedirects");
+ hash_Icmp6InMLDv2Reports = simple_hash("Icmp6InMLDv2Reports");
+ hash_Icmp6OutDestUnreachs = simple_hash("Icmp6OutDestUnreachs");
+ hash_Icmp6OutPktTooBigs = simple_hash("Icmp6OutPktTooBigs");
+ hash_Icmp6OutTimeExcds = simple_hash("Icmp6OutTimeExcds");
+ hash_Icmp6OutParmProblems = simple_hash("Icmp6OutParmProblems");
+ hash_Icmp6OutEchos = simple_hash("Icmp6OutEchos");
+ hash_Icmp6OutEchoReplies = simple_hash("Icmp6OutEchoReplies");
+ hash_Icmp6OutGroupMembQueries = simple_hash("Icmp6OutGroupMembQueries");
+ hash_Icmp6OutGroupMembResponses = simple_hash("Icmp6OutGroupMembResponses");
+ hash_Icmp6OutGroupMembReductions = simple_hash("Icmp6OutGroupMembReductions");
+ hash_Icmp6OutRouterSolicits = simple_hash("Icmp6OutRouterSolicits");
+ hash_Icmp6OutRouterAdvertisements = simple_hash("Icmp6OutRouterAdvertisements");
+ hash_Icmp6OutNeighborSolicits = simple_hash("Icmp6OutNeighborSolicits");
+ hash_Icmp6OutNeighborAdvertisements = simple_hash("Icmp6OutNeighborAdvertisements");
+ hash_Icmp6OutRedirects = simple_hash("Icmp6OutRedirects");
+ hash_Icmp6OutMLDv2Reports = simple_hash("Icmp6OutMLDv2Reports");
+ hash_Icmp6InType1 = simple_hash("Icmp6InType1");
+ hash_Icmp6InType128 = simple_hash("Icmp6InType128");
+ hash_Icmp6InType129 = simple_hash("Icmp6InType129");
+ hash_Icmp6InType136 = simple_hash("Icmp6InType136");
+ hash_Icmp6OutType1 = simple_hash("Icmp6OutType1");
+ hash_Icmp6OutType128 = simple_hash("Icmp6OutType128");
+ hash_Icmp6OutType129 = simple_hash("Icmp6OutType129");
+ hash_Icmp6OutType133 = simple_hash("Icmp6OutType133");
+ hash_Icmp6OutType135 = simple_hash("Icmp6OutType135");
+ hash_Icmp6OutType143 = simple_hash("Icmp6OutType143");
+ hash_Udp6InDatagrams = simple_hash("Udp6InDatagrams");
+ hash_Udp6NoPorts = simple_hash("Udp6NoPorts");
+ hash_Udp6InErrors = simple_hash("Udp6InErrors");
+ hash_Udp6OutDatagrams = simple_hash("Udp6OutDatagrams");
+ hash_Udp6RcvbufErrors = simple_hash("Udp6RcvbufErrors");
+ hash_Udp6SndbufErrors = simple_hash("Udp6SndbufErrors");
+ hash_Udp6InCsumErrors = simple_hash("Udp6InCsumErrors");
+ hash_Udp6IgnoredMulti = simple_hash("Udp6IgnoredMulti");
+ hash_UdpLite6InDatagrams = simple_hash("UdpLite6InDatagrams");
+ hash_UdpLite6NoPorts = simple_hash("UdpLite6NoPorts");
+ hash_UdpLite6InErrors = simple_hash("UdpLite6InErrors");
+ hash_UdpLite6OutDatagrams = simple_hash("UdpLite6OutDatagrams");
+ hash_UdpLite6RcvbufErrors = simple_hash("UdpLite6RcvbufErrors");
+ hash_UdpLite6SndbufErrors = simple_hash("UdpLite6SndbufErrors");
+ hash_UdpLite6InCsumErrors = simple_hash("UdpLite6InCsumErrors");
+ }
+
+ if(do_ip_packets == -1) do_ip_packets = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 packets", CONFIG_ONDEMAND_ONDEMAND);
+ if(do_ip_fragsout == -1) do_ip_fragsout = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 fragments sent", CONFIG_ONDEMAND_ONDEMAND);
+ if(do_ip_fragsin == -1) do_ip_fragsin = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 fragments assembly", CONFIG_ONDEMAND_ONDEMAND);
+ if(do_ip_errors == -1) do_ip_errors = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 errors", CONFIG_ONDEMAND_ONDEMAND);
+ if(do_udp_packets == -1) do_udp_packets = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 UDP packets", CONFIG_ONDEMAND_ONDEMAND);
+ if(do_udp_errors == -1) do_udp_errors = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 UDP errors", CONFIG_ONDEMAND_ONDEMAND);
+ if(do_udplite_packets == -1) do_udplite_packets = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 UDPlite packets", CONFIG_ONDEMAND_ONDEMAND);
+ if(do_udplite_errors == -1) do_udplite_errors = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 UDPlite errors", CONFIG_ONDEMAND_ONDEMAND);
+ if(do_bandwidth == -1) do_bandwidth = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "bandwidth", CONFIG_ONDEMAND_ONDEMAND);
+ if(do_mcast == -1) do_mcast = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "multicast bandwidth", CONFIG_ONDEMAND_ONDEMAND);
+ if(do_bcast == -1) do_bcast = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "broadcast bandwidth", CONFIG_ONDEMAND_ONDEMAND);
+ if(do_mcast_p == -1) do_mcast_p = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "multicast packets", CONFIG_ONDEMAND_ONDEMAND);
+ if(do_icmp == -1) do_icmp = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp", CONFIG_ONDEMAND_ONDEMAND);
+ if(do_icmp_redir == -1) do_icmp_redir = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp redirects", CONFIG_ONDEMAND_ONDEMAND);
+ if(do_icmp_errors == -1) do_icmp_errors = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp errors", CONFIG_ONDEMAND_ONDEMAND);
+ if(do_icmp_echos == -1) do_icmp_echos = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp echos", CONFIG_ONDEMAND_ONDEMAND);
+ if(do_icmp_groupmemb == -1) do_icmp_groupmemb = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp group membership", CONFIG_ONDEMAND_ONDEMAND);
+ if(do_icmp_router == -1) do_icmp_router = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp router", CONFIG_ONDEMAND_ONDEMAND);
+ if(do_icmp_neighbor == -1) do_icmp_neighbor = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp neighbor", CONFIG_ONDEMAND_ONDEMAND);
+ if(do_icmp_mldv2 == -1) do_icmp_mldv2 = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp mldv2", CONFIG_ONDEMAND_ONDEMAND);
+ if(do_icmp_types == -1) do_icmp_types = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp types", CONFIG_ONDEMAND_ONDEMAND);
+ if(do_ect == -1) do_ect = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ect", CONFIG_ONDEMAND_ONDEMAND);
+
+ if(dt) {};
+
+ if(!ff) {
+ char filename[FILENAME_MAX + 1];
+ snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/net/snmp6");
+ ff = procfile_open(config_get("plugin:proc:/proc/net/snmp6", "filename to monitor", filename), " \t:", PROCFILE_FLAG_DEFAULT);
+ }
+ if(!ff) return 1;
+
+ ff = procfile_readall(ff);
+ if(!ff) return 0; // we return 0, so that we will retry to open it next time
+
+ uint32_t lines = procfile_lines(ff), l;
+ uint32_t words;
+
+ unsigned long long Ip6InReceives = 0ULL;
+ unsigned long long Ip6InHdrErrors = 0ULL;
+ unsigned long long Ip6InTooBigErrors = 0ULL;
+ unsigned long long Ip6InNoRoutes = 0ULL;
+ unsigned long long Ip6InAddrErrors = 0ULL;
+ unsigned long long Ip6InUnknownProtos = 0ULL;
+ unsigned long long Ip6InTruncatedPkts = 0ULL;
+ unsigned long long Ip6InDiscards = 0ULL;
+ unsigned long long Ip6InDelivers = 0ULL;
+ unsigned long long Ip6OutForwDatagrams = 0ULL;
+ unsigned long long Ip6OutRequests = 0ULL;
+ unsigned long long Ip6OutDiscards = 0ULL;
+ unsigned long long Ip6OutNoRoutes = 0ULL;
+ unsigned long long Ip6ReasmTimeout = 0ULL;
+ unsigned long long Ip6ReasmReqds = 0ULL;
+ unsigned long long Ip6ReasmOKs = 0ULL;
+ unsigned long long Ip6ReasmFails = 0ULL;
+ unsigned long long Ip6FragOKs = 0ULL;
+ unsigned long long Ip6FragFails = 0ULL;
+ unsigned long long Ip6FragCreates = 0ULL;
+ unsigned long long Ip6InMcastPkts = 0ULL;
+ unsigned long long Ip6OutMcastPkts = 0ULL;
+ unsigned long long Ip6InOctets = 0ULL;
+ unsigned long long Ip6OutOctets = 0ULL;
+ unsigned long long Ip6InMcastOctets = 0ULL;
+ unsigned long long Ip6OutMcastOctets = 0ULL;
+ unsigned long long Ip6InBcastOctets = 0ULL;
+ unsigned long long Ip6OutBcastOctets = 0ULL;
+ unsigned long long Ip6InNoECTPkts = 0ULL;
+ unsigned long long Ip6InECT1Pkts = 0ULL;
+ unsigned long long Ip6InECT0Pkts = 0ULL;
+ unsigned long long Ip6InCEPkts = 0ULL;
+ unsigned long long Icmp6InMsgs = 0ULL;
+ unsigned long long Icmp6InErrors = 0ULL;
+ unsigned long long Icmp6OutMsgs = 0ULL;
+ unsigned long long Icmp6OutErrors = 0ULL;
+ unsigned long long Icmp6InCsumErrors = 0ULL;
+ unsigned long long Icmp6InDestUnreachs = 0ULL;
+ unsigned long long Icmp6InPktTooBigs = 0ULL;
+ unsigned long long Icmp6InTimeExcds = 0ULL;
+ unsigned long long Icmp6InParmProblems = 0ULL;
+ unsigned long long Icmp6InEchos = 0ULL;
+ unsigned long long Icmp6InEchoReplies = 0ULL;
+ unsigned long long Icmp6InGroupMembQueries = 0ULL;
+ unsigned long long Icmp6InGroupMembResponses = 0ULL;
+ unsigned long long Icmp6InGroupMembReductions = 0ULL;
+ unsigned long long Icmp6InRouterSolicits = 0ULL;
+ unsigned long long Icmp6InRouterAdvertisements = 0ULL;
+ unsigned long long Icmp6InNeighborSolicits = 0ULL;
+ unsigned long long Icmp6InNeighborAdvertisements = 0ULL;
+ unsigned long long Icmp6InRedirects = 0ULL;
+ unsigned long long Icmp6InMLDv2Reports = 0ULL;
+ unsigned long long Icmp6OutDestUnreachs = 0ULL;
+ unsigned long long Icmp6OutPktTooBigs = 0ULL;
+ unsigned long long Icmp6OutTimeExcds = 0ULL;
+ unsigned long long Icmp6OutParmProblems = 0ULL;
+ unsigned long long Icmp6OutEchos = 0ULL;
+ unsigned long long Icmp6OutEchoReplies = 0ULL;
+ unsigned long long Icmp6OutGroupMembQueries = 0ULL;
+ unsigned long long Icmp6OutGroupMembResponses = 0ULL;
+ unsigned long long Icmp6OutGroupMembReductions = 0ULL;
+ unsigned long long Icmp6OutRouterSolicits = 0ULL;
+ unsigned long long Icmp6OutRouterAdvertisements = 0ULL;
+ unsigned long long Icmp6OutNeighborSolicits = 0ULL;
+ unsigned long long Icmp6OutNeighborAdvertisements = 0ULL;
+ unsigned long long Icmp6OutRedirects = 0ULL;
+ unsigned long long Icmp6OutMLDv2Reports = 0ULL;
+ unsigned long long Icmp6InType1 = 0ULL;
+ unsigned long long Icmp6InType128 = 0ULL;
+ unsigned long long Icmp6InType129 = 0ULL;
+ unsigned long long Icmp6InType136 = 0ULL;
+ unsigned long long Icmp6OutType1 = 0ULL;
+ unsigned long long Icmp6OutType128 = 0ULL;
+ unsigned long long Icmp6OutType129 = 0ULL;
+ unsigned long long Icmp6OutType133 = 0ULL;
+ unsigned long long Icmp6OutType135 = 0ULL;
+ unsigned long long Icmp6OutType143 = 0ULL;
+ unsigned long long Udp6InDatagrams = 0ULL;
+ unsigned long long Udp6NoPorts = 0ULL;
+ unsigned long long Udp6InErrors = 0ULL;
+ unsigned long long Udp6OutDatagrams = 0ULL;
+ unsigned long long Udp6RcvbufErrors = 0ULL;
+ unsigned long long Udp6SndbufErrors = 0ULL;
+ unsigned long long Udp6InCsumErrors = 0ULL;
+ unsigned long long Udp6IgnoredMulti = 0ULL;
+ unsigned long long UdpLite6InDatagrams = 0ULL;
+ unsigned long long UdpLite6NoPorts = 0ULL;
+ unsigned long long UdpLite6InErrors = 0ULL;
+ unsigned long long UdpLite6OutDatagrams = 0ULL;
+ unsigned long long UdpLite6RcvbufErrors = 0ULL;
+ unsigned long long UdpLite6SndbufErrors = 0ULL;
+ unsigned long long UdpLite6InCsumErrors = 0ULL;
+
+ for(l = 0; l < lines ;l++) {
+ words = procfile_linewords(ff, l);
+ if(words < 2) {
+ if(words) error("Cannot read /proc/net/snmp6 line %u. Expected 2 params, read %u.", l, words);
+ continue;
+ }
+
+ char *name = procfile_lineword(ff, l, 0);
+ char * value = procfile_lineword(ff, l, 1);
+ if(!name || !*name || !value || !*value) continue;
+
+ uint32_t hash = simple_hash(name);
+
+ if(0) ;
+ else if(hash == hash_Ip6InReceives && strcmp(name, "Ip6InReceives") == 0) Ip6InReceives = strtoull(value, NULL, 10);
+ else if(hash == hash_Ip6InHdrErrors && strcmp(name, "Ip6InHdrErrors") == 0) Ip6InHdrErrors = strtoull(value, NULL, 10);
+ else if(hash == hash_Ip6InTooBigErrors && strcmp(name, "Ip6InTooBigErrors") == 0) Ip6InTooBigErrors = strtoull(value, NULL, 10);
+ else if(hash == hash_Ip6InNoRoutes && strcmp(name, "Ip6InNoRoutes") == 0) Ip6InNoRoutes = strtoull(value, NULL, 10);
+ else if(hash == hash_Ip6InAddrErrors && strcmp(name, "Ip6InAddrErrors") == 0) Ip6InAddrErrors = strtoull(value, NULL, 10);
+ else if(hash == hash_Ip6InUnknownProtos && strcmp(name, "Ip6InUnknownProtos") == 0) Ip6InUnknownProtos = strtoull(value, NULL, 10);
+ else if(hash == hash_Ip6InTruncatedPkts && strcmp(name, "Ip6InTruncatedPkts") == 0) Ip6InTruncatedPkts = strtoull(value, NULL, 10);
+ else if(hash == hash_Ip6InDiscards && strcmp(name, "Ip6InDiscards") == 0) Ip6InDiscards = strtoull(value, NULL, 10);
+ else if(hash == hash_Ip6InDelivers && strcmp(name, "Ip6InDelivers") == 0) Ip6InDelivers = strtoull(value, NULL, 10);
+ else if(hash == hash_Ip6OutForwDatagrams && strcmp(name, "Ip6OutForwDatagrams") == 0) Ip6OutForwDatagrams = strtoull(value, NULL, 10);
+ else if(hash == hash_Ip6OutRequests && strcmp(name, "Ip6OutRequests") == 0) Ip6OutRequests = strtoull(value, NULL, 10);
+ else if(hash == hash_Ip6OutDiscards && strcmp(name, "Ip6OutDiscards") == 0) Ip6OutDiscards = strtoull(value, NULL, 10);
+ else if(hash == hash_Ip6OutNoRoutes && strcmp(name, "Ip6OutNoRoutes") == 0) Ip6OutNoRoutes = strtoull(value, NULL, 10);
+ else if(hash == hash_Ip6ReasmTimeout && strcmp(name, "Ip6ReasmTimeout") == 0) Ip6ReasmTimeout = strtoull(value, NULL, 10);
+ else if(hash == hash_Ip6ReasmReqds && strcmp(name, "Ip6ReasmReqds") == 0) Ip6ReasmReqds = strtoull(value, NULL, 10);
+ else if(hash == hash_Ip6ReasmOKs && strcmp(name, "Ip6ReasmOKs") == 0) Ip6ReasmOKs = strtoull(value, NULL, 10);
+ else if(hash == hash_Ip6ReasmFails && strcmp(name, "Ip6ReasmFails") == 0) Ip6ReasmFails = strtoull(value, NULL, 10);
+ else if(hash == hash_Ip6FragOKs && strcmp(name, "Ip6FragOKs") == 0) Ip6FragOKs = strtoull(value, NULL, 10);
+ else if(hash == hash_Ip6FragFails && strcmp(name, "Ip6FragFails") == 0) Ip6FragFails = strtoull(value, NULL, 10);
+ else if(hash == hash_Ip6FragCreates && strcmp(name, "Ip6FragCreates") == 0) Ip6FragCreates = strtoull(value, NULL, 10);
+ else if(hash == hash_Ip6InMcastPkts && strcmp(name, "Ip6InMcastPkts") == 0) Ip6InMcastPkts = strtoull(value, NULL, 10);
+ else if(hash == hash_Ip6OutMcastPkts && strcmp(name, "Ip6OutMcastPkts") == 0) Ip6OutMcastPkts = strtoull(value, NULL, 10);
+ else if(hash == hash_Ip6InOctets && strcmp(name, "Ip6InOctets") == 0) Ip6InOctets = strtoull(value, NULL, 10);
+ else if(hash == hash_Ip6OutOctets && strcmp(name, "Ip6OutOctets") == 0) Ip6OutOctets = strtoull(value, NULL, 10);
+ else if(hash == hash_Ip6InMcastOctets && strcmp(name, "Ip6InMcastOctets") == 0) Ip6InMcastOctets = strtoull(value, NULL, 10);
+ else if(hash == hash_Ip6OutMcastOctets && strcmp(name, "Ip6OutMcastOctets") == 0) Ip6OutMcastOctets = strtoull(value, NULL, 10);
+ else if(hash == hash_Ip6InBcastOctets && strcmp(name, "Ip6InBcastOctets") == 0) Ip6InBcastOctets = strtoull(value, NULL, 10);
+ else if(hash == hash_Ip6OutBcastOctets && strcmp(name, "Ip6OutBcastOctets") == 0) Ip6OutBcastOctets = strtoull(value, NULL, 10);
+ else if(hash == hash_Ip6InNoECTPkts && strcmp(name, "Ip6InNoECTPkts") == 0) Ip6InNoECTPkts = strtoull(value, NULL, 10);
+ else if(hash == hash_Ip6InECT1Pkts && strcmp(name, "Ip6InECT1Pkts") == 0) Ip6InECT1Pkts = strtoull(value, NULL, 10);
+ else if(hash == hash_Ip6InECT0Pkts && strcmp(name, "Ip6InECT0Pkts") == 0) Ip6InECT0Pkts = strtoull(value, NULL, 10);
+ else if(hash == hash_Ip6InCEPkts && strcmp(name, "Ip6InCEPkts") == 0) Ip6InCEPkts = strtoull(value, NULL, 10);
+ else if(hash == hash_Icmp6InMsgs && strcmp(name, "Icmp6InMsgs") == 0) Icmp6InMsgs = strtoull(value, NULL, 10);
+ else if(hash == hash_Icmp6InErrors && strcmp(name, "Icmp6InErrors") == 0) Icmp6InErrors = strtoull(value, NULL, 10);
+ else if(hash == hash_Icmp6OutMsgs && strcmp(name, "Icmp6OutMsgs") == 0) Icmp6OutMsgs = strtoull(value, NULL, 10);
+ else if(hash == hash_Icmp6OutErrors && strcmp(name, "Icmp6OutErrors") == 0) Icmp6OutErrors = strtoull(value, NULL, 10);
+ else if(hash == hash_Icmp6InCsumErrors && strcmp(name, "Icmp6InCsumErrors") == 0) Icmp6InCsumErrors = strtoull(value, NULL, 10);
+ else if(hash == hash_Icmp6InDestUnreachs && strcmp(name, "Icmp6InDestUnreachs") == 0) Icmp6InDestUnreachs = strtoull(value, NULL, 10);
+ else if(hash == hash_Icmp6InPktTooBigs && strcmp(name, "Icmp6InPktTooBigs") == 0) Icmp6InPktTooBigs = strtoull(value, NULL, 10);
+ else if(hash == hash_Icmp6InTimeExcds && strcmp(name, "Icmp6InTimeExcds") == 0) Icmp6InTimeExcds = strtoull(value, NULL, 10);
+ else if(hash == hash_Icmp6InParmProblems && strcmp(name, "Icmp6InParmProblems") == 0) Icmp6InParmProblems = strtoull(value, NULL, 10);
+ else if(hash == hash_Icmp6InEchos && strcmp(name, "Icmp6InEchos") == 0) Icmp6InEchos = strtoull(value, NULL, 10);
+ else if(hash == hash_Icmp6InEchoReplies && strcmp(name, "Icmp6InEchoReplies") == 0) Icmp6InEchoReplies = strtoull(value, NULL, 10);
+ else if(hash == hash_Icmp6InGroupMembQueries && strcmp(name, "Icmp6InGroupMembQueries") == 0) Icmp6InGroupMembQueries = strtoull(value, NULL, 10);
+ else if(hash == hash_Icmp6InGroupMembResponses && strcmp(name, "Icmp6InGroupMembResponses") == 0) Icmp6InGroupMembResponses = strtoull(value, NULL, 10);
+ else if(hash == hash_Icmp6InGroupMembReductions && strcmp(name, "Icmp6InGroupMembReductions") == 0) Icmp6InGroupMembReductions = strtoull(value, NULL, 10);
+ else if(hash == hash_Icmp6InRouterSolicits && strcmp(name, "Icmp6InRouterSolicits") == 0) Icmp6InRouterSolicits = strtoull(value, NULL, 10);
+ else if(hash == hash_Icmp6InRouterAdvertisements && strcmp(name, "Icmp6InRouterAdvertisements") == 0) Icmp6InRouterAdvertisements = strtoull(value, NULL, 10);
+ else if(hash == hash_Icmp6InNeighborSolicits && strcmp(name, "Icmp6InNeighborSolicits") == 0) Icmp6InNeighborSolicits = strtoull(value, NULL, 10);
+ else if(hash == hash_Icmp6InNeighborAdvertisements && strcmp(name, "Icmp6InNeighborAdvertisements") == 0) Icmp6InNeighborAdvertisements = strtoull(value, NULL, 10);
+ else if(hash == hash_Icmp6InRedirects && strcmp(name, "Icmp6InRedirects") == 0) Icmp6InRedirects = strtoull(value, NULL, 10);
+ else if(hash == hash_Icmp6InMLDv2Reports && strcmp(name, "Icmp6InMLDv2Reports") == 0) Icmp6InMLDv2Reports = strtoull(value, NULL, 10);
+ else if(hash == hash_Icmp6OutDestUnreachs && strcmp(name, "Icmp6OutDestUnreachs") == 0) Icmp6OutDestUnreachs = strtoull(value, NULL, 10);
+ else if(hash == hash_Icmp6OutPktTooBigs && strcmp(name, "Icmp6OutPktTooBigs") == 0) Icmp6OutPktTooBigs = strtoull(value, NULL, 10);
+ else if(hash == hash_Icmp6OutTimeExcds && strcmp(name, "Icmp6OutTimeExcds") == 0) Icmp6OutTimeExcds = strtoull(value, NULL, 10);
+ else if(hash == hash_Icmp6OutParmProblems && strcmp(name, "Icmp6OutParmProblems") == 0) Icmp6OutParmProblems = strtoull(value, NULL, 10);
+ else if(hash == hash_Icmp6OutEchos && strcmp(name, "Icmp6OutEchos") == 0) Icmp6OutEchos = strtoull(value, NULL, 10);
+ else if(hash == hash_Icmp6OutEchoReplies && strcmp(name, "Icmp6OutEchoReplies") == 0) Icmp6OutEchoReplies = strtoull(value, NULL, 10);
+ else if(hash == hash_Icmp6OutGroupMembQueries && strcmp(name, "Icmp6OutGroupMembQueries") == 0) Icmp6OutGroupMembQueries = strtoull(value, NULL, 10);
+ else if(hash == hash_Icmp6OutGroupMembResponses && strcmp(name, "Icmp6OutGroupMembResponses") == 0) Icmp6OutGroupMembResponses = strtoull(value, NULL, 10);
+ else if(hash == hash_Icmp6OutGroupMembReductions && strcmp(name, "Icmp6OutGroupMembReductions") == 0) Icmp6OutGroupMembReductions = strtoull(value, NULL, 10);
+ else if(hash == hash_Icmp6OutRouterSolicits && strcmp(name, "Icmp6OutRouterSolicits") == 0) Icmp6OutRouterSolicits = strtoull(value, NULL, 10);
+ else if(hash == hash_Icmp6OutRouterAdvertisements && strcmp(name, "Icmp6OutRouterAdvertisements") == 0) Icmp6OutRouterAdvertisements = strtoull(value, NULL, 10);
+ else if(hash == hash_Icmp6OutNeighborSolicits && strcmp(name, "Icmp6OutNeighborSolicits") == 0) Icmp6OutNeighborSolicits = strtoull(value, NULL, 10);
+ else if(hash == hash_Icmp6OutNeighborAdvertisements && strcmp(name, "Icmp6OutNeighborAdvertisements") == 0) Icmp6OutNeighborAdvertisements = strtoull(value, NULL, 10);
+ else if(hash == hash_Icmp6OutRedirects && strcmp(name, "Icmp6OutRedirects") == 0) Icmp6OutRedirects = strtoull(value, NULL, 10);
+ else if(hash == hash_Icmp6OutMLDv2Reports && strcmp(name, "Icmp6OutMLDv2Reports") == 0) Icmp6OutMLDv2Reports = strtoull(value, NULL, 10);
+ else if(hash == hash_Icmp6InType1 && strcmp(name, "Icmp6InType1") == 0) Icmp6InType1 = strtoull(value, NULL, 10);
+ else if(hash == hash_Icmp6InType128 && strcmp(name, "Icmp6InType128") == 0) Icmp6InType128 = strtoull(value, NULL, 10);
+ else if(hash == hash_Icmp6InType129 && strcmp(name, "Icmp6InType129") == 0) Icmp6InType129 = strtoull(value, NULL, 10);
+ else if(hash == hash_Icmp6InType136 && strcmp(name, "Icmp6InType136") == 0) Icmp6InType136 = strtoull(value, NULL, 10);
+ else if(hash == hash_Icmp6OutType1 && strcmp(name, "Icmp6OutType1") == 0) Icmp6OutType1 = strtoull(value, NULL, 10);
+ else if(hash == hash_Icmp6OutType128 && strcmp(name, "Icmp6OutType128") == 0) Icmp6OutType128 = strtoull(value, NULL, 10);
+ else if(hash == hash_Icmp6OutType129 && strcmp(name, "Icmp6OutType129") == 0) Icmp6OutType129 = strtoull(value, NULL, 10);
+ else if(hash == hash_Icmp6OutType133 && strcmp(name, "Icmp6OutType133") == 0) Icmp6OutType133 = strtoull(value, NULL, 10);
+ else if(hash == hash_Icmp6OutType135 && strcmp(name, "Icmp6OutType135") == 0) Icmp6OutType135 = strtoull(value, NULL, 10);
+ else if(hash == hash_Icmp6OutType143 && strcmp(name, "Icmp6OutType143") == 0) Icmp6OutType143 = strtoull(value, NULL, 10);
+ else if(hash == hash_Udp6InDatagrams && strcmp(name, "Udp6InDatagrams") == 0) Udp6InDatagrams = strtoull(value, NULL, 10);
+ else if(hash == hash_Udp6NoPorts && strcmp(name, "Udp6NoPorts") == 0) Udp6NoPorts = strtoull(value, NULL, 10);
+ else if(hash == hash_Udp6InErrors && strcmp(name, "Udp6InErrors") == 0) Udp6InErrors = strtoull(value, NULL, 10);
+ else if(hash == hash_Udp6OutDatagrams && strcmp(name, "Udp6OutDatagrams") == 0) Udp6OutDatagrams = strtoull(value, NULL, 10);
+ else if(hash == hash_Udp6RcvbufErrors && strcmp(name, "Udp6RcvbufErrors") == 0) Udp6RcvbufErrors = strtoull(value, NULL, 10);
+ else if(hash == hash_Udp6SndbufErrors && strcmp(name, "Udp6SndbufErrors") == 0) Udp6SndbufErrors = strtoull(value, NULL, 10);
+ else if(hash == hash_Udp6InCsumErrors && strcmp(name, "Udp6InCsumErrors") == 0) Udp6InCsumErrors = strtoull(value, NULL, 10);
+ else if(hash == hash_Udp6IgnoredMulti && strcmp(name, "Udp6IgnoredMulti") == 0) Udp6IgnoredMulti = strtoull(value, NULL, 10);
+ else if(hash == hash_UdpLite6InDatagrams && strcmp(name, "UdpLite6InDatagrams") == 0) UdpLite6InDatagrams = strtoull(value, NULL, 10);
+ else if(hash == hash_UdpLite6NoPorts && strcmp(name, "UdpLite6NoPorts") == 0) UdpLite6NoPorts = strtoull(value, NULL, 10);
+ else if(hash == hash_UdpLite6InErrors && strcmp(name, "UdpLite6InErrors") == 0) UdpLite6InErrors = strtoull(value, NULL, 10);
+ else if(hash == hash_UdpLite6OutDatagrams && strcmp(name, "UdpLite6OutDatagrams") == 0) UdpLite6OutDatagrams = strtoull(value, NULL, 10);
+ else if(hash == hash_UdpLite6RcvbufErrors && strcmp(name, "UdpLite6RcvbufErrors") == 0) UdpLite6RcvbufErrors = strtoull(value, NULL, 10);
+ else if(hash == hash_UdpLite6SndbufErrors && strcmp(name, "UdpLite6SndbufErrors") == 0) UdpLite6SndbufErrors = strtoull(value, NULL, 10);
+ else if(hash == hash_UdpLite6InCsumErrors && strcmp(name, "UdpLite6InCsumErrors") == 0) UdpLite6InCsumErrors = strtoull(value, NULL, 10);
+ }
+
+ RRDSET *st;
+
+ // --------------------------------------------------------------------
+
+ if(do_bandwidth == CONFIG_ONDEMAND_YES || (do_bandwidth == CONFIG_ONDEMAND_ONDEMAND && (Ip6InOctets || Ip6OutOctets))) {
+ do_bandwidth = CONFIG_ONDEMAND_YES;
+ st = rrdset_find("system.ipv6");
+ if(!st) {
+ st = rrdset_create("system", "ipv6", NULL, "network", NULL, "IPv6 Bandwidth", "kilobits/s", 500, update_every, RRDSET_TYPE_AREA);
+
+ rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "sent", Ip6OutOctets);
+ rrddim_set(st, "received", Ip6InOctets);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_ip_packets == CONFIG_ONDEMAND_YES || (do_ip_packets == CONFIG_ONDEMAND_ONDEMAND && (Ip6InReceives || Ip6OutRequests || Ip6InDelivers || Ip6OutForwDatagrams))) {
+ do_ip_packets = CONFIG_ONDEMAND_YES;
+ st = rrdset_find(RRD_TYPE_NET_SNMP6 ".packets");
+ if(!st) {
+ st = rrdset_create(RRD_TYPE_NET_SNMP6, "packets", NULL, "packets", NULL, "IPv6 Packets", "packets/s", 3000, update_every, RRDSET_TYPE_LINE);
+
+ rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "forwarded", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "delivers", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "sent", Ip6OutRequests);
+ rrddim_set(st, "received", Ip6InReceives);
+ rrddim_set(st, "forwarded", Ip6InDelivers);
+ rrddim_set(st, "delivers", Ip6OutForwDatagrams);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_ip_fragsout == CONFIG_ONDEMAND_YES || (do_ip_fragsout == CONFIG_ONDEMAND_ONDEMAND && (Ip6FragOKs || Ip6FragFails || Ip6FragCreates))) {
+ do_ip_fragsout = CONFIG_ONDEMAND_YES;
+ st = rrdset_find(RRD_TYPE_NET_SNMP6 ".fragsout");
+ if(!st) {
+ st = rrdset_create(RRD_TYPE_NET_SNMP6, "fragsout", NULL, "fragments", NULL, "IPv6 Fragments Sent", "packets/s", 3010, update_every, RRDSET_TYPE_LINE);
+ st->isdetail = 1;
+
+ rrddim_add(st, "ok", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "failed", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "all", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "ok", Ip6FragOKs);
+ rrddim_set(st, "failed", Ip6FragFails);
+ rrddim_set(st, "all", Ip6FragCreates);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_ip_fragsin == CONFIG_ONDEMAND_YES || (do_ip_fragsin == CONFIG_ONDEMAND_ONDEMAND
+ && (
+ Ip6ReasmOKs
+ || Ip6ReasmFails
+ || Ip6ReasmTimeout
+ || Ip6ReasmReqds
+ ))) {
+ do_ip_fragsin = CONFIG_ONDEMAND_YES;
+ st = rrdset_find(RRD_TYPE_NET_SNMP6 ".fragsin");
+ if(!st) {
+ st = rrdset_create(RRD_TYPE_NET_SNMP6, "fragsin", NULL, "fragments", NULL, "IPv6 Fragments Reassembly", "packets/s", 3011, update_every, RRDSET_TYPE_LINE);
+ st->isdetail = 1;
+
+ rrddim_add(st, "ok", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "failed", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "timeout", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "all", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "ok", Ip6ReasmOKs);
+ rrddim_set(st, "failed", Ip6ReasmFails);
+ rrddim_set(st, "timeout", Ip6ReasmTimeout);
+ rrddim_set(st, "all", Ip6ReasmReqds);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_ip_errors == CONFIG_ONDEMAND_YES || (do_ip_errors == CONFIG_ONDEMAND_ONDEMAND
+ && (
+ Ip6InDiscards
+ || Ip6OutDiscards
+ || Ip6InHdrErrors
+ || Ip6InAddrErrors
+ || Ip6InUnknownProtos
+ || Ip6InTooBigErrors
+ || Ip6InTruncatedPkts
+ || Ip6InNoRoutes
+ ))) {
+ do_ip_errors = CONFIG_ONDEMAND_YES;
+ st = rrdset_find(RRD_TYPE_NET_SNMP6 ".errors");
+ if(!st) {
+ st = rrdset_create(RRD_TYPE_NET_SNMP6, "errors", NULL, "errors", NULL, "IPv6 Errors", "packets/s", 3002, update_every, RRDSET_TYPE_LINE);
+ st->isdetail = 1;
+
+ rrddim_add(st, "InDiscards", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "OutDiscards", NULL, -1, 1, RRDDIM_INCREMENTAL);
+
+ rrddim_add(st, "InHdrErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "InAddrErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "InUnknownProtos", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "InTooBigErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "InTruncatedPkts", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "InNoRoutes", NULL, 1, 1, RRDDIM_INCREMENTAL);
+
+ rrddim_add(st, "OutNoRoutes", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "InDiscards", Ip6InDiscards);
+ rrddim_set(st, "OutDiscards", Ip6OutDiscards);
+
+ rrddim_set(st, "InHdrErrors", Ip6InHdrErrors);
+ rrddim_set(st, "InAddrErrors", Ip6InAddrErrors);
+ rrddim_set(st, "InUnknownProtos", Ip6InUnknownProtos);
+ rrddim_set(st, "InTooBigErrors", Ip6InTooBigErrors);
+ rrddim_set(st, "InTruncatedPkts", Ip6InTruncatedPkts);
+ rrddim_set(st, "InNoRoutes", Ip6InNoRoutes);
+
+ rrddim_set(st, "OutNoRoutes", Ip6OutNoRoutes);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_udp_packets == CONFIG_ONDEMAND_YES || (do_udp_packets == CONFIG_ONDEMAND_ONDEMAND && (Udp6InDatagrams || Udp6OutDatagrams))) {
+ do_udp_packets = CONFIG_ONDEMAND_YES;
+ st = rrdset_find(RRD_TYPE_NET_SNMP6 ".udppackets");
+ if(!st) {
+ st = rrdset_create(RRD_TYPE_NET_SNMP6, "udppackets", NULL, "udp", NULL, "IPv6 UDP Packets", "packets/s", 3601, update_every, RRDSET_TYPE_LINE);
+
+ rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "received", Udp6InDatagrams);
+ rrddim_set(st, "sent", Udp6OutDatagrams);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_udp_errors == CONFIG_ONDEMAND_YES || (do_udp_errors == CONFIG_ONDEMAND_ONDEMAND
+ && (
+ Udp6InErrors
+ || Udp6NoPorts
+ || Udp6RcvbufErrors
+ || Udp6SndbufErrors
+ || Udp6InCsumErrors
+ || Udp6IgnoredMulti
+ ))) {
+ do_udp_errors = CONFIG_ONDEMAND_YES;
+ st = rrdset_find(RRD_TYPE_NET_SNMP6 ".udperrors");
+ if(!st) {
+ st = rrdset_create(RRD_TYPE_NET_SNMP6, "udperrors", NULL, "udp", NULL, "IPv6 UDP Errors", "events/s", 3701, update_every, RRDSET_TYPE_LINE);
+ st->isdetail = 1;
+
+ rrddim_add(st, "RcvbufErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "SndbufErrors", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "InErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "NoPorts", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "IgnoredMulti", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "InErrors", Udp6InErrors);
+ rrddim_set(st, "NoPorts", Udp6NoPorts);
+ rrddim_set(st, "RcvbufErrors", Udp6RcvbufErrors);
+ rrddim_set(st, "SndbufErrors", Udp6SndbufErrors);
+ rrddim_set(st, "InCsumErrors", Udp6InCsumErrors);
+ rrddim_set(st, "IgnoredMulti", Udp6IgnoredMulti);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_udplite_packets == CONFIG_ONDEMAND_YES || (do_udplite_packets == CONFIG_ONDEMAND_ONDEMAND && (UdpLite6InDatagrams || UdpLite6OutDatagrams))) {
+ do_udplite_packets = CONFIG_ONDEMAND_YES;
+ st = rrdset_find(RRD_TYPE_NET_SNMP6 ".udplitepackets");
+ if(!st) {
+ st = rrdset_create(RRD_TYPE_NET_SNMP6, "udplitepackets", NULL, "udplite", NULL, "IPv6 UDPlite Packets", "packets/s", 3601, update_every, RRDSET_TYPE_LINE);
+
+ rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "received", UdpLite6InDatagrams);
+ rrddim_set(st, "sent", UdpLite6OutDatagrams);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_udplite_errors == CONFIG_ONDEMAND_YES || (do_udplite_errors == CONFIG_ONDEMAND_ONDEMAND
+ && (
+ UdpLite6InErrors
+ || UdpLite6NoPorts
+ || UdpLite6RcvbufErrors
+ || UdpLite6SndbufErrors
+ || Udp6InCsumErrors
+ || UdpLite6InCsumErrors
+ ))) {
+ do_udplite_errors = CONFIG_ONDEMAND_YES;
+ st = rrdset_find(RRD_TYPE_NET_SNMP6 ".udpliteerrors");
+ if(!st) {
+ st = rrdset_create(RRD_TYPE_NET_SNMP6, "udpliteerrors", NULL, "udplite", NULL, "IPv6 UDP Lite Errors", "events/s", 3701, update_every, RRDSET_TYPE_LINE);
+ st->isdetail = 1;
+
+ rrddim_add(st, "RcvbufErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "SndbufErrors", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "InErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "NoPorts", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "InErrors", UdpLite6InErrors);
+ rrddim_set(st, "NoPorts", UdpLite6NoPorts);
+ rrddim_set(st, "RcvbufErrors", UdpLite6RcvbufErrors);
+ rrddim_set(st, "SndbufErrors", UdpLite6SndbufErrors);
+ rrddim_set(st, "InCsumErrors", UdpLite6InCsumErrors);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_mcast == CONFIG_ONDEMAND_YES || (do_mcast == CONFIG_ONDEMAND_ONDEMAND && (Ip6OutMcastOctets || Ip6InMcastOctets))) {
+ do_mcast = CONFIG_ONDEMAND_YES;
+ st = rrdset_find(RRD_TYPE_NET_SNMP6 ".mcast");
+ if(!st) {
+ st = rrdset_create(RRD_TYPE_NET_SNMP6, "mcast", NULL, "multicast", NULL, "IPv6 Multicast Bandwidth", "kilobits/s", 9000, update_every, RRDSET_TYPE_AREA);
+ st->isdetail = 1;
+
+ rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "sent", Ip6OutMcastOctets);
+ rrddim_set(st, "received", Ip6InMcastOctets);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_bcast == CONFIG_ONDEMAND_YES || (do_bcast == CONFIG_ONDEMAND_ONDEMAND && (Ip6OutBcastOctets || Ip6InBcastOctets))) {
+ do_bcast = CONFIG_ONDEMAND_YES;
+ st = rrdset_find(RRD_TYPE_NET_SNMP6 ".bcast");
+ if(!st) {
+ st = rrdset_create(RRD_TYPE_NET_SNMP6, "bcast", NULL, "broadcast", NULL, "IPv6 Broadcast Bandwidth", "kilobits/s", 8000, update_every, RRDSET_TYPE_AREA);
+ st->isdetail = 1;
+
+ rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "sent", Ip6OutBcastOctets);
+ rrddim_set(st, "received", Ip6InBcastOctets);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_mcast_p == CONFIG_ONDEMAND_YES || (do_mcast_p == CONFIG_ONDEMAND_ONDEMAND && (Ip6OutMcastPkts || Ip6InMcastPkts))) {
+ do_mcast_p = CONFIG_ONDEMAND_YES;
+ st = rrdset_find(RRD_TYPE_NET_SNMP6 ".mcastpkts");
+ if(!st) {
+ st = rrdset_create(RRD_TYPE_NET_SNMP6, "mcastpkts", NULL, "multicast", NULL, "IPv6 Multicast Packets", "packets/s", 9500, update_every, RRDSET_TYPE_LINE);
+ st->isdetail = 1;
+
+ rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "sent", Ip6OutMcastPkts);
+ rrddim_set(st, "received", Ip6InMcastPkts);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_icmp == CONFIG_ONDEMAND_YES || (do_icmp == CONFIG_ONDEMAND_ONDEMAND && (Icmp6InMsgs || Icmp6OutMsgs))) {
+ do_icmp = CONFIG_ONDEMAND_YES;
+ st = rrdset_find(RRD_TYPE_NET_SNMP6 ".icmp");
+ if(!st) {
+ st = rrdset_create(RRD_TYPE_NET_SNMP6, "icmp", NULL, "icmp", NULL, "IPv6 ICMP Messages", "messages/s", 10000, update_every, RRDSET_TYPE_LINE);
+
+ rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "sent", Icmp6InMsgs);
+ rrddim_set(st, "received", Icmp6OutMsgs);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_icmp_redir == CONFIG_ONDEMAND_YES || (do_icmp_redir == CONFIG_ONDEMAND_ONDEMAND && (Icmp6InRedirects || Icmp6OutRedirects))) {
+ do_icmp_redir = CONFIG_ONDEMAND_YES;
+ st = rrdset_find(RRD_TYPE_NET_SNMP6 ".icmpredir");
+ if(!st) {
+ st = rrdset_create(RRD_TYPE_NET_SNMP6, "icmpredir", NULL, "icmp", NULL, "IPv6 ICMP Redirects", "redirects/s", 10050, update_every, RRDSET_TYPE_LINE);
+
+ rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "sent", Icmp6InRedirects);
+ rrddim_set(st, "received", Icmp6OutRedirects);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_icmp_errors == CONFIG_ONDEMAND_YES || (do_icmp_errors == CONFIG_ONDEMAND_ONDEMAND
+ && (
+ Icmp6InErrors
+ || Icmp6OutErrors
+ || Icmp6InCsumErrors
+ || Icmp6InDestUnreachs
+ || Icmp6InPktTooBigs
+ || Icmp6InTimeExcds
+ || Icmp6InParmProblems
+ || Icmp6OutDestUnreachs
+ || Icmp6OutPktTooBigs
+ || Icmp6OutTimeExcds
+ || Icmp6OutParmProblems
+ ))) {
+ do_icmp_errors = CONFIG_ONDEMAND_YES;
+ st = rrdset_find(RRD_TYPE_NET_SNMP6 ".icmperrors");
+ if(!st) {
+ st = rrdset_create(RRD_TYPE_NET_SNMP6, "icmperrors", NULL, "icmp", NULL, "IPv6 ICMP Errors", "errors/s", 10100, update_every, RRDSET_TYPE_LINE);
+
+ rrddim_add(st, "InErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "OutErrors", NULL, -1, 1, RRDDIM_INCREMENTAL);
+
+ rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "InDestUnreachs", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "InPktTooBigs", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "InTimeExcds", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "InParmProblems", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "OutDestUnreachs", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "OutPktTooBigs", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "OutTimeExcds", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "OutParmProblems", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "InErrors", Icmp6InErrors);
+ rrddim_set(st, "OutErrors", Icmp6OutErrors);
+ rrddim_set(st, "InCsumErrors", Icmp6InCsumErrors);
+ rrddim_set(st, "InDestUnreachs", Icmp6InDestUnreachs);
+ rrddim_set(st, "InPktTooBigs", Icmp6InPktTooBigs);
+ rrddim_set(st, "InTimeExcds", Icmp6InTimeExcds);
+ rrddim_set(st, "InParmProblems", Icmp6InParmProblems);
+ rrddim_set(st, "OutDestUnreachs", Icmp6OutDestUnreachs);
+ rrddim_set(st, "OutPktTooBigs", Icmp6OutPktTooBigs);
+ rrddim_set(st, "OutTimeExcds", Icmp6OutTimeExcds);
+ rrddim_set(st, "OutParmProblems", Icmp6OutParmProblems);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_icmp_echos == CONFIG_ONDEMAND_YES || (do_icmp_echos == CONFIG_ONDEMAND_ONDEMAND
+ && (
+ Icmp6InEchos
+ || Icmp6OutEchos
+ || Icmp6InEchoReplies
+ || Icmp6OutEchoReplies
+ ))) {
+ do_icmp_echos = CONFIG_ONDEMAND_YES;
+ st = rrdset_find(RRD_TYPE_NET_SNMP6 ".icmpechos");
+ if(!st) {
+ st = rrdset_create(RRD_TYPE_NET_SNMP6, "icmpechos", NULL, "icmp", NULL, "IPv6 ICMP Echo", "messages/s", 10200, update_every, RRDSET_TYPE_LINE);
+
+ rrddim_add(st, "InEchos", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "OutEchos", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "InEchoReplies", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "OutEchoReplies", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "InEchos", Icmp6InEchos);
+ rrddim_set(st, "OutEchos", Icmp6OutEchos);
+ rrddim_set(st, "InEchoReplies", Icmp6InEchoReplies);
+ rrddim_set(st, "OutEchoReplies", Icmp6OutEchoReplies);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_icmp_groupmemb == CONFIG_ONDEMAND_YES || (do_icmp_groupmemb == CONFIG_ONDEMAND_ONDEMAND
+ && (
+ Icmp6InGroupMembQueries
+ || Icmp6OutGroupMembQueries
+ || Icmp6InGroupMembResponses
+ || Icmp6OutGroupMembResponses
+ || Icmp6InGroupMembReductions
+ || Icmp6OutGroupMembReductions
+ ))) {
+ do_icmp_groupmemb = CONFIG_ONDEMAND_YES;
+ st = rrdset_find(RRD_TYPE_NET_SNMP6 ".groupmemb");
+ if(!st) {
+ st = rrdset_create(RRD_TYPE_NET_SNMP6, "groupmemb", NULL, "icmp", NULL, "IPv6 ICMP Group Membership", "messages/s", 10300, update_every, RRDSET_TYPE_LINE);
+
+ rrddim_add(st, "InQueries", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "OutQueries", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "InResponses", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "OutResponses", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "InReductions", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "OutReductions", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "InQueries", Icmp6InGroupMembQueries);
+ rrddim_set(st, "OutQueries", Icmp6OutGroupMembQueries);
+ rrddim_set(st, "InResponses", Icmp6InGroupMembResponses);
+ rrddim_set(st, "OutResponses", Icmp6OutGroupMembResponses);
+ rrddim_set(st, "InReductions", Icmp6InGroupMembReductions);
+ rrddim_set(st, "OutReductions", Icmp6OutGroupMembReductions);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_icmp_router == CONFIG_ONDEMAND_YES || (do_icmp_router == CONFIG_ONDEMAND_ONDEMAND
+ && (
+ Icmp6InRouterSolicits
+ || Icmp6OutRouterSolicits
+ || Icmp6InRouterAdvertisements
+ || Icmp6OutRouterAdvertisements
+ ))) {
+ do_icmp_router = CONFIG_ONDEMAND_YES;
+ st = rrdset_find(RRD_TYPE_NET_SNMP6 ".icmprouter");
+ if(!st) {
+ st = rrdset_create(RRD_TYPE_NET_SNMP6, "icmprouter", NULL, "icmp", NULL, "IPv6 Router Messages", "messages/s", 10400, update_every, RRDSET_TYPE_LINE);
+
+ rrddim_add(st, "InSolicits", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "OutSolicits", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "InAdvertisements", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "OutAdvertisements", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "InSolicits", Icmp6InRouterSolicits);
+ rrddim_set(st, "OutSolicits", Icmp6OutRouterSolicits);
+ rrddim_set(st, "InAdvertisements", Icmp6InRouterAdvertisements);
+ rrddim_set(st, "OutAdvertisements", Icmp6OutRouterAdvertisements);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_icmp_neighbor == CONFIG_ONDEMAND_YES || (do_icmp_neighbor == CONFIG_ONDEMAND_ONDEMAND
+ && (
+ Icmp6InNeighborSolicits
+ || Icmp6OutNeighborSolicits
+ || Icmp6InNeighborAdvertisements
+ || Icmp6OutNeighborAdvertisements
+ ))) {
+ do_icmp_neighbor = CONFIG_ONDEMAND_YES;
+ st = rrdset_find(RRD_TYPE_NET_SNMP6 ".icmpneighbor");
+ if(!st) {
+ st = rrdset_create(RRD_TYPE_NET_SNMP6, "icmpneighbor", NULL, "icmp", NULL, "IPv6 Neighbor Messages", "messages/s", 10500, update_every, RRDSET_TYPE_LINE);
+
+ rrddim_add(st, "InSolicits", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "OutSolicits", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "InAdvertisements", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "OutAdvertisements", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "InSolicits", Icmp6InNeighborSolicits);
+ rrddim_set(st, "OutSolicits", Icmp6OutNeighborSolicits);
+ rrddim_set(st, "InAdvertisements", Icmp6InNeighborAdvertisements);
+ rrddim_set(st, "OutAdvertisements", Icmp6OutNeighborAdvertisements);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_icmp_mldv2 == CONFIG_ONDEMAND_YES || (do_icmp_mldv2 == CONFIG_ONDEMAND_ONDEMAND && (Icmp6InMLDv2Reports || Icmp6OutMLDv2Reports))) {
+ do_icmp_mldv2 = CONFIG_ONDEMAND_YES;
+ st = rrdset_find(RRD_TYPE_NET_SNMP6 ".icmpmldv2");
+ if(!st) {
+ st = rrdset_create(RRD_TYPE_NET_SNMP6, "icmpmldv2", NULL, "icmp", NULL, "IPv6 ICMP MLDv2 Reports", "reports/s", 10600, update_every, RRDSET_TYPE_LINE);
+
+ rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "sent", Icmp6InMLDv2Reports);
+ rrddim_set(st, "received", Icmp6OutMLDv2Reports);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_icmp_types == CONFIG_ONDEMAND_YES || (do_icmp_types == CONFIG_ONDEMAND_ONDEMAND
+ && (
+ Icmp6InType1
+ || Icmp6InType128
+ || Icmp6InType129
+ || Icmp6InType136
+ || Icmp6OutType1
+ || Icmp6OutType128
+ || Icmp6OutType129
+ || Icmp6OutType133
+ || Icmp6OutType135
+ || Icmp6OutType143
+ ))) {
+ do_icmp_types = CONFIG_ONDEMAND_YES;
+ st = rrdset_find(RRD_TYPE_NET_SNMP6 ".icmptypes");
+ if(!st) {
+ st = rrdset_create(RRD_TYPE_NET_SNMP6, "icmptypes", NULL, "icmp", NULL, "IPv6 ICMP Types", "messages/s", 10700, update_every, RRDSET_TYPE_LINE);
+
+ rrddim_add(st, "InType1", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "InType128", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "InType129", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "InType136", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "OutType1", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "OutType128", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "OutType129", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "OutType133", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "OutType135", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "OutType143", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "InType1", Icmp6InType1);
+ rrddim_set(st, "InType128", Icmp6InType128);
+ rrddim_set(st, "InType129", Icmp6InType129);
+ rrddim_set(st, "InType136", Icmp6InType136);
+ rrddim_set(st, "OutType1", Icmp6OutType1);
+ rrddim_set(st, "OutType128", Icmp6OutType128);
+ rrddim_set(st, "OutType129", Icmp6OutType129);
+ rrddim_set(st, "OutType133", Icmp6OutType133);
+ rrddim_set(st, "OutType135", Icmp6OutType135);
+ rrddim_set(st, "OutType143", Icmp6OutType143);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_ect == CONFIG_ONDEMAND_YES || (do_ect == CONFIG_ONDEMAND_ONDEMAND
+ && (
+ Ip6InNoECTPkts
+ || Ip6InECT1Pkts
+ || Ip6InECT0Pkts
+ || Ip6InCEPkts
+ ))) {
+ do_ect = CONFIG_ONDEMAND_YES;
+ st = rrdset_find(RRD_TYPE_NET_SNMP6 ".ect");
+ if(!st) {
+ st = rrdset_create(RRD_TYPE_NET_SNMP6, "ect", NULL, "packets", NULL, "IPv6 ECT Packets", "packets/s", 10800, update_every, RRDSET_TYPE_LINE);
+
+ rrddim_add(st, "InNoECTPkts", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "InECT1Pkts", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "InECT0Pkts", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "InCEPkts", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "InNoECTPkts", Ip6InNoECTPkts);
+ rrddim_set(st, "InECT1Pkts", Ip6InECT1Pkts);
+ rrddim_set(st, "InECT0Pkts", Ip6InECT0Pkts);
+ rrddim_set(st, "InCEPkts", Ip6InCEPkts);
+ rrdset_done(st);
+ }
+
+ return 0;
}
diff --git a/src/proc_net_stat_conntrack.c b/src/proc_net_stat_conntrack.c
index 7d754a1d9..8234b20d2 100644
--- a/src/proc_net_stat_conntrack.c
+++ b/src/proc_net_stat_conntrack.c
@@ -1,215 +1,203 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
#include "common.h"
-#include "log.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "rrd.h"
-#include "plugin_proc.h"
-#define RRD_TYPE_NET_STAT_NETFILTER "netfilter"
-#define RRD_TYPE_NET_STAT_CONNTRACK "conntrack"
-#define RRD_TYPE_NET_STAT_CONNTRACK_LEN strlen(RRD_TYPE_NET_STAT_CONNTRACK)
+#define RRD_TYPE_NET_STAT_NETFILTER "netfilter"
+#define RRD_TYPE_NET_STAT_CONNTRACK "conntrack"
+#define RRD_TYPE_NET_STAT_CONNTRACK_LEN strlen(RRD_TYPE_NET_STAT_CONNTRACK)
int do_proc_net_stat_conntrack(int update_every, unsigned long long dt) {
- static procfile *ff = NULL;
- static int do_sockets = -1, do_new = -1, do_changes = -1, do_expect = -1, do_search = -1, do_errors = -1;
-
- if(do_sockets == -1) do_sockets = config_get_boolean("plugin:proc:/proc/net/stat/nf_conntrack", "netfilter connections", 1);
- if(do_new == -1) do_new = config_get_boolean("plugin:proc:/proc/net/stat/nf_conntrack", "netfilter new connections", 1);
- if(do_changes == -1) do_changes = config_get_boolean("plugin:proc:/proc/net/stat/nf_conntrack", "netfilter connection changes", 1);
- if(do_expect == -1) do_expect = config_get_boolean("plugin:proc:/proc/net/stat/nf_conntrack", "netfilter connection expectations", 1);
- if(do_search == -1) do_search = config_get_boolean("plugin:proc:/proc/net/stat/nf_conntrack", "netfilter connection searches", 1);
- if(do_errors == -1) do_errors = config_get_boolean("plugin:proc:/proc/net/stat/nf_conntrack", "netfilter errors", 1);
-
- if(dt) {};
-
- if(!ff) {
- char filename[FILENAME_MAX + 1];
- snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/net/stat/nf_conntrack");
- ff = procfile_open(config_get("plugin:proc:/proc/net/stat/nf_conntrack", "filename to monitor", filename), " \t:", PROCFILE_FLAG_DEFAULT);
- }
- if(!ff) return 1;
-
- ff = procfile_readall(ff);
- if(!ff) return 0; // we return 0, so that we will retry to open it next time
-
- uint32_t lines = procfile_lines(ff), l;
- uint32_t words;
-
- unsigned long long aentries = 0, asearched = 0, afound = 0, anew = 0, ainvalid = 0, aignore = 0, adelete = 0, adelete_list = 0,
- ainsert = 0, ainsert_failed = 0, adrop = 0, aearly_drop = 0, aicmp_error = 0, aexpect_new = 0, aexpect_create = 0, aexpect_delete = 0, asearch_restart = 0;
-
- for(l = 1; l < lines ;l++) {
- words = procfile_linewords(ff, l);
- if(words < 17) {
- if(words) error("Cannot read /proc/net/stat/nf_conntrack line. Expected 17 params, read %d.", words);
- continue;
- }
-
- unsigned long long tentries = 0, tsearched = 0, tfound = 0, tnew = 0, tinvalid = 0, tignore = 0, tdelete = 0, tdelete_list = 0, tinsert = 0, tinsert_failed = 0, tdrop = 0, tearly_drop = 0, ticmp_error = 0, texpect_new = 0, texpect_create = 0, texpect_delete = 0, tsearch_restart = 0;
-
- tentries = strtoull(procfile_lineword(ff, l, 0), NULL, 16);
- tsearched = strtoull(procfile_lineword(ff, l, 1), NULL, 16);
- tfound = strtoull(procfile_lineword(ff, l, 2), NULL, 16);
- tnew = strtoull(procfile_lineword(ff, l, 3), NULL, 16);
- tinvalid = strtoull(procfile_lineword(ff, l, 4), NULL, 16);
- tignore = strtoull(procfile_lineword(ff, l, 5), NULL, 16);
- tdelete = strtoull(procfile_lineword(ff, l, 6), NULL, 16);
- tdelete_list = strtoull(procfile_lineword(ff, l, 7), NULL, 16);
- tinsert = strtoull(procfile_lineword(ff, l, 8), NULL, 16);
- tinsert_failed = strtoull(procfile_lineword(ff, l, 9), NULL, 16);
- tdrop = strtoull(procfile_lineword(ff, l, 10), NULL, 16);
- tearly_drop = strtoull(procfile_lineword(ff, l, 11), NULL, 16);
- ticmp_error = strtoull(procfile_lineword(ff, l, 12), NULL, 16);
- texpect_new = strtoull(procfile_lineword(ff, l, 13), NULL, 16);
- texpect_create = strtoull(procfile_lineword(ff, l, 14), NULL, 16);
- texpect_delete = strtoull(procfile_lineword(ff, l, 15), NULL, 16);
- tsearch_restart = strtoull(procfile_lineword(ff, l, 16), NULL, 16);
-
- if(!aentries) aentries = tentries;
-
- // sum all the cpus together
- asearched += tsearched; // conntrack.search
- afound += tfound; // conntrack.search
- anew += tnew; // conntrack.new
- ainvalid += tinvalid; // conntrack.new
- aignore += tignore; // conntrack.new
- adelete += tdelete; // conntrack.changes
- adelete_list += tdelete_list; // conntrack.changes
- ainsert += tinsert; // conntrack.changes
- ainsert_failed += tinsert_failed; // conntrack.errors
- adrop += tdrop; // conntrack.errors
- aearly_drop += tearly_drop; // conntrack.errors
- aicmp_error += ticmp_error; // conntrack.errors
- aexpect_new += texpect_new; // conntrack.expect
- aexpect_create += texpect_create; // conntrack.expect
- aexpect_delete += texpect_delete; // conntrack.expect
- asearch_restart += tsearch_restart; // conntrack.search
- }
-
- RRDSET *st;
-
- // --------------------------------------------------------------------
-
- if(do_sockets) {
- st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_sockets");
- if(!st) {
- st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_sockets", NULL, RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker Connections", "active connections", 1000, update_every, RRDSET_TYPE_LINE);
-
- rrddim_add(st, "connections", NULL, 1, 1, RRDDIM_ABSOLUTE);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "connections", aentries);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(do_new) {
- st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_new");
- if(!st) {
- st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_new", NULL, RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker New Connections", "connections/s", 1001, update_every, RRDSET_TYPE_LINE);
-
- rrddim_add(st, "new", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "ignore", NULL, -1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "invalid", NULL, -1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "new", anew);
- rrddim_set(st, "ignore", aignore);
- rrddim_set(st, "invalid", ainvalid);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(do_changes) {
- st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_changes");
- if(!st) {
- st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_changes", NULL, RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker Changes", "changes/s", 1002, update_every, RRDSET_TYPE_LINE);
- st->isdetail = 1;
-
- rrddim_add(st, "inserted", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "deleted", NULL, -1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "delete_list", NULL, -1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "inserted", ainsert);
- rrddim_set(st, "deleted", adelete);
- rrddim_set(st, "delete_list", adelete_list);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(do_expect) {
- st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_expect");
- if(!st) {
- st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_expect", NULL, RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker Expectations", "expectations/s", 1003, update_every, RRDSET_TYPE_LINE);
- st->isdetail = 1;
-
- rrddim_add(st, "created", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "deleted", NULL, -1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "new", NULL, 1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "created", aexpect_create);
- rrddim_set(st, "deleted", aexpect_delete);
- rrddim_set(st, "new", aexpect_new);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(do_search) {
- st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_search");
- if(!st) {
- st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_search", NULL, RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker Searches", "searches/s", 1010, update_every, RRDSET_TYPE_LINE);
- st->isdetail = 1;
-
- rrddim_add(st, "searched", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "restarted", NULL, -1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "found", NULL, 1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "searched", asearched);
- rrddim_set(st, "restarted", asearch_restart);
- rrddim_set(st, "found", afound);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(do_errors) {
- st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_errors");
- if(!st) {
- st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_errors", NULL, RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker Errors", "events/s", 1005, update_every, RRDSET_TYPE_LINE);
- st->isdetail = 1;
-
- rrddim_add(st, "icmp_error", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "insert_failed", NULL, -1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "drop", NULL, -1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "early_drop", NULL, -1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "icmp_error", aicmp_error);
- rrddim_set(st, "insert_failed", ainsert_failed);
- rrddim_set(st, "drop", adrop);
- rrddim_set(st, "early_drop", aearly_drop);
- rrdset_done(st);
- }
-
- return 0;
+ static procfile *ff = NULL;
+ static int do_sockets = -1, do_new = -1, do_changes = -1, do_expect = -1, do_search = -1, do_errors = -1;
+
+ if(do_sockets == -1) do_sockets = config_get_boolean("plugin:proc:/proc/net/stat/nf_conntrack", "netfilter connections", 1);
+ if(do_new == -1) do_new = config_get_boolean("plugin:proc:/proc/net/stat/nf_conntrack", "netfilter new connections", 1);
+ if(do_changes == -1) do_changes = config_get_boolean("plugin:proc:/proc/net/stat/nf_conntrack", "netfilter connection changes", 1);
+ if(do_expect == -1) do_expect = config_get_boolean("plugin:proc:/proc/net/stat/nf_conntrack", "netfilter connection expectations", 1);
+ if(do_search == -1) do_search = config_get_boolean("plugin:proc:/proc/net/stat/nf_conntrack", "netfilter connection searches", 1);
+ if(do_errors == -1) do_errors = config_get_boolean("plugin:proc:/proc/net/stat/nf_conntrack", "netfilter errors", 1);
+
+ if(dt) {};
+
+ if(!ff) {
+ char filename[FILENAME_MAX + 1];
+ snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/net/stat/nf_conntrack");
+ ff = procfile_open(config_get("plugin:proc:/proc/net/stat/nf_conntrack", "filename to monitor", filename), " \t:", PROCFILE_FLAG_DEFAULT);
+ }
+ if(!ff) return 1;
+
+ ff = procfile_readall(ff);
+ if(!ff) return 0; // we return 0, so that we will retry to open it next time
+
+ uint32_t lines = procfile_lines(ff), l;
+ uint32_t words;
+
+ unsigned long long aentries = 0, asearched = 0, afound = 0, anew = 0, ainvalid = 0, aignore = 0, adelete = 0, adelete_list = 0,
+ ainsert = 0, ainsert_failed = 0, adrop = 0, aearly_drop = 0, aicmp_error = 0, aexpect_new = 0, aexpect_create = 0, aexpect_delete = 0, asearch_restart = 0;
+
+ for(l = 1; l < lines ;l++) {
+ words = procfile_linewords(ff, l);
+ if(words < 17) {
+ if(words) error("Cannot read /proc/net/stat/nf_conntrack line. Expected 17 params, read %u.", words);
+ continue;
+ }
+
+ unsigned long long tentries = 0, tsearched = 0, tfound = 0, tnew = 0, tinvalid = 0, tignore = 0, tdelete = 0, tdelete_list = 0, tinsert = 0, tinsert_failed = 0, tdrop = 0, tearly_drop = 0, ticmp_error = 0, texpect_new = 0, texpect_create = 0, texpect_delete = 0, tsearch_restart = 0;
+
+ tentries = strtoull(procfile_lineword(ff, l, 0), NULL, 16);
+ tsearched = strtoull(procfile_lineword(ff, l, 1), NULL, 16);
+ tfound = strtoull(procfile_lineword(ff, l, 2), NULL, 16);
+ tnew = strtoull(procfile_lineword(ff, l, 3), NULL, 16);
+ tinvalid = strtoull(procfile_lineword(ff, l, 4), NULL, 16);
+ tignore = strtoull(procfile_lineword(ff, l, 5), NULL, 16);
+ tdelete = strtoull(procfile_lineword(ff, l, 6), NULL, 16);
+ tdelete_list = strtoull(procfile_lineword(ff, l, 7), NULL, 16);
+ tinsert = strtoull(procfile_lineword(ff, l, 8), NULL, 16);
+ tinsert_failed = strtoull(procfile_lineword(ff, l, 9), NULL, 16);
+ tdrop = strtoull(procfile_lineword(ff, l, 10), NULL, 16);
+ tearly_drop = strtoull(procfile_lineword(ff, l, 11), NULL, 16);
+ ticmp_error = strtoull(procfile_lineword(ff, l, 12), NULL, 16);
+ texpect_new = strtoull(procfile_lineword(ff, l, 13), NULL, 16);
+ texpect_create = strtoull(procfile_lineword(ff, l, 14), NULL, 16);
+ texpect_delete = strtoull(procfile_lineword(ff, l, 15), NULL, 16);
+ tsearch_restart = strtoull(procfile_lineword(ff, l, 16), NULL, 16);
+
+ if(!aentries) aentries = tentries;
+
+ // sum all the cpus together
+ asearched += tsearched; // conntrack.search
+ afound += tfound; // conntrack.search
+ anew += tnew; // conntrack.new
+ ainvalid += tinvalid; // conntrack.new
+ aignore += tignore; // conntrack.new
+ adelete += tdelete; // conntrack.changes
+ adelete_list += tdelete_list; // conntrack.changes
+ ainsert += tinsert; // conntrack.changes
+ ainsert_failed += tinsert_failed; // conntrack.errors
+ adrop += tdrop; // conntrack.errors
+ aearly_drop += tearly_drop; // conntrack.errors
+ aicmp_error += ticmp_error; // conntrack.errors
+ aexpect_new += texpect_new; // conntrack.expect
+ aexpect_create += texpect_create; // conntrack.expect
+ aexpect_delete += texpect_delete; // conntrack.expect
+ asearch_restart += tsearch_restart; // conntrack.search
+ }
+
+ RRDSET *st;
+
+ // --------------------------------------------------------------------
+
+ if(do_sockets) {
+ st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_sockets");
+ if(!st) {
+ st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_sockets", NULL, RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker Connections", "active connections", 1000, update_every, RRDSET_TYPE_LINE);
+
+ rrddim_add(st, "connections", NULL, 1, 1, RRDDIM_ABSOLUTE);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "connections", aentries);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_new) {
+ st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_new");
+ if(!st) {
+ st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_new", NULL, RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker New Connections", "connections/s", 1001, update_every, RRDSET_TYPE_LINE);
+
+ rrddim_add(st, "new", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "ignore", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "invalid", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "new", anew);
+ rrddim_set(st, "ignore", aignore);
+ rrddim_set(st, "invalid", ainvalid);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_changes) {
+ st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_changes");
+ if(!st) {
+ st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_changes", NULL, RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker Changes", "changes/s", 1002, update_every, RRDSET_TYPE_LINE);
+ st->isdetail = 1;
+
+ rrddim_add(st, "inserted", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "deleted", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "delete_list", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "inserted", ainsert);
+ rrddim_set(st, "deleted", adelete);
+ rrddim_set(st, "delete_list", adelete_list);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_expect) {
+ st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_expect");
+ if(!st) {
+ st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_expect", NULL, RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker Expectations", "expectations/s", 1003, update_every, RRDSET_TYPE_LINE);
+ st->isdetail = 1;
+
+ rrddim_add(st, "created", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "deleted", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "new", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "created", aexpect_create);
+ rrddim_set(st, "deleted", aexpect_delete);
+ rrddim_set(st, "new", aexpect_new);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_search) {
+ st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_search");
+ if(!st) {
+ st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_search", NULL, RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker Searches", "searches/s", 1010, update_every, RRDSET_TYPE_LINE);
+ st->isdetail = 1;
+
+ rrddim_add(st, "searched", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "restarted", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "found", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "searched", asearched);
+ rrddim_set(st, "restarted", asearch_restart);
+ rrddim_set(st, "found", afound);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_errors) {
+ st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_errors");
+ if(!st) {
+ st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_errors", NULL, RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker Errors", "events/s", 1005, update_every, RRDSET_TYPE_LINE);
+ st->isdetail = 1;
+
+ rrddim_add(st, "icmp_error", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "insert_failed", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "drop", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "early_drop", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "icmp_error", aicmp_error);
+ rrddim_set(st, "insert_failed", ainsert_failed);
+ rrddim_set(st, "drop", adrop);
+ rrddim_set(st, "early_drop", aearly_drop);
+ rrdset_done(st);
+ }
+
+ return 0;
}
diff --git a/src/proc_net_stat_synproxy.c b/src/proc_net_stat_synproxy.c
index 508b7d3b4..758c35dee 100644
--- a/src/proc_net_stat_synproxy.c
+++ b/src/proc_net_stat_synproxy.c
@@ -1,138 +1,127 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-
#include "common.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "rrd.h"
-#include "plugin_proc.h"
-#include "log.h"
-#define RRD_TYPE_NET_STAT_NETFILTER "netfilter"
-#define RRD_TYPE_NET_STAT_SYNPROXY "synproxy"
-#define RRD_TYPE_NET_STAT_SYNPROXY_LEN strlen(RRD_TYPE_NET_STAT_SYNPROXY)
+#define RRD_TYPE_NET_STAT_NETFILTER "netfilter"
+#define RRD_TYPE_NET_STAT_SYNPROXY "synproxy"
+#define RRD_TYPE_NET_STAT_SYNPROXY_LEN strlen(RRD_TYPE_NET_STAT_SYNPROXY)
int do_proc_net_stat_synproxy(int update_every, unsigned long long dt) {
- static int do_entries = -1, do_cookies = -1, do_syns = -1, do_reopened = -1;
- static procfile *ff = NULL;
+ static int do_entries = -1, do_cookies = -1, do_syns = -1, do_reopened = -1;
+ static procfile *ff = NULL;
- if(do_entries == -1) do_entries = config_get_boolean_ondemand("plugin:proc:/proc/net/stat/synproxy", "SYNPROXY entries", CONFIG_ONDEMAND_ONDEMAND);
- if(do_cookies == -1) do_cookies = config_get_boolean_ondemand("plugin:proc:/proc/net/stat/synproxy", "SYNPROXY cookies", CONFIG_ONDEMAND_ONDEMAND);
- if(do_syns == -1) do_syns = config_get_boolean_ondemand("plugin:proc:/proc/net/stat/synproxy", "SYNPROXY SYN received", CONFIG_ONDEMAND_ONDEMAND);
- if(do_reopened == -1) do_reopened = config_get_boolean_ondemand("plugin:proc:/proc/net/stat/synproxy", "SYNPROXY connections reopened", CONFIG_ONDEMAND_ONDEMAND);
+ if(do_entries == -1) do_entries = config_get_boolean_ondemand("plugin:proc:/proc/net/stat/synproxy", "SYNPROXY entries", CONFIG_ONDEMAND_ONDEMAND);
+ if(do_cookies == -1) do_cookies = config_get_boolean_ondemand("plugin:proc:/proc/net/stat/synproxy", "SYNPROXY cookies", CONFIG_ONDEMAND_ONDEMAND);
+ if(do_syns == -1) do_syns = config_get_boolean_ondemand("plugin:proc:/proc/net/stat/synproxy", "SYNPROXY SYN received", CONFIG_ONDEMAND_ONDEMAND);
+ if(do_reopened == -1) do_reopened = config_get_boolean_ondemand("plugin:proc:/proc/net/stat/synproxy", "SYNPROXY connections reopened", CONFIG_ONDEMAND_ONDEMAND);
- if(dt) {};
+ if(dt) {};
- if(!ff) {
- char filename[FILENAME_MAX + 1];
- snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/net/stat/synproxy");
- ff = procfile_open(config_get("plugin:proc:/proc/net/stat/synproxy", "filename to monitor", filename), " \t,:|", PROCFILE_FLAG_DEFAULT);
- }
- if(!ff) return 1;
+ if(!ff) {
+ char filename[FILENAME_MAX + 1];
+ snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/net/stat/synproxy");
+ ff = procfile_open(config_get("plugin:proc:/proc/net/stat/synproxy", "filename to monitor", filename), " \t,:|", PROCFILE_FLAG_DEFAULT);
+ }
+ if(!ff) return 1;
- ff = procfile_readall(ff);
- if(!ff) return 0; // we return 0, so that we will retry to open it next time
+ ff = procfile_readall(ff);
+ if(!ff) return 0; // we return 0, so that we will retry to open it next time
- // make sure we have 3 lines
- unsigned long lines = procfile_lines(ff), l;
- if(lines < 2) {
- error("/proc/net/stat/synproxy has %d lines, expected no less than 2. Disabling it.", lines);
- return 1;
- }
+ // make sure we have 3 lines
+ size_t lines = procfile_lines(ff), l;
+ if(lines < 2) {
+ error("/proc/net/stat/synproxy has %zu lines, expected no less than 2. Disabling it.", lines);
+ return 1;
+ }
- unsigned long long entries = 0, syn_received = 0, cookie_invalid = 0, cookie_valid = 0, cookie_retrans = 0, conn_reopened = 0;
+ unsigned long long entries = 0, syn_received = 0, cookie_invalid = 0, cookie_valid = 0, cookie_retrans = 0, conn_reopened = 0;
- // synproxy gives its values per CPU
- for(l = 1; l < lines ;l++) {
- int words = procfile_linewords(ff, l);
- if(words < 6) continue;
+ // synproxy gives its values per CPU
+ for(l = 1; l < lines ;l++) {
+ int words = procfile_linewords(ff, l);
+ if(words < 6) continue;
- entries += strtoull(procfile_lineword(ff, l, 0), NULL, 16);
- syn_received += strtoull(procfile_lineword(ff, l, 1), NULL, 16);
- cookie_invalid += strtoull(procfile_lineword(ff, l, 2), NULL, 16);
- cookie_valid += strtoull(procfile_lineword(ff, l, 3), NULL, 16);
- cookie_retrans += strtoull(procfile_lineword(ff, l, 4), NULL, 16);
- conn_reopened += strtoull(procfile_lineword(ff, l, 5), NULL, 16);
- }
+ entries += strtoull(procfile_lineword(ff, l, 0), NULL, 16);
+ syn_received += strtoull(procfile_lineword(ff, l, 1), NULL, 16);
+ cookie_invalid += strtoull(procfile_lineword(ff, l, 2), NULL, 16);
+ cookie_valid += strtoull(procfile_lineword(ff, l, 3), NULL, 16);
+ cookie_retrans += strtoull(procfile_lineword(ff, l, 4), NULL, 16);
+ conn_reopened += strtoull(procfile_lineword(ff, l, 5), NULL, 16);
+ }
- unsigned long long events = entries + syn_received + cookie_invalid + cookie_valid + cookie_retrans + conn_reopened;
+ unsigned long long events = entries + syn_received + cookie_invalid + cookie_valid + cookie_retrans + conn_reopened;
- RRDSET *st;
+ RRDSET *st;
- // --------------------------------------------------------------------
+ // --------------------------------------------------------------------
- if((do_entries == CONFIG_ONDEMAND_ONDEMAND && events) || do_entries == CONFIG_ONDEMAND_YES) {
- do_entries = CONFIG_ONDEMAND_YES;
+ if((do_entries == CONFIG_ONDEMAND_ONDEMAND && events) || do_entries == CONFIG_ONDEMAND_YES) {
+ do_entries = CONFIG_ONDEMAND_YES;
- st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_SYNPROXY "_entries");
- if(!st) {
- st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_SYNPROXY "_entries", NULL, RRD_TYPE_NET_STAT_SYNPROXY, NULL, "SYNPROXY Entries Used", "entries", 1004, update_every, RRDSET_TYPE_LINE);
+ st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_SYNPROXY "_entries");
+ if(!st) {
+ st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_SYNPROXY "_entries", NULL, RRD_TYPE_NET_STAT_SYNPROXY, NULL, "SYNPROXY Entries Used", "entries", 1004, update_every, RRDSET_TYPE_LINE);
- rrddim_add(st, "entries", NULL, 1, 1, RRDDIM_ABSOLUTE);
- }
- else rrdset_next(st);
+ rrddim_add(st, "entries", NULL, 1, 1, RRDDIM_ABSOLUTE);
+ }
+ else rrdset_next(st);
- rrddim_set(st, "entries", entries);
- rrdset_done(st);
- }
+ rrddim_set(st, "entries", entries);
+ rrdset_done(st);
+ }
- // --------------------------------------------------------------------
+ // --------------------------------------------------------------------
- if((do_syns == CONFIG_ONDEMAND_ONDEMAND && events) || do_syns == CONFIG_ONDEMAND_YES) {
- do_syns = CONFIG_ONDEMAND_YES;
+ if((do_syns == CONFIG_ONDEMAND_ONDEMAND && events) || do_syns == CONFIG_ONDEMAND_YES) {
+ do_syns = CONFIG_ONDEMAND_YES;
- st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_SYNPROXY "_syn_received");
- if(!st) {
- st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_SYNPROXY "_syn_received", NULL, RRD_TYPE_NET_STAT_SYNPROXY, NULL, "SYNPROXY SYN Packets received", "SYN/s", 1001, update_every, RRDSET_TYPE_LINE);
+ st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_SYNPROXY "_syn_received");
+ if(!st) {
+ st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_SYNPROXY "_syn_received", NULL, RRD_TYPE_NET_STAT_SYNPROXY, NULL, "SYNPROXY SYN Packets received", "SYN/s", 1001, update_every, RRDSET_TYPE_LINE);
- rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
+ rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
- rrddim_set(st, "received", syn_received);
- rrdset_done(st);
- }
+ rrddim_set(st, "received", syn_received);
+ rrdset_done(st);
+ }
- // --------------------------------------------------------------------
+ // --------------------------------------------------------------------
- if((do_reopened == CONFIG_ONDEMAND_ONDEMAND && events) || do_reopened == CONFIG_ONDEMAND_YES) {
- do_reopened = CONFIG_ONDEMAND_YES;
+ if((do_reopened == CONFIG_ONDEMAND_ONDEMAND && events) || do_reopened == CONFIG_ONDEMAND_YES) {
+ do_reopened = CONFIG_ONDEMAND_YES;
- st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_SYNPROXY "_conn_reopened");
- if(!st) {
- st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_SYNPROXY "_conn_reopened", NULL, RRD_TYPE_NET_STAT_SYNPROXY, NULL, "SYNPROXY Connections Reopened", "connections/s", 1003, update_every, RRDSET_TYPE_LINE);
+ st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_SYNPROXY "_conn_reopened");
+ if(!st) {
+ st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_SYNPROXY "_conn_reopened", NULL, RRD_TYPE_NET_STAT_SYNPROXY, NULL, "SYNPROXY Connections Reopened", "connections/s", 1003, update_every, RRDSET_TYPE_LINE);
- rrddim_add(st, "reopened", NULL, 1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
+ rrddim_add(st, "reopened", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
- rrddim_set(st, "reopened", conn_reopened);
- rrdset_done(st);
- }
+ rrddim_set(st, "reopened", conn_reopened);
+ rrdset_done(st);
+ }
- // --------------------------------------------------------------------
+ // --------------------------------------------------------------------
- if((do_cookies == CONFIG_ONDEMAND_ONDEMAND && events) || do_cookies == CONFIG_ONDEMAND_YES) {
- do_cookies = CONFIG_ONDEMAND_YES;
+ if((do_cookies == CONFIG_ONDEMAND_ONDEMAND && events) || do_cookies == CONFIG_ONDEMAND_YES) {
+ do_cookies = CONFIG_ONDEMAND_YES;
- st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_SYNPROXY "_cookies");
- if(!st) {
- st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_SYNPROXY "_cookies", NULL, RRD_TYPE_NET_STAT_SYNPROXY, NULL, "SYNPROXY TCP Cookies", "cookies/s", 1002, update_every, RRDSET_TYPE_LINE);
+ st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_SYNPROXY "_cookies");
+ if(!st) {
+ st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_SYNPROXY "_cookies", NULL, RRD_TYPE_NET_STAT_SYNPROXY, NULL, "SYNPROXY TCP Cookies", "cookies/s", 1002, update_every, RRDSET_TYPE_LINE);
- rrddim_add(st, "valid", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "invalid", NULL, -1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "retransmits", NULL, 1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
+ rrddim_add(st, "valid", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "invalid", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "retransmits", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
- rrddim_set(st, "valid", cookie_valid);
- rrddim_set(st, "invalid", cookie_invalid);
- rrddim_set(st, "retransmits", cookie_retrans);
- rrdset_done(st);
- }
+ rrddim_set(st, "valid", cookie_valid);
+ rrddim_set(st, "invalid", cookie_invalid);
+ rrddim_set(st, "retransmits", cookie_retrans);
+ rrdset_done(st);
+ }
- return 0;
+ return 0;
}
diff --git a/src/proc_self_mountinfo.c b/src/proc_self_mountinfo.c
index 45630b4c0..51aea7aee 100644
--- a/src/proc_self_mountinfo.c
+++ b/src/proc_self_mountinfo.c
@@ -1,231 +1,242 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-
#include "common.h"
-#include "log.h"
-#include "appconfig.h"
-
-#include "proc_self_mountinfo.h"
// find the mount info with the given major:minor
// in the supplied linked list of mountinfo structures
struct mountinfo *mountinfo_find(struct mountinfo *root, unsigned long major, unsigned long minor) {
- struct mountinfo *mi;
+ struct mountinfo *mi;
- for(mi = root; mi ; mi = mi->next)
- if(mi->major == major && mi->minor == minor)
- return mi;
+ for(mi = root; mi ; mi = mi->next)
+ if(mi->major == major && mi->minor == minor)
+ return mi;
- return NULL;
+ return NULL;
}
// find the mount info with the given filesystem and mount_source
// in the supplied linked list of mountinfo structures
struct mountinfo *mountinfo_find_by_filesystem_mount_source(struct mountinfo *root, const char *filesystem, const char *mount_source) {
- struct mountinfo *mi;
- uint32_t filesystem_hash = simple_hash(filesystem), mount_source_hash = simple_hash(mount_source);
-
- for(mi = root; mi ; mi = mi->next)
- if(mi->filesystem
- && mi->mount_source
- && mi->filesystem_hash == filesystem_hash
- && mi->mount_source_hash == mount_source_hash
- && !strcmp(mi->filesystem, filesystem)
- && !strcmp(mi->mount_source, mount_source))
- return mi;
-
- return NULL;
+ struct mountinfo *mi;
+ uint32_t filesystem_hash = simple_hash(filesystem), mount_source_hash = simple_hash(mount_source);
+
+ for(mi = root; mi ; mi = mi->next)
+ if(mi->filesystem
+ && mi->mount_source
+ && mi->filesystem_hash == filesystem_hash
+ && mi->mount_source_hash == mount_source_hash
+ && !strcmp(mi->filesystem, filesystem)
+ && !strcmp(mi->mount_source, mount_source))
+ return mi;
+
+ return NULL;
}
struct mountinfo *mountinfo_find_by_filesystem_super_option(struct mountinfo *root, const char *filesystem, const char *super_options) {
- struct mountinfo *mi;
- uint32_t filesystem_hash = simple_hash(filesystem);
+ struct mountinfo *mi;
+ uint32_t filesystem_hash = simple_hash(filesystem);
- size_t solen = strlen(super_options);
+ size_t solen = strlen(super_options);
- for(mi = root; mi ; mi = mi->next)
- if(mi->filesystem
- && mi->super_options
- && mi->filesystem_hash == filesystem_hash
- && !strcmp(mi->filesystem, filesystem)) {
+ for(mi = root; mi ; mi = mi->next)
+ if(mi->filesystem
+ && mi->super_options
+ && mi->filesystem_hash == filesystem_hash
+ && !strcmp(mi->filesystem, filesystem)) {
- // super_options is a comma separated list
- char *s = mi->super_options, *e;
- while(*s) {
- e = ++s;
- while(*e && *e != ',') e++;
+ // super_options is a comma separated list
+ char *s = mi->super_options, *e;
+ while(*s) {
+ e = s + 1;
+ while(*e && *e != ',') e++;
- size_t len = e - s;
- if(len == solen && !strncmp(s, super_options, len))
- return mi;
+ size_t len = e - s;
+ if(len == solen && !strncmp(s, super_options, len))
+ return mi;
- if(*e == ',') s = ++e;
- else s = e;
- }
- }
+ if(*e == ',') s = ++e;
+ else s = e;
+ }
+ }
- return NULL;
+ return NULL;
}
// free a linked list of mountinfo structures
void mountinfo_free(struct mountinfo *mi) {
- if(unlikely(!mi))
- return;
+ if(unlikely(!mi))
+ return;
- if(likely(mi->next))
- mountinfo_free(mi->next);
+ if(likely(mi->next))
+ mountinfo_free(mi->next);
- if(mi->root) free(mi->root);
- if(mi->mount_point) free(mi->mount_point);
- if(mi->mount_options) free(mi->mount_options);
+ freez(mi->root);
+ freez(mi->mount_point);
+ freez(mi->mount_options);
/*
- if(mi->optional_fields_count) {
- int i;
- for(i = 0; i < mi->optional_fields_count ; i++)
- free(*mi->optional_fields[i]);
- }
- free(mi->optional_fields);
+ if(mi->optional_fields_count) {
+ int i;
+ for(i = 0; i < mi->optional_fields_count ; i++)
+ free(*mi->optional_fields[i]);
+ }
+ free(mi->optional_fields);
*/
- free(mi->filesystem);
- free(mi->mount_source);
- free(mi->super_options);
- free(mi);
+ freez(mi->filesystem);
+ freez(mi->mount_source);
+ freez(mi->super_options);
+ freez(mi);
+}
+
+static char *strdupz_decoding_octal(const char *string) {
+ char *buffer = strdupz(string);
+
+ char *d = buffer;
+ const char *s = string;
+
+ while(*s) {
+ if(unlikely(*s == '\\')) {
+ s++;
+ if(likely(isdigit(*s) && isdigit(s[1]) && isdigit(s[2]))) {
+ char c = *s++ - '0';
+ c <<= 3;
+ c |= *s++ - '0';
+ c <<= 3;
+ c |= *s++ - '0';
+ *d++ = c;
+ }
+ else *d++ = '_';
+ }
+ else *d++ = *s++;
+ }
+ *d = '\0';
+
+ return buffer;
}
// read the whole mountinfo into a linked list
struct mountinfo *mountinfo_read() {
- procfile *ff = NULL;
+ procfile *ff = NULL;
+
+ char filename[FILENAME_MAX + 1];
+ snprintfz(filename, FILENAME_MAX, "%s/proc/self/mountinfo", global_host_prefix);
+ ff = procfile_open(filename, " \t", PROCFILE_FLAG_DEFAULT);
+ if(!ff) {
+ snprintfz(filename, FILENAME_MAX, "%s/proc/1/mountinfo", global_host_prefix);
+ ff = procfile_open(filename, " \t", PROCFILE_FLAG_DEFAULT);
+ if(!ff) return NULL;
+ }
+
+ ff = procfile_readall(ff);
+ if(!ff) return NULL;
- char filename[FILENAME_MAX + 1];
- snprintfz(filename, FILENAME_MAX, "%s/proc/self/mountinfo", global_host_prefix);
- ff = procfile_open(filename, " \t", PROCFILE_FLAG_DEFAULT);
- if(!ff) {
- snprintfz(filename, FILENAME_MAX, "%s/proc/1/mountinfo", global_host_prefix);
- ff = procfile_open(filename, " \t", PROCFILE_FLAG_DEFAULT);
- if(!ff) return NULL;
- }
+ struct mountinfo *root = NULL, *last = NULL, *mi = NULL;
- ff = procfile_readall(ff);
- if(!ff) return NULL;
+ unsigned long l, lines = procfile_lines(ff);
+ for(l = 0; l < lines ;l++) {
+ if(procfile_linewords(ff, l) < 5)
+ continue;
- struct mountinfo *root = NULL, *last = NULL, *mi = NULL;
+ mi = mallocz(sizeof(struct mountinfo));
- unsigned long l, lines = procfile_lines(ff);
- for(l = 0; l < lines ;l++) {
- if(procfile_linewords(ff, l) < 5)
- continue;
+ if(unlikely(!root))
+ root = last = mi;
+ else
+ last->next = mi;
- mi = malloc(sizeof(struct mountinfo));
- if(unlikely(!mi)) fatal("Cannot allocate memory for mountinfo");
+ last = mi;
+ mi->next = NULL;
- if(unlikely(!root))
- root = last = mi;
- else
- last->next = mi;
+ unsigned long w = 0;
+ mi->id = strtoul(procfile_lineword(ff, l, w), NULL, 10); w++;
+ mi->parentid = strtoul(procfile_lineword(ff, l, w), NULL, 10); w++;
- last = mi;
- mi->next = NULL;
+ char *major = procfile_lineword(ff, l, w), *minor; w++;
+ for(minor = major; *minor && *minor != ':' ;minor++) ;
- unsigned long w = 0;
- mi->id = strtoul(procfile_lineword(ff, l, w), NULL, 10); w++;
- mi->parentid = strtoul(procfile_lineword(ff, l, w), NULL, 10); w++;
+ if(!*minor) {
+ error("Cannot parse major:minor on '%s' at line %lu of '%s'", major, l + 1, filename);
+ continue;
+ }
- char *major = procfile_lineword(ff, l, w), *minor; w++;
- for(minor = major; *minor && *minor != ':' ;minor++) ;
- *minor = '\0';
- minor++;
+ *minor = '\0';
+ minor++;
- mi->major = strtoul(major, NULL, 10);
- mi->minor = strtoul(minor, NULL, 10);
+ mi->major = strtoul(major, NULL, 10);
+ mi->minor = strtoul(minor, NULL, 10);
- mi->root = strdup(procfile_lineword(ff, l, w)); w++;
- if(unlikely(!mi->root)) fatal("Cannot allocate memory");
- mi->root_hash = simple_hash(mi->root);
+ mi->root = strdupz(procfile_lineword(ff, l, w)); w++;
+ mi->root_hash = simple_hash(mi->root);
- mi->mount_point = strdup(procfile_lineword(ff, l, w)); w++;
- if(unlikely(!mi->mount_point)) fatal("Cannot allocate memory");
- mi->mount_point_hash = simple_hash(mi->mount_point);
+ mi->mount_point = strdupz_decoding_octal(procfile_lineword(ff, l, w)); w++;
+ mi->mount_point_hash = simple_hash(mi->mount_point);
- mi->mount_options = strdup(procfile_lineword(ff, l, w)); w++;
- if(unlikely(!mi->mount_options)) fatal("Cannot allocate memory");
+ mi->mount_options = strdupz(procfile_lineword(ff, l, w)); w++;
- // count the optional fields
+ // count the optional fields
/*
- unsigned long wo = w;
+ unsigned long wo = w;
*/
- mi->optional_fields_count = 0;
- char *s = procfile_lineword(ff, l, w);
- while(*s && *s != '-') {
- w++;
- s = procfile_lineword(ff, l, w);
- mi->optional_fields_count++;
- }
+ mi->optional_fields_count = 0;
+ char *s = procfile_lineword(ff, l, w);
+ while(*s && *s != '-') {
+ w++;
+ s = procfile_lineword(ff, l, w);
+ mi->optional_fields_count++;
+ }
/*
- if(unlikely(mi->optional_fields_count)) {
- // we have some optional fields
- // read them into a new array of pointers;
-
- mi->optional_fields = malloc(mi->optional_fields_count * sizeof(char *));
- if(unlikely(!mi->optional_fields))
- fatal("Cannot allocate memory for %d mountinfo optional fields", mi->optional_fields_count);
-
- int i;
- for(i = 0; i < mi->optional_fields_count ; i++) {
- *mi->optional_fields[wo] = strdup(procfile_lineword(ff, l, w));
- if(!mi->optional_fields[wo]) fatal("Cannot allocate memory");
- wo++;
- }
- }
- else
- mi->optional_fields = NULL;
+ if(unlikely(mi->optional_fields_count)) {
+ // we have some optional fields
+ // read them into a new array of pointers;
+
+ mi->optional_fields = malloc(mi->optional_fields_count * sizeof(char *));
+ if(unlikely(!mi->optional_fields))
+ fatal("Cannot allocate memory for %d mountinfo optional fields", mi->optional_fields_count);
+
+ int i;
+ for(i = 0; i < mi->optional_fields_count ; i++) {
+ *mi->optional_fields[wo] = strdup(procfile_lineword(ff, l, w));
+ if(!mi->optional_fields[wo]) fatal("Cannot allocate memory");
+ wo++;
+ }
+ }
+ else
+ mi->optional_fields = NULL;
*/
- if(likely(*s == '-')) {
- w++;
+ if(likely(*s == '-')) {
+ w++;
- mi->filesystem = strdup(procfile_lineword(ff, l, w)); w++;
- if(!mi->filesystem) fatal("Cannot allocate memory");
- mi->filesystem_hash = simple_hash(mi->filesystem);
+ mi->filesystem = strdupz(procfile_lineword(ff, l, w)); w++;
+ mi->filesystem_hash = simple_hash(mi->filesystem);
- mi->mount_source = strdup(procfile_lineword(ff, l, w)); w++;
- if(!mi->mount_source) fatal("Cannot allocate memory");
- mi->mount_source_hash = simple_hash(mi->mount_source);
+ mi->mount_source = strdupz(procfile_lineword(ff, l, w)); w++;
+ mi->mount_source_hash = simple_hash(mi->mount_source);
- mi->super_options = strdup(procfile_lineword(ff, l, w)); w++;
- if(!mi->super_options) fatal("Cannot allocate memory");
- }
- else {
- mi->filesystem = NULL;
- mi->mount_source = NULL;
- mi->super_options = NULL;
- }
+ mi->super_options = strdupz(procfile_lineword(ff, l, w)); w++;
+ }
+ else {
+ mi->filesystem = NULL;
+ mi->mount_source = NULL;
+ mi->super_options = NULL;
+ }
/*
- info("MOUNTINFO: %u %u %u:%u root '%s', mount point '%s', mount options '%s', filesystem '%s', mount source '%s', super options '%s'",
- mi->id,
- mi->parentid,
- mi->major,
- mi->minor,
- mi->root,
- mi->mount_point,
- mi->mount_options,
- mi->filesystem,
- mi->mount_source,
- mi->super_options
- );
+ info("MOUNTINFO: %u %u %u:%u root '%s', mount point '%s', mount options '%s', filesystem '%s', mount source '%s', super options '%s'",
+ mi->id,
+ mi->parentid,
+ mi->major,
+ mi->minor,
+ mi->root,
+ mi->mount_point,
+ mi->mount_options,
+ mi->filesystem,
+ mi->mount_source,
+ mi->super_options
+ );
*/
- }
+ }
- procfile_close(ff);
- return root;
+ procfile_close(ff);
+ return root;
}
diff --git a/src/proc_self_mountinfo.h b/src/proc_self_mountinfo.h
index 51712a58a..c2d9688c1 100644
--- a/src/proc_self_mountinfo.h
+++ b/src/proc_self_mountinfo.h
@@ -1,35 +1,33 @@
-#include "procfile.h"
-
#ifndef NETDATA_PROC_SELF_MOUNTINFO_H
#define NETDATA_PROC_SELF_MOUNTINFO_H 1
struct mountinfo {
- long id; // mount ID: unique identifier of the mount (may be reused after umount(2)).
- long parentid; // parent ID: ID of parent mount (or of self for the top of the mount tree).
- unsigned long major; // major:minor: value of st_dev for files on filesystem (see stat(2)).
- unsigned long minor;
+ long id; // mount ID: unique identifier of the mount (may be reused after umount(2)).
+ long parentid; // parent ID: ID of parent mount (or of self for the top of the mount tree).
+ unsigned long major; // major:minor: value of st_dev for files on filesystem (see stat(2)).
+ unsigned long minor;
- char *root; // root: root of the mount within the filesystem.
- uint32_t root_hash;
+ char *root; // root: root of the mount within the filesystem.
+ uint32_t root_hash;
- char *mount_point; // mount point: mount point relative to the process's root.
- uint32_t mount_point_hash;
+ char *mount_point; // mount point: mount point relative to the process's root.
+ uint32_t mount_point_hash;
- char *mount_options; // mount options: per-mount options.
+ char *mount_options; // mount options: per-mount options.
- int optional_fields_count;
+ int optional_fields_count;
/*
- char ***optional_fields; // optional fields: zero or more fields of the form "tag[:value]".
+ char ***optional_fields; // optional fields: zero or more fields of the form "tag[:value]".
*/
- char *filesystem; // filesystem type: name of filesystem in the form "type[.subtype]".
- uint32_t filesystem_hash;
+ char *filesystem; // filesystem type: name of filesystem in the form "type[.subtype]".
+ uint32_t filesystem_hash;
- char *mount_source; // mount source: filesystem-specific information or "none".
- uint32_t mount_source_hash;
+ char *mount_source; // mount source: filesystem-specific information or "none".
+ uint32_t mount_source_hash;
- char *super_options; // super options: per-superblock options.
+ char *super_options; // super options: per-superblock options.
- struct mountinfo *next;
+ struct mountinfo *next;
};
extern struct mountinfo *mountinfo_find(struct mountinfo *root, unsigned long major, unsigned long minor);
diff --git a/src/proc_softirqs.c b/src/proc_softirqs.c
index 96b5d3d30..a5165040a 100644
--- a/src/proc_softirqs.c
+++ b/src/proc_softirqs.c
@@ -1,26 +1,13 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-
#include "common.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "rrd.h"
-#include "plugin_proc.h"
-#include "log.h"
#define MAX_INTERRUPT_NAME 50
struct interrupt {
- int used;
- char *id;
- char name[MAX_INTERRUPT_NAME + 1];
- unsigned long long total;
- unsigned long long value[];
+ int used;
+ char *id;
+ char name[MAX_INTERRUPT_NAME + 1];
+ unsigned long long total;
+ unsigned long long value[];
};
// since each interrupt is variable in size
@@ -31,160 +18,157 @@ struct interrupt {
#define irrindex(base, line, cpus) ((struct interrupt *)&((char *)(base))[line * recordsize(cpus)])
static inline struct interrupt *get_interrupts_array(int lines, int cpus) {
- static struct interrupt *irrs = NULL;
- static int allocated = 0;
-
- if(lines < allocated) return irrs;
- else {
- irrs = (struct interrupt *)realloc(irrs, lines * recordsize(cpus));
- if(!irrs)
- fatal("Cannot allocate memory for %d interrupts", lines);
+ static struct interrupt *irrs = NULL;
+ static int allocated = 0;
- allocated = lines;
- }
+ if(lines < allocated) return irrs;
+ else {
+ irrs = (struct interrupt *)reallocz(irrs, lines * recordsize(cpus));
+ allocated = lines;
+ }
- return irrs;
+ return irrs;
}
int do_proc_softirqs(int update_every, unsigned long long dt) {
- static procfile *ff = NULL;
- static int cpus = -1, do_per_core = -1;
+ static procfile *ff = NULL;
+ static int cpus = -1, do_per_core = -1;
- struct interrupt *irrs = NULL;
-
- if(dt) {};
+ struct interrupt *irrs = NULL;
+
+ if(dt) {};
- if(do_per_core == -1) do_per_core = config_get_boolean("plugin:proc:/proc/softirqs", "interrupts per core", 1);
-
- if(!ff) {
- char filename[FILENAME_MAX + 1];
- snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/softirqs");
- ff = procfile_open(config_get("plugin:proc:/proc/softirqs", "filename to monitor", filename), " \t", PROCFILE_FLAG_DEFAULT);
- }
- if(!ff) return 1;
-
- ff = procfile_readall(ff);
- if(!ff) return 0; // we return 0, so that we will retry to open it next time
-
- uint32_t lines = procfile_lines(ff), l;
- uint32_t words = procfile_linewords(ff, 0), w;
-
- if(!lines) {
- error("Cannot read /proc/softirqs, zero lines reported.");
- return 1;
- }
-
- // find how many CPUs are there
- if(cpus == -1) {
- cpus = 0;
- for(w = 0; w < words ; w++) {
- if(strncmp(procfile_lineword(ff, 0, w), "CPU", 3) == 0)
- cpus++;
- }
- }
-
- if(!cpus) {
- error("PLUGIN: PROC_SOFTIRQS: Cannot find the number of CPUs in /proc/softirqs");
- return 1;
- }
-
- // allocate the size we need;
- irrs = get_interrupts_array(lines, cpus);
- irrs[0].used = 0;
-
- // loop through all lines
- for(l = 1; l < lines ;l++) {
- struct interrupt *irr = irrindex(irrs, l, cpus);
- irr->used = 0;
- irr->total = 0;
-
- words = procfile_linewords(ff, l);
- if(!words) continue;
-
- irr->id = procfile_lineword(ff, l, 0);
- if(!irr->id || !irr->id[0]) continue;
-
- int idlen = strlen(irr->id);
- if(irr->id[idlen - 1] == ':')
- irr->id[idlen - 1] = '\0';
-
- int c;
- for(c = 0; c < cpus ;c++) {
- if((c + 1) < (int)words)
- irr->value[c] = strtoull(procfile_lineword(ff, l, (uint32_t)(c + 1)), NULL, 10);
- else
- irr->value[c] = 0;
-
- irr->total += irr->value[c];
- }
-
- strncpyz(irr->name, irr->id, MAX_INTERRUPT_NAME);
-
- irr->used = 1;
- }
-
- RRDSET *st;
-
- // --------------------------------------------------------------------
-
- st = rrdset_find_bytype("system", "softirqs");
- if(!st) {
- st = rrdset_create("system", "softirqs", NULL, "softirqs", NULL, "System softirqs", "softirqs/s", 950, update_every, RRDSET_TYPE_STACKED);
-
- for(l = 0; l < lines ;l++) {
- struct interrupt *irr = irrindex(irrs, l, cpus);
- if(!irr->used) continue;
- rrddim_add(st, irr->id, irr->name, 1, 1, RRDDIM_INCREMENTAL);
- }
- }
- else rrdset_next(st);
-
- for(l = 0; l < lines ;l++) {
- struct interrupt *irr = irrindex(irrs, l, cpus);
- if(!irr->used) continue;
- rrddim_set(st, irr->id, irr->total);
- }
- rrdset_done(st);
-
- if(do_per_core) {
- int c;
-
- for(c = 0; c < cpus ; c++) {
- char id[256+1];
- snprintfz(id, 256, "cpu%d_softirqs", c);
-
- st = rrdset_find_bytype("cpu", id);
- if(!st) {
- // find if everything is zero
- unsigned long long core_sum = 0 ;
- for(l = 0; l < lines ;l++) {
- struct interrupt *irr = irrindex(irrs, l, cpus);
- if(!irr->used) continue;
- core_sum += irr->value[c];
- }
- if(core_sum == 0) continue; // try next core
-
- char name[256+1], title[256+1];
- snprintfz(name, 256, "cpu%d_softirqs", c);
- snprintfz(title, 256, "CPU%d softirqs", c);
- st = rrdset_create("cpu", id, name, "softirqs", "cpu.softirqs", title, "softirqs/s", 3000 + c, update_every, RRDSET_TYPE_STACKED);
-
- for(l = 0; l < lines ;l++) {
- struct interrupt *irr = irrindex(irrs, l, cpus);
- if(!irr->used) continue;
- rrddim_add(st, irr->id, irr->name, 1, 1, RRDDIM_INCREMENTAL);
- }
- }
- else rrdset_next(st);
-
- for(l = 0; l < lines ;l++) {
- struct interrupt *irr = irrindex(irrs, l, cpus);
- if(!irr->used) continue;
- rrddim_set(st, irr->id, irr->value[c]);
- }
- rrdset_done(st);
- }
- }
-
- return 0;
+ if(do_per_core == -1) do_per_core = config_get_boolean("plugin:proc:/proc/softirqs", "interrupts per core", 1);
+
+ if(!ff) {
+ char filename[FILENAME_MAX + 1];
+ snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/softirqs");
+ ff = procfile_open(config_get("plugin:proc:/proc/softirqs", "filename to monitor", filename), " \t", PROCFILE_FLAG_DEFAULT);
+ }
+ if(!ff) return 1;
+
+ ff = procfile_readall(ff);
+ if(!ff) return 0; // we return 0, so that we will retry to open it next time
+
+ uint32_t lines = procfile_lines(ff), l;
+ uint32_t words = procfile_linewords(ff, 0), w;
+
+ if(!lines) {
+ error("Cannot read /proc/softirqs, zero lines reported.");
+ return 1;
+ }
+
+ // find how many CPUs are there
+ if(cpus == -1) {
+ cpus = 0;
+ for(w = 0; w < words ; w++) {
+ if(strncmp(procfile_lineword(ff, 0, w), "CPU", 3) == 0)
+ cpus++;
+ }
+ }
+
+ if(!cpus) {
+ error("PLUGIN: PROC_SOFTIRQS: Cannot find the number of CPUs in /proc/softirqs");
+ return 1;
+ }
+
+ // allocate the size we need;
+ irrs = get_interrupts_array(lines, cpus);
+ irrs[0].used = 0;
+
+ // loop through all lines
+ for(l = 1; l < lines ;l++) {
+ struct interrupt *irr = irrindex(irrs, l, cpus);
+ irr->used = 0;
+ irr->total = 0;
+
+ words = procfile_linewords(ff, l);
+ if(!words) continue;
+
+ irr->id = procfile_lineword(ff, l, 0);
+ if(!irr->id || !irr->id[0]) continue;
+
+ int idlen = strlen(irr->id);
+ if(irr->id[idlen - 1] == ':')
+ irr->id[idlen - 1] = '\0';
+
+ int c;
+ for(c = 0; c < cpus ;c++) {
+ if((c + 1) < (int)words)
+ irr->value[c] = strtoull(procfile_lineword(ff, l, (uint32_t)(c + 1)), NULL, 10);
+ else
+ irr->value[c] = 0;
+
+ irr->total += irr->value[c];
+ }
+
+ strncpyz(irr->name, irr->id, MAX_INTERRUPT_NAME);
+
+ irr->used = 1;
+ }
+
+ RRDSET *st;
+
+ // --------------------------------------------------------------------
+
+ st = rrdset_find_bytype("system", "softirqs");
+ if(!st) {
+ st = rrdset_create("system", "softirqs", NULL, "softirqs", NULL, "System softirqs", "softirqs/s", 950, update_every, RRDSET_TYPE_STACKED);
+
+ for(l = 0; l < lines ;l++) {
+ struct interrupt *irr = irrindex(irrs, l, cpus);
+ if(!irr->used) continue;
+ rrddim_add(st, irr->id, irr->name, 1, 1, RRDDIM_INCREMENTAL);
+ }
+ }
+ else rrdset_next(st);
+
+ for(l = 0; l < lines ;l++) {
+ struct interrupt *irr = irrindex(irrs, l, cpus);
+ if(!irr->used) continue;
+ rrddim_set(st, irr->id, irr->total);
+ }
+ rrdset_done(st);
+
+ if(do_per_core) {
+ int c;
+
+ for(c = 0; c < cpus ; c++) {
+ char id[256+1];
+ snprintfz(id, 256, "cpu%d_softirqs", c);
+
+ st = rrdset_find_bytype("cpu", id);
+ if(!st) {
+ // find if everything is zero
+ unsigned long long core_sum = 0 ;
+ for(l = 0; l < lines ;l++) {
+ struct interrupt *irr = irrindex(irrs, l, cpus);
+ if(!irr->used) continue;
+ core_sum += irr->value[c];
+ }
+ if(core_sum == 0) continue; // try next core
+
+ char name[256+1], title[256+1];
+ snprintfz(name, 256, "cpu%d_softirqs", c);
+ snprintfz(title, 256, "CPU%d softirqs", c);
+ st = rrdset_create("cpu", id, name, "softirqs", "cpu.softirqs", title, "softirqs/s", 3000 + c, update_every, RRDSET_TYPE_STACKED);
+
+ for(l = 0; l < lines ;l++) {
+ struct interrupt *irr = irrindex(irrs, l, cpus);
+ if(!irr->used) continue;
+ rrddim_add(st, irr->id, irr->name, 1, 1, RRDDIM_INCREMENTAL);
+ }
+ }
+ else rrdset_next(st);
+
+ for(l = 0; l < lines ;l++) {
+ struct interrupt *irr = irrindex(irrs, l, cpus);
+ if(!irr->used) continue;
+ rrddim_set(st, irr->id, irr->value[c]);
+ }
+ rrdset_done(st);
+ }
+ }
+
+ return 0;
}
diff --git a/src/proc_stat.c b/src/proc_stat.c
index 154ba167d..88cb820b3 100644
--- a/src/proc_stat.c
+++ b/src/proc_stat.c
@@ -1,204 +1,211 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
#include "common.h"
-#include "log.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "rrd.h"
-#include "plugin_proc.h"
-
-#define RRD_TYPE_STAT "cpu"
-#define RRD_TYPE_STAT_LEN strlen(RRD_TYPE_STAT)
int do_proc_stat(int update_every, unsigned long long dt) {
- static procfile *ff = NULL;
- static int do_cpu = -1, do_cpu_cores = -1, do_interrupts = -1, do_context = -1, do_forks = -1, do_processes = -1;
-
- if(do_cpu == -1) do_cpu = config_get_boolean("plugin:proc:/proc/stat", "cpu utilization", 1);
- if(do_cpu_cores == -1) do_cpu_cores = config_get_boolean("plugin:proc:/proc/stat", "per cpu core utilization", 1);
- if(do_interrupts == -1) do_interrupts = config_get_boolean("plugin:proc:/proc/stat", "cpu interrupts", 1);
- if(do_context == -1) do_context = config_get_boolean("plugin:proc:/proc/stat", "context switches", 1);
- if(do_forks == -1) do_forks = config_get_boolean("plugin:proc:/proc/stat", "processes started", 1);
- if(do_processes == -1) do_processes = config_get_boolean("plugin:proc:/proc/stat", "processes running", 1);
-
- if(dt) {};
-
- if(!ff) {
- char filename[FILENAME_MAX + 1];
- snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/stat");
- ff = procfile_open(config_get("plugin:proc:/proc/stat", "filename to monitor", filename), " \t:", PROCFILE_FLAG_DEFAULT);
- }
- if(!ff) return 1;
-
- ff = procfile_readall(ff);
- if(!ff) return 0; // we return 0, so that we will retry to open it next time
-
- uint32_t lines = procfile_lines(ff), l;
- uint32_t words;
-
- unsigned long long processes = 0, running = 0 , blocked = 0;
- RRDSET *st;
-
- for(l = 0; l < lines ;l++) {
- if(strncmp(procfile_lineword(ff, l, 0), "cpu", 3) == 0) {
- words = procfile_linewords(ff, l);
- if(words < 9) {
- error("Cannot read /proc/stat cpu line. Expected 9 params, read %d.", words);
- continue;
- }
-
- char *id;
- unsigned long long user = 0, nice = 0, system = 0, idle = 0, iowait = 0, irq = 0, softirq = 0, steal = 0, guest = 0, guest_nice = 0;
-
- id = procfile_lineword(ff, l, 0);
- user = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
- nice = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
- system = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
- idle = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
- iowait = strtoull(procfile_lineword(ff, l, 5), NULL, 10);
- irq = strtoull(procfile_lineword(ff, l, 6), NULL, 10);
- softirq = strtoull(procfile_lineword(ff, l, 7), NULL, 10);
- steal = strtoull(procfile_lineword(ff, l, 8), NULL, 10);
- if(words >= 10) guest = strtoull(procfile_lineword(ff, l, 9), NULL, 10);
- if(words >= 11) guest_nice = strtoull(procfile_lineword(ff, l, 10), NULL, 10);
-
- char *title = "Core utilization";
- char *type = RRD_TYPE_STAT;
- char *context = "cpu.cpu";
- char *family = "utilization";
- long priority = 1000;
- int isthistotal = 0;
-
- if(strcmp(id, "cpu") == 0) {
- isthistotal = 1;
- type = "system";
- title = "Total CPU utilization";
- context = "system.cpu";
- family = id;
- priority = 100;
- }
-
- if((isthistotal && do_cpu) || (!isthistotal && do_cpu_cores)) {
- st = rrdset_find_bytype(type, id);
- if(!st) {
- st = rrdset_create(type, id, NULL, family, context, title, "percentage", priority, update_every, RRDSET_TYPE_STACKED);
-
- long multiplier = 1;
- long divisor = 1; // sysconf(_SC_CLK_TCK);
-
- rrddim_add(st, "guest_nice", NULL, multiplier, divisor, RRDDIM_PCENT_OVER_DIFF_TOTAL);
- rrddim_add(st, "guest", NULL, multiplier, divisor, RRDDIM_PCENT_OVER_DIFF_TOTAL);
- rrddim_add(st, "steal", NULL, multiplier, divisor, RRDDIM_PCENT_OVER_DIFF_TOTAL);
- rrddim_add(st, "softirq", NULL, multiplier, divisor, RRDDIM_PCENT_OVER_DIFF_TOTAL);
- rrddim_add(st, "irq", NULL, multiplier, divisor, RRDDIM_PCENT_OVER_DIFF_TOTAL);
- rrddim_add(st, "user", NULL, multiplier, divisor, RRDDIM_PCENT_OVER_DIFF_TOTAL);
- rrddim_add(st, "system", NULL, multiplier, divisor, RRDDIM_PCENT_OVER_DIFF_TOTAL);
- rrddim_add(st, "nice", NULL, multiplier, divisor, RRDDIM_PCENT_OVER_DIFF_TOTAL);
- rrddim_add(st, "iowait", NULL, multiplier, divisor, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-
- rrddim_add(st, "idle", NULL, multiplier, divisor, RRDDIM_PCENT_OVER_DIFF_TOTAL);
- rrddim_hide(st, "idle");
- }
- else rrdset_next(st);
-
- rrddim_set(st, "user", user);
- rrddim_set(st, "nice", nice);
- rrddim_set(st, "system", system);
- rrddim_set(st, "idle", idle);
- rrddim_set(st, "iowait", iowait);
- rrddim_set(st, "irq", irq);
- rrddim_set(st, "softirq", softirq);
- rrddim_set(st, "steal", steal);
- rrddim_set(st, "guest", guest);
- rrddim_set(st, "guest_nice", guest_nice);
- rrdset_done(st);
- }
- }
- else if(strcmp(procfile_lineword(ff, l, 0), "intr") == 0) {
- unsigned long long value = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
-
- // --------------------------------------------------------------------
-
- if(do_interrupts) {
- st = rrdset_find_bytype("system", "intr");
- if(!st) {
- st = rrdset_create("system", "intr", NULL, "interrupts", NULL, "CPU Interrupts", "interrupts/s", 900, update_every, RRDSET_TYPE_LINE);
- st->isdetail = 1;
-
- rrddim_add(st, "interrupts", NULL, 1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "interrupts", value);
- rrdset_done(st);
- }
- }
- else if(strcmp(procfile_lineword(ff, l, 0), "ctxt") == 0) {
- unsigned long long value = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
-
- // --------------------------------------------------------------------
-
- if(do_context) {
- st = rrdset_find_bytype("system", "ctxt");
- if(!st) {
- st = rrdset_create("system", "ctxt", NULL, "processes", NULL, "CPU Context Switches", "context switches/s", 800, update_every, RRDSET_TYPE_LINE);
-
- rrddim_add(st, "switches", NULL, 1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "switches", value);
- rrdset_done(st);
- }
- }
- else if(!processes && strcmp(procfile_lineword(ff, l, 0), "processes") == 0) {
- processes = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
- }
- else if(!running && strcmp(procfile_lineword(ff, l, 0), "procs_running") == 0) {
- running = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
- }
- else if(!blocked && strcmp(procfile_lineword(ff, l, 0), "procs_blocked") == 0) {
- blocked = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
- }
- }
-
- // --------------------------------------------------------------------
-
- if(do_forks) {
- st = rrdset_find_bytype("system", "forks");
- if(!st) {
- st = rrdset_create("system", "forks", NULL, "processes", NULL, "Started Processes", "processes/s", 700, update_every, RRDSET_TYPE_LINE);
- st->isdetail = 1;
-
- rrddim_add(st, "started", NULL, 1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "started", processes);
- rrdset_done(st);
- }
-
- // --------------------------------------------------------------------
-
- if(do_processes) {
- st = rrdset_find_bytype("system", "processes");
- if(!st) {
- st = rrdset_create("system", "processes", NULL, "processes", NULL, "System Processes", "processes", 600, update_every, RRDSET_TYPE_LINE);
-
- rrddim_add(st, "running", NULL, 1, 1, RRDDIM_ABSOLUTE);
- rrddim_add(st, "blocked", NULL, -1, 1, RRDDIM_ABSOLUTE);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "running", running);
- rrddim_set(st, "blocked", blocked);
- rrdset_done(st);
- }
-
- return 0;
+ (void)dt;
+
+ static procfile *ff = NULL;
+ static int do_cpu = -1, do_cpu_cores = -1, do_interrupts = -1, do_context = -1, do_forks = -1, do_processes = -1;
+ static uint32_t hash_intr, hash_ctxt, hash_processes, hash_procs_running, hash_procs_blocked;
+
+ if(unlikely(do_cpu == -1)) {
+ do_cpu = config_get_boolean("plugin:proc:/proc/stat", "cpu utilization", 1);
+ do_cpu_cores = config_get_boolean("plugin:proc:/proc/stat", "per cpu core utilization", 1);
+ do_interrupts = config_get_boolean("plugin:proc:/proc/stat", "cpu interrupts", 1);
+ do_context = config_get_boolean("plugin:proc:/proc/stat", "context switches", 1);
+ do_forks = config_get_boolean("plugin:proc:/proc/stat", "processes started", 1);
+ do_processes = config_get_boolean("plugin:proc:/proc/stat", "processes running", 1);
+
+ hash_intr = simple_hash("intr");
+ hash_ctxt = simple_hash("ctxt");
+ hash_processes = simple_hash("processes");
+ hash_procs_running = simple_hash("procs_running");
+ hash_procs_blocked = simple_hash("procs_blocked");
+ }
+
+ if(unlikely(!ff)) {
+ char filename[FILENAME_MAX + 1];
+ snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/stat");
+ ff = procfile_open(config_get("plugin:proc:/proc/stat", "filename to monitor", filename), " \t:", PROCFILE_FLAG_DEFAULT);
+ if(unlikely(!ff)) return 1;
+ }
+
+ ff = procfile_readall(ff);
+ if(unlikely(!ff)) return 0; // we return 0, so that we will retry to open it next time
+
+ uint32_t lines = procfile_lines(ff), l;
+ uint32_t words;
+
+ unsigned long long processes = 0, running = 0 , blocked = 0;
+ RRDSET *st;
+
+ for(l = 0; l < lines ;l++) {
+ char *row_key = procfile_lineword(ff, l, 0);
+ uint32_t hash = simple_hash(row_key);
+
+ // faster strncmp(row_key, "cpu", 3) == 0
+ if(likely(row_key[0] == 'c' && row_key[1] == 'p' && row_key[2] == 'u')) {
+ words = procfile_linewords(ff, l);
+ if(unlikely(words < 9)) {
+ error("Cannot read /proc/stat cpu line. Expected 9 params, read %u.", words);
+ continue;
+ }
+
+ char *id;
+ unsigned long long user = 0, nice = 0, system = 0, idle = 0, iowait = 0, irq = 0, softirq = 0, steal = 0, guest = 0, guest_nice = 0;
+
+ id = row_key;
+ user = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
+ nice = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
+ system = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
+ idle = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
+ iowait = strtoull(procfile_lineword(ff, l, 5), NULL, 10);
+ irq = strtoull(procfile_lineword(ff, l, 6), NULL, 10);
+ softirq = strtoull(procfile_lineword(ff, l, 7), NULL, 10);
+ steal = strtoull(procfile_lineword(ff, l, 8), NULL, 10);
+
+ guest = strtoull(procfile_lineword(ff, l, 9), NULL, 10);
+ user -= guest;
+
+ guest_nice = strtoull(procfile_lineword(ff, l, 10), NULL, 10);
+ nice -= guest_nice;
+
+ char *title, *type, *context, *family;
+ long priority;
+ int isthistotal;
+
+ if(unlikely(strcmp(id, "cpu")) == 0) {
+ title = "Total CPU utilization";
+ type = "system";
+ context = "system.cpu";
+ family = id;
+ priority = 100;
+ isthistotal = 1;
+ }
+ else {
+ title = "Core utilization";
+ type = "cpu";
+ context = "cpu.cpu";
+ family = "utilization";
+ priority = 1000;
+ isthistotal = 0;
+ }
+
+ if(likely((isthistotal && do_cpu) || (!isthistotal && do_cpu_cores))) {
+ st = rrdset_find_bytype(type, id);
+ if(unlikely(!st)) {
+ st = rrdset_create(type, id, NULL, family, context, title, "percentage", priority, update_every, RRDSET_TYPE_STACKED);
+
+ long multiplier = 1;
+ long divisor = 1; // sysconf(_SC_CLK_TCK);
+
+ rrddim_add(st, "guest_nice", NULL, multiplier, divisor, RRDDIM_PCENT_OVER_DIFF_TOTAL);
+ rrddim_add(st, "guest", NULL, multiplier, divisor, RRDDIM_PCENT_OVER_DIFF_TOTAL);
+ rrddim_add(st, "steal", NULL, multiplier, divisor, RRDDIM_PCENT_OVER_DIFF_TOTAL);
+ rrddim_add(st, "softirq", NULL, multiplier, divisor, RRDDIM_PCENT_OVER_DIFF_TOTAL);
+ rrddim_add(st, "irq", NULL, multiplier, divisor, RRDDIM_PCENT_OVER_DIFF_TOTAL);
+ rrddim_add(st, "user", NULL, multiplier, divisor, RRDDIM_PCENT_OVER_DIFF_TOTAL);
+ rrddim_add(st, "system", NULL, multiplier, divisor, RRDDIM_PCENT_OVER_DIFF_TOTAL);
+ rrddim_add(st, "nice", NULL, multiplier, divisor, RRDDIM_PCENT_OVER_DIFF_TOTAL);
+ rrddim_add(st, "iowait", NULL, multiplier, divisor, RRDDIM_PCENT_OVER_DIFF_TOTAL);
+
+ rrddim_add(st, "idle", NULL, multiplier, divisor, RRDDIM_PCENT_OVER_DIFF_TOTAL);
+ rrddim_hide(st, "idle");
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "user", user);
+ rrddim_set(st, "nice", nice);
+ rrddim_set(st, "system", system);
+ rrddim_set(st, "idle", idle);
+ rrddim_set(st, "iowait", iowait);
+ rrddim_set(st, "irq", irq);
+ rrddim_set(st, "softirq", softirq);
+ rrddim_set(st, "steal", steal);
+ rrddim_set(st, "guest", guest);
+ rrddim_set(st, "guest_nice", guest_nice);
+ rrdset_done(st);
+ }
+ }
+ else if(hash == hash_intr && strcmp(row_key, "intr") == 0) {
+ unsigned long long value = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
+
+ // --------------------------------------------------------------------
+
+ if(likely(do_interrupts)) {
+ st = rrdset_find_bytype("system", "intr");
+ if(unlikely(!st)) {
+ st = rrdset_create("system", "intr", NULL, "interrupts", NULL, "CPU Interrupts", "interrupts/s", 900, update_every, RRDSET_TYPE_LINE);
+ st->isdetail = 1;
+
+ rrddim_add(st, "interrupts", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "interrupts", value);
+ rrdset_done(st);
+ }
+ }
+ else if(hash == hash_ctxt && strcmp(row_key, "ctxt") == 0) {
+ unsigned long long value = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
+
+ // --------------------------------------------------------------------
+
+ if(likely(do_context)) {
+ st = rrdset_find_bytype("system", "ctxt");
+ if(unlikely(!st)) {
+ st = rrdset_create("system", "ctxt", NULL, "processes", NULL, "CPU Context Switches", "context switches/s", 800, update_every, RRDSET_TYPE_LINE);
+
+ rrddim_add(st, "switches", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "switches", value);
+ rrdset_done(st);
+ }
+ }
+ else if(hash == hash_processes && !processes && strcmp(row_key, "processes") == 0) {
+ processes = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
+ }
+ else if(hash == hash_procs_running && !running && strcmp(row_key, "procs_running") == 0) {
+ running = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
+ }
+ else if(hash == hash_procs_blocked && !blocked && strcmp(row_key, "procs_blocked") == 0) {
+ blocked = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ if(likely(do_forks)) {
+ st = rrdset_find_bytype("system", "forks");
+ if(unlikely(!st)) {
+ st = rrdset_create("system", "forks", NULL, "processes", NULL, "Started Processes", "processes/s", 700, update_every, RRDSET_TYPE_LINE);
+ st->isdetail = 1;
+
+ rrddim_add(st, "started", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "started", processes);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(likely(do_processes)) {
+ st = rrdset_find_bytype("system", "processes");
+ if(unlikely(!st)) {
+ st = rrdset_create("system", "processes", NULL, "processes", NULL, "System Processes", "processes", 600, update_every, RRDSET_TYPE_LINE);
+
+ rrddim_add(st, "running", NULL, 1, 1, RRDDIM_ABSOLUTE);
+ rrddim_add(st, "blocked", NULL, -1, 1, RRDDIM_ABSOLUTE);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "running", running);
+ rrddim_set(st, "blocked", blocked);
+ rrdset_done(st);
+ }
+
+ return 0;
}
diff --git a/src/proc_sys_kernel_random_entropy_avail.c b/src/proc_sys_kernel_random_entropy_avail.c
index d7d1e8261..9515dad61 100644
--- a/src/proc_sys_kernel_random_entropy_avail.c
+++ b/src/proc_sys_kernel_random_entropy_avail.c
@@ -1,41 +1,31 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-
#include "common.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "rrd.h"
-#include "plugin_proc.h"
int do_proc_sys_kernel_random_entropy_avail(int update_every, unsigned long long dt) {
- static procfile *ff = NULL;
+ static procfile *ff = NULL;
- if(dt) {} ;
+ if(dt) {} ;
- if(!ff) {
- char filename[FILENAME_MAX + 1];
- snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/sys/kernel/random/entropy_avail");
- ff = procfile_open(config_get("plugin:proc:/proc/sys/kernel/random/entropy_avail", "filename to monitor", filename), "", PROCFILE_FLAG_DEFAULT);
- }
- if(!ff) return 1;
+ if(!ff) {
+ char filename[FILENAME_MAX + 1];
+ snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/sys/kernel/random/entropy_avail");
+ ff = procfile_open(config_get("plugin:proc:/proc/sys/kernel/random/entropy_avail", "filename to monitor", filename), "", PROCFILE_FLAG_DEFAULT);
+ }
+ if(!ff) return 1;
- ff = procfile_readall(ff);
- if(!ff) return 0; // we return 0, so that we will retry to open it next time
+ ff = procfile_readall(ff);
+ if(!ff) return 0; // we return 0, so that we will retry to open it next time
- unsigned long long entropy = strtoull(procfile_lineword(ff, 0, 0), NULL, 10);
+ unsigned long long entropy = strtoull(procfile_lineword(ff, 0, 0), NULL, 10);
- RRDSET *st = rrdset_find_bytype("system", "entropy");
- if(!st) {
- st = rrdset_create("system", "entropy", NULL, "entropy", NULL, "Available Entropy", "entropy", 1000, update_every, RRDSET_TYPE_LINE);
- rrddim_add(st, "entropy", NULL, 1, 1, RRDDIM_ABSOLUTE);
- }
- else rrdset_next(st);
+ RRDSET *st = rrdset_find_bytype("system", "entropy");
+ if(!st) {
+ st = rrdset_create("system", "entropy", NULL, "entropy", NULL, "Available Entropy", "entropy", 1000, update_every, RRDSET_TYPE_LINE);
+ rrddim_add(st, "entropy", NULL, 1, 1, RRDDIM_ABSOLUTE);
+ }
+ else rrdset_next(st);
- rrddim_set(st, "entropy", entropy);
- rrdset_done(st);
+ rrddim_set(st, "entropy", entropy);
+ rrdset_done(st);
- return 0;
+ return 0;
}
diff --git a/src/proc_vmstat.c b/src/proc_vmstat.c
index 7b20ed8cf..f25d50c50 100644
--- a/src/proc_vmstat.c
+++ b/src/proc_vmstat.c
@@ -1,483 +1,470 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
#include "common.h"
-#include "log.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "rrd.h"
-#include "plugin_proc.h"
int do_proc_vmstat(int update_every, unsigned long long dt) {
- static procfile *ff = NULL;
- static int do_swapio = -1, do_io = -1, do_pgfaults = -1, gen_hashes = -1;
+ static procfile *ff = NULL;
+ static int do_swapio = -1, do_io = -1, do_pgfaults = -1, gen_hashes = -1;
- // static uint32_t hash_allocstall = -1;
- // static uint32_t hash_compact_blocks_moved = -1;
- // static uint32_t hash_compact_fail = -1;
- // static uint32_t hash_compact_pagemigrate_failed = -1;
- // static uint32_t hash_compact_pages_moved = -1;
- // static uint32_t hash_compact_stall = -1;
- // static uint32_t hash_compact_success = -1;
- // static uint32_t hash_htlb_buddy_alloc_fail = -1;
- // static uint32_t hash_htlb_buddy_alloc_success = -1;
- // static uint32_t hash_kswapd_high_wmark_hit_quickly = -1;
- // static uint32_t hash_kswapd_inodesteal = -1;
- // static uint32_t hash_kswapd_low_wmark_hit_quickly = -1;
- // static uint32_t hash_kswapd_skip_congestion_wait = -1;
- // static uint32_t hash_nr_active_anon = -1;
- // static uint32_t hash_nr_active_file = -1;
- // static uint32_t hash_nr_anon_pages = -1;
- // static uint32_t hash_nr_anon_transparent_hugepages = -1;
- // static uint32_t hash_nr_bounce = -1;
- // static uint32_t hash_nr_dirtied = -1;
- // static uint32_t hash_nr_dirty = -1;
- // static uint32_t hash_nr_dirty_background_threshold = -1;
- // static uint32_t hash_nr_dirty_threshold = -1;
- // static uint32_t hash_nr_file_pages = -1;
- // static uint32_t hash_nr_free_pages = -1;
- // static uint32_t hash_nr_inactive_anon = -1;
- // static uint32_t hash_nr_inactive_file = -1;
- // static uint32_t hash_nr_isolated_anon = -1;
- // static uint32_t hash_nr_isolated_file = -1;
- // static uint32_t hash_nr_kernel_stack = -1;
- // static uint32_t hash_nr_mapped = -1;
- // static uint32_t hash_nr_mlock = -1;
- // static uint32_t hash_nr_page_table_pages = -1;
- // static uint32_t hash_nr_shmem = -1;
- // static uint32_t hash_nr_slab_reclaimable = -1;
- // static uint32_t hash_nr_slab_unreclaimable = -1;
- // static uint32_t hash_nr_unevictable = -1;
- // static uint32_t hash_nr_unstable = -1;
- // static uint32_t hash_nr_vmscan_immediate_reclaim = -1;
- // static uint32_t hash_nr_vmscan_write = -1;
- // static uint32_t hash_nr_writeback = -1;
- // static uint32_t hash_nr_writeback_temp = -1;
- // static uint32_t hash_nr_written = -1;
- // static uint32_t hash_pageoutrun = -1;
- // static uint32_t hash_pgactivate = -1;
- // static uint32_t hash_pgalloc_dma = -1;
- // static uint32_t hash_pgalloc_dma32 = -1;
- // static uint32_t hash_pgalloc_movable = -1;
- // static uint32_t hash_pgalloc_normal = -1;
- // static uint32_t hash_pgdeactivate = -1;
- static uint32_t hash_pgfault = -1;
- // static uint32_t hash_pgfree = -1;
- // static uint32_t hash_pginodesteal = -1;
- static uint32_t hash_pgmajfault = -1;
- static uint32_t hash_pgpgin = -1;
- static uint32_t hash_pgpgout = -1;
- // static uint32_t hash_pgrefill_dma = -1;
- // static uint32_t hash_pgrefill_dma32 = -1;
- // static uint32_t hash_pgrefill_movable = -1;
- // static uint32_t hash_pgrefill_normal = -1;
- // static uint32_t hash_pgrotated = -1;
- // static uint32_t hash_pgscan_direct_dma = -1;
- // static uint32_t hash_pgscan_direct_dma32 = -1;
- // static uint32_t hash_pgscan_direct_movable = -1;
- // static uint32_t hash_pgscan_direct_normal = -1;
- // static uint32_t hash_pgscan_kswapd_dma = -1;
- // static uint32_t hash_pgscan_kswapd_dma32 = -1;
- // static uint32_t hash_pgscan_kswapd_movable = -1;
- // static uint32_t hash_pgscan_kswapd_normal = -1;
- // static uint32_t hash_pgsteal_direct_dma = -1;
- // static uint32_t hash_pgsteal_direct_dma32 = -1;
- // static uint32_t hash_pgsteal_direct_movable = -1;
- // static uint32_t hash_pgsteal_direct_normal = -1;
- // static uint32_t hash_pgsteal_kswapd_dma = -1;
- // static uint32_t hash_pgsteal_kswapd_dma32 = -1;
- // static uint32_t hash_pgsteal_kswapd_movable = -1;
- // static uint32_t hash_pgsteal_kswapd_normal = -1;
- static uint32_t hash_pswpin = -1;
- static uint32_t hash_pswpout = -1;
- // static uint32_t hash_slabs_scanned = -1;
- // static uint32_t hash_thp_collapse_alloc = -1;
- // static uint32_t hash_thp_collapse_alloc_failed = -1;
- // static uint32_t hash_thp_fault_alloc = -1;
- // static uint32_t hash_thp_fault_fallback = -1;
- // static uint32_t hash_thp_split = -1;
- // static uint32_t hash_unevictable_pgs_cleared = -1;
- // static uint32_t hash_unevictable_pgs_culled = -1;
- // static uint32_t hash_unevictable_pgs_mlocked = -1;
- // static uint32_t hash_unevictable_pgs_mlockfreed = -1;
- // static uint32_t hash_unevictable_pgs_munlocked = -1;
- // static uint32_t hash_unevictable_pgs_rescued = -1;
- // static uint32_t hash_unevictable_pgs_scanned = -1;
- // static uint32_t hash_unevictable_pgs_stranded = -1;
+ // static uint32_t hash_allocstall = -1;
+ // static uint32_t hash_compact_blocks_moved = -1;
+ // static uint32_t hash_compact_fail = -1;
+ // static uint32_t hash_compact_pagemigrate_failed = -1;
+ // static uint32_t hash_compact_pages_moved = -1;
+ // static uint32_t hash_compact_stall = -1;
+ // static uint32_t hash_compact_success = -1;
+ // static uint32_t hash_htlb_buddy_alloc_fail = -1;
+ // static uint32_t hash_htlb_buddy_alloc_success = -1;
+ // static uint32_t hash_kswapd_high_wmark_hit_quickly = -1;
+ // static uint32_t hash_kswapd_inodesteal = -1;
+ // static uint32_t hash_kswapd_low_wmark_hit_quickly = -1;
+ // static uint32_t hash_kswapd_skip_congestion_wait = -1;
+ // static uint32_t hash_nr_active_anon = -1;
+ // static uint32_t hash_nr_active_file = -1;
+ // static uint32_t hash_nr_anon_pages = -1;
+ // static uint32_t hash_nr_anon_transparent_hugepages = -1;
+ // static uint32_t hash_nr_bounce = -1;
+ // static uint32_t hash_nr_dirtied = -1;
+ // static uint32_t hash_nr_dirty = -1;
+ // static uint32_t hash_nr_dirty_background_threshold = -1;
+ // static uint32_t hash_nr_dirty_threshold = -1;
+ // static uint32_t hash_nr_file_pages = -1;
+ // static uint32_t hash_nr_free_pages = -1;
+ // static uint32_t hash_nr_inactive_anon = -1;
+ // static uint32_t hash_nr_inactive_file = -1;
+ // static uint32_t hash_nr_isolated_anon = -1;
+ // static uint32_t hash_nr_isolated_file = -1;
+ // static uint32_t hash_nr_kernel_stack = -1;
+ // static uint32_t hash_nr_mapped = -1;
+ // static uint32_t hash_nr_mlock = -1;
+ // static uint32_t hash_nr_page_table_pages = -1;
+ // static uint32_t hash_nr_shmem = -1;
+ // static uint32_t hash_nr_slab_reclaimable = -1;
+ // static uint32_t hash_nr_slab_unreclaimable = -1;
+ // static uint32_t hash_nr_unevictable = -1;
+ // static uint32_t hash_nr_unstable = -1;
+ // static uint32_t hash_nr_vmscan_immediate_reclaim = -1;
+ // static uint32_t hash_nr_vmscan_write = -1;
+ // static uint32_t hash_nr_writeback = -1;
+ // static uint32_t hash_nr_writeback_temp = -1;
+ // static uint32_t hash_nr_written = -1;
+ // static uint32_t hash_pageoutrun = -1;
+ // static uint32_t hash_pgactivate = -1;
+ // static uint32_t hash_pgalloc_dma = -1;
+ // static uint32_t hash_pgalloc_dma32 = -1;
+ // static uint32_t hash_pgalloc_movable = -1;
+ // static uint32_t hash_pgalloc_normal = -1;
+ // static uint32_t hash_pgdeactivate = -1;
+ static uint32_t hash_pgfault = -1;
+ // static uint32_t hash_pgfree = -1;
+ // static uint32_t hash_pginodesteal = -1;
+ static uint32_t hash_pgmajfault = -1;
+ static uint32_t hash_pgpgin = -1;
+ static uint32_t hash_pgpgout = -1;
+ // static uint32_t hash_pgrefill_dma = -1;
+ // static uint32_t hash_pgrefill_dma32 = -1;
+ // static uint32_t hash_pgrefill_movable = -1;
+ // static uint32_t hash_pgrefill_normal = -1;
+ // static uint32_t hash_pgrotated = -1;
+ // static uint32_t hash_pgscan_direct_dma = -1;
+ // static uint32_t hash_pgscan_direct_dma32 = -1;
+ // static uint32_t hash_pgscan_direct_movable = -1;
+ // static uint32_t hash_pgscan_direct_normal = -1;
+ // static uint32_t hash_pgscan_kswapd_dma = -1;
+ // static uint32_t hash_pgscan_kswapd_dma32 = -1;
+ // static uint32_t hash_pgscan_kswapd_movable = -1;
+ // static uint32_t hash_pgscan_kswapd_normal = -1;
+ // static uint32_t hash_pgsteal_direct_dma = -1;
+ // static uint32_t hash_pgsteal_direct_dma32 = -1;
+ // static uint32_t hash_pgsteal_direct_movable = -1;
+ // static uint32_t hash_pgsteal_direct_normal = -1;
+ // static uint32_t hash_pgsteal_kswapd_dma = -1;
+ // static uint32_t hash_pgsteal_kswapd_dma32 = -1;
+ // static uint32_t hash_pgsteal_kswapd_movable = -1;
+ // static uint32_t hash_pgsteal_kswapd_normal = -1;
+ static uint32_t hash_pswpin = -1;
+ static uint32_t hash_pswpout = -1;
+ // static uint32_t hash_slabs_scanned = -1;
+ // static uint32_t hash_thp_collapse_alloc = -1;
+ // static uint32_t hash_thp_collapse_alloc_failed = -1;
+ // static uint32_t hash_thp_fault_alloc = -1;
+ // static uint32_t hash_thp_fault_fallback = -1;
+ // static uint32_t hash_thp_split = -1;
+ // static uint32_t hash_unevictable_pgs_cleared = -1;
+ // static uint32_t hash_unevictable_pgs_culled = -1;
+ // static uint32_t hash_unevictable_pgs_mlocked = -1;
+ // static uint32_t hash_unevictable_pgs_mlockfreed = -1;
+ // static uint32_t hash_unevictable_pgs_munlocked = -1;
+ // static uint32_t hash_unevictable_pgs_rescued = -1;
+ // static uint32_t hash_unevictable_pgs_scanned = -1;
+ // static uint32_t hash_unevictable_pgs_stranded = -1;
- if(gen_hashes != 1) {
- gen_hashes = 1;
- // hash_allocstall = simple_hash("allocstall");
- // hash_compact_blocks_moved = simple_hash("compact_blocks_moved");
- // hash_compact_fail = simple_hash("compact_fail");
- // hash_compact_pagemigrate_failed = simple_hash("compact_pagemigrate_failed");
- // hash_compact_pages_moved = simple_hash("compact_pages_moved");
- // hash_compact_stall = simple_hash("compact_stall");
- // hash_compact_success = simple_hash("compact_success");
- // hash_htlb_buddy_alloc_fail = simple_hash("htlb_buddy_alloc_fail");
- // hash_htlb_buddy_alloc_success = simple_hash("htlb_buddy_alloc_success");
- // hash_kswapd_high_wmark_hit_quickly = simple_hash("kswapd_high_wmark_hit_quickly");
- // hash_kswapd_inodesteal = simple_hash("kswapd_inodesteal");
- // hash_kswapd_low_wmark_hit_quickly = simple_hash("kswapd_low_wmark_hit_quickly");
- // hash_kswapd_skip_congestion_wait = simple_hash("kswapd_skip_congestion_wait");
- // hash_nr_active_anon = simple_hash("nr_active_anon");
- // hash_nr_active_file = simple_hash("nr_active_file");
- // hash_nr_anon_pages = simple_hash("nr_anon_pages");
- // hash_nr_anon_transparent_hugepages = simple_hash("nr_anon_transparent_hugepages");
- // hash_nr_bounce = simple_hash("nr_bounce");
- // hash_nr_dirtied = simple_hash("nr_dirtied");
- // hash_nr_dirty = simple_hash("nr_dirty");
- // hash_nr_dirty_background_threshold = simple_hash("nr_dirty_background_threshold");
- // hash_nr_dirty_threshold = simple_hash("nr_dirty_threshold");
- // hash_nr_file_pages = simple_hash("nr_file_pages");
- // hash_nr_free_pages = simple_hash("nr_free_pages");
- // hash_nr_inactive_anon = simple_hash("nr_inactive_anon");
- // hash_nr_inactive_file = simple_hash("nr_inactive_file");
- // hash_nr_isolated_anon = simple_hash("nr_isolated_anon");
- // hash_nr_isolated_file = simple_hash("nr_isolated_file");
- // hash_nr_kernel_stack = simple_hash("nr_kernel_stack");
- // hash_nr_mapped = simple_hash("nr_mapped");
- // hash_nr_mlock = simple_hash("nr_mlock");
- // hash_nr_page_table_pages = simple_hash("nr_page_table_pages");
- // hash_nr_shmem = simple_hash("nr_shmem");
- // hash_nr_slab_reclaimable = simple_hash("nr_slab_reclaimable");
- // hash_nr_slab_unreclaimable = simple_hash("nr_slab_unreclaimable");
- // hash_nr_unevictable = simple_hash("nr_unevictable");
- // hash_nr_unstable = simple_hash("nr_unstable");
- // hash_nr_vmscan_immediate_reclaim = simple_hash("nr_vmscan_immediate_reclaim");
- // hash_nr_vmscan_write = simple_hash("nr_vmscan_write");
- // hash_nr_writeback = simple_hash("nr_writeback");
- // hash_nr_writeback_temp = simple_hash("nr_writeback_temp");
- // hash_nr_written = simple_hash("nr_written");
- // hash_pageoutrun = simple_hash("pageoutrun");
- // hash_pgactivate = simple_hash("pgactivate");
- // hash_pgalloc_dma = simple_hash("pgalloc_dma");
- // hash_pgalloc_dma32 = simple_hash("pgalloc_dma32");
- // hash_pgalloc_movable = simple_hash("pgalloc_movable");
- // hash_pgalloc_normal = simple_hash("pgalloc_normal");
- // hash_pgdeactivate = simple_hash("pgdeactivate");
- hash_pgfault = simple_hash("pgfault");
- // hash_pgfree = simple_hash("pgfree");
- // hash_pginodesteal = simple_hash("pginodesteal");
- hash_pgmajfault = simple_hash("pgmajfault");
- hash_pgpgin = simple_hash("pgpgin");
- hash_pgpgout = simple_hash("pgpgout");
- // hash_pgrefill_dma = simple_hash("pgrefill_dma");
- // hash_pgrefill_dma32 = simple_hash("pgrefill_dma32");
- // hash_pgrefill_movable = simple_hash("pgrefill_movable");
- // hash_pgrefill_normal = simple_hash("pgrefill_normal");
- // hash_pgrotated = simple_hash("pgrotated");
- // hash_pgscan_direct_dma = simple_hash("pgscan_direct_dma");
- // hash_pgscan_direct_dma32 = simple_hash("pgscan_direct_dma32");
- // hash_pgscan_direct_movable = simple_hash("pgscan_direct_movable");
- // hash_pgscan_direct_normal = simple_hash("pgscan_direct_normal");
- // hash_pgscan_kswapd_dma = simple_hash("pgscan_kswapd_dma");
- // hash_pgscan_kswapd_dma32 = simple_hash("pgscan_kswapd_dma32");
- // hash_pgscan_kswapd_movable = simple_hash("pgscan_kswapd_movable");
- // hash_pgscan_kswapd_normal = simple_hash("pgscan_kswapd_normal");
- // hash_pgsteal_direct_dma = simple_hash("pgsteal_direct_dma");
- // hash_pgsteal_direct_dma32 = simple_hash("pgsteal_direct_dma32");
- // hash_pgsteal_direct_movable = simple_hash("pgsteal_direct_movable");
- // hash_pgsteal_direct_normal = simple_hash("pgsteal_direct_normal");
- // hash_pgsteal_kswapd_dma = simple_hash("pgsteal_kswapd_dma");
- // hash_pgsteal_kswapd_dma32 = simple_hash("pgsteal_kswapd_dma32");
- // hash_pgsteal_kswapd_movable = simple_hash("pgsteal_kswapd_movable");
- // hash_pgsteal_kswapd_normal = simple_hash("pgsteal_kswapd_normal");
- hash_pswpin = simple_hash("pswpin");
- hash_pswpout = simple_hash("pswpout");
- // hash_slabs_scanned = simple_hash("slabs_scanned");
- // hash_thp_collapse_alloc = simple_hash("thp_collapse_alloc");
- // hash_thp_collapse_alloc_failed = simple_hash("thp_collapse_alloc_failed");
- // hash_thp_fault_alloc = simple_hash("thp_fault_alloc");
- // hash_thp_fault_fallback = simple_hash("thp_fault_fallback");
- // hash_thp_split = simple_hash("thp_split");
- // hash_unevictable_pgs_cleared = simple_hash("unevictable_pgs_cleared");
- // hash_unevictable_pgs_culled = simple_hash("unevictable_pgs_culled");
- // hash_unevictable_pgs_mlocked = simple_hash("unevictable_pgs_mlocked");
- // hash_unevictable_pgs_mlockfreed = simple_hash("unevictable_pgs_mlockfreed");
- // hash_unevictable_pgs_munlocked = simple_hash("unevictable_pgs_munlocked");
- // hash_unevictable_pgs_rescued = simple_hash("unevictable_pgs_rescued");
- // hash_unevictable_pgs_scanned = simple_hash("unevictable_pgs_scanned");
- // hash_unevictable_pgs_stranded = simple_hash("unevictable_pgs_stranded");
- }
+ if(gen_hashes != 1) {
+ gen_hashes = 1;
+ // hash_allocstall = simple_hash("allocstall");
+ // hash_compact_blocks_moved = simple_hash("compact_blocks_moved");
+ // hash_compact_fail = simple_hash("compact_fail");
+ // hash_compact_pagemigrate_failed = simple_hash("compact_pagemigrate_failed");
+ // hash_compact_pages_moved = simple_hash("compact_pages_moved");
+ // hash_compact_stall = simple_hash("compact_stall");
+ // hash_compact_success = simple_hash("compact_success");
+ // hash_htlb_buddy_alloc_fail = simple_hash("htlb_buddy_alloc_fail");
+ // hash_htlb_buddy_alloc_success = simple_hash("htlb_buddy_alloc_success");
+ // hash_kswapd_high_wmark_hit_quickly = simple_hash("kswapd_high_wmark_hit_quickly");
+ // hash_kswapd_inodesteal = simple_hash("kswapd_inodesteal");
+ // hash_kswapd_low_wmark_hit_quickly = simple_hash("kswapd_low_wmark_hit_quickly");
+ // hash_kswapd_skip_congestion_wait = simple_hash("kswapd_skip_congestion_wait");
+ // hash_nr_active_anon = simple_hash("nr_active_anon");
+ // hash_nr_active_file = simple_hash("nr_active_file");
+ // hash_nr_anon_pages = simple_hash("nr_anon_pages");
+ // hash_nr_anon_transparent_hugepages = simple_hash("nr_anon_transparent_hugepages");
+ // hash_nr_bounce = simple_hash("nr_bounce");
+ // hash_nr_dirtied = simple_hash("nr_dirtied");
+ // hash_nr_dirty = simple_hash("nr_dirty");
+ // hash_nr_dirty_background_threshold = simple_hash("nr_dirty_background_threshold");
+ // hash_nr_dirty_threshold = simple_hash("nr_dirty_threshold");
+ // hash_nr_file_pages = simple_hash("nr_file_pages");
+ // hash_nr_free_pages = simple_hash("nr_free_pages");
+ // hash_nr_inactive_anon = simple_hash("nr_inactive_anon");
+ // hash_nr_inactive_file = simple_hash("nr_inactive_file");
+ // hash_nr_isolated_anon = simple_hash("nr_isolated_anon");
+ // hash_nr_isolated_file = simple_hash("nr_isolated_file");
+ // hash_nr_kernel_stack = simple_hash("nr_kernel_stack");
+ // hash_nr_mapped = simple_hash("nr_mapped");
+ // hash_nr_mlock = simple_hash("nr_mlock");
+ // hash_nr_page_table_pages = simple_hash("nr_page_table_pages");
+ // hash_nr_shmem = simple_hash("nr_shmem");
+ // hash_nr_slab_reclaimable = simple_hash("nr_slab_reclaimable");
+ // hash_nr_slab_unreclaimable = simple_hash("nr_slab_unreclaimable");
+ // hash_nr_unevictable = simple_hash("nr_unevictable");
+ // hash_nr_unstable = simple_hash("nr_unstable");
+ // hash_nr_vmscan_immediate_reclaim = simple_hash("nr_vmscan_immediate_reclaim");
+ // hash_nr_vmscan_write = simple_hash("nr_vmscan_write");
+ // hash_nr_writeback = simple_hash("nr_writeback");
+ // hash_nr_writeback_temp = simple_hash("nr_writeback_temp");
+ // hash_nr_written = simple_hash("nr_written");
+ // hash_pageoutrun = simple_hash("pageoutrun");
+ // hash_pgactivate = simple_hash("pgactivate");
+ // hash_pgalloc_dma = simple_hash("pgalloc_dma");
+ // hash_pgalloc_dma32 = simple_hash("pgalloc_dma32");
+ // hash_pgalloc_movable = simple_hash("pgalloc_movable");
+ // hash_pgalloc_normal = simple_hash("pgalloc_normal");
+ // hash_pgdeactivate = simple_hash("pgdeactivate");
+ hash_pgfault = simple_hash("pgfault");
+ // hash_pgfree = simple_hash("pgfree");
+ // hash_pginodesteal = simple_hash("pginodesteal");
+ hash_pgmajfault = simple_hash("pgmajfault");
+ hash_pgpgin = simple_hash("pgpgin");
+ hash_pgpgout = simple_hash("pgpgout");
+ // hash_pgrefill_dma = simple_hash("pgrefill_dma");
+ // hash_pgrefill_dma32 = simple_hash("pgrefill_dma32");
+ // hash_pgrefill_movable = simple_hash("pgrefill_movable");
+ // hash_pgrefill_normal = simple_hash("pgrefill_normal");
+ // hash_pgrotated = simple_hash("pgrotated");
+ // hash_pgscan_direct_dma = simple_hash("pgscan_direct_dma");
+ // hash_pgscan_direct_dma32 = simple_hash("pgscan_direct_dma32");
+ // hash_pgscan_direct_movable = simple_hash("pgscan_direct_movable");
+ // hash_pgscan_direct_normal = simple_hash("pgscan_direct_normal");
+ // hash_pgscan_kswapd_dma = simple_hash("pgscan_kswapd_dma");
+ // hash_pgscan_kswapd_dma32 = simple_hash("pgscan_kswapd_dma32");
+ // hash_pgscan_kswapd_movable = simple_hash("pgscan_kswapd_movable");
+ // hash_pgscan_kswapd_normal = simple_hash("pgscan_kswapd_normal");
+ // hash_pgsteal_direct_dma = simple_hash("pgsteal_direct_dma");
+ // hash_pgsteal_direct_dma32 = simple_hash("pgsteal_direct_dma32");
+ // hash_pgsteal_direct_movable = simple_hash("pgsteal_direct_movable");
+ // hash_pgsteal_direct_normal = simple_hash("pgsteal_direct_normal");
+ // hash_pgsteal_kswapd_dma = simple_hash("pgsteal_kswapd_dma");
+ // hash_pgsteal_kswapd_dma32 = simple_hash("pgsteal_kswapd_dma32");
+ // hash_pgsteal_kswapd_movable = simple_hash("pgsteal_kswapd_movable");
+ // hash_pgsteal_kswapd_normal = simple_hash("pgsteal_kswapd_normal");
+ hash_pswpin = simple_hash("pswpin");
+ hash_pswpout = simple_hash("pswpout");
+ // hash_slabs_scanned = simple_hash("slabs_scanned");
+ // hash_thp_collapse_alloc = simple_hash("thp_collapse_alloc");
+ // hash_thp_collapse_alloc_failed = simple_hash("thp_collapse_alloc_failed");
+ // hash_thp_fault_alloc = simple_hash("thp_fault_alloc");
+ // hash_thp_fault_fallback = simple_hash("thp_fault_fallback");
+ // hash_thp_split = simple_hash("thp_split");
+ // hash_unevictable_pgs_cleared = simple_hash("unevictable_pgs_cleared");
+ // hash_unevictable_pgs_culled = simple_hash("unevictable_pgs_culled");
+ // hash_unevictable_pgs_mlocked = simple_hash("unevictable_pgs_mlocked");
+ // hash_unevictable_pgs_mlockfreed = simple_hash("unevictable_pgs_mlockfreed");
+ // hash_unevictable_pgs_munlocked = simple_hash("unevictable_pgs_munlocked");
+ // hash_unevictable_pgs_rescued = simple_hash("unevictable_pgs_rescued");
+ // hash_unevictable_pgs_scanned = simple_hash("unevictable_pgs_scanned");
+ // hash_unevictable_pgs_stranded = simple_hash("unevictable_pgs_stranded");
+ }
- if(do_swapio == -1) do_swapio = config_get_boolean("plugin:proc:/proc/vmstat", "swap i/o", 1);
- if(do_io == -1) do_io = config_get_boolean("plugin:proc:/proc/vmstat", "disk i/o", 1);
- if(do_pgfaults == -1) do_pgfaults = config_get_boolean("plugin:proc:/proc/vmstat", "memory page faults", 1);
+ if(do_swapio == -1) do_swapio = config_get_boolean("plugin:proc:/proc/vmstat", "swap i/o", 1);
+ if(do_io == -1) do_io = config_get_boolean("plugin:proc:/proc/vmstat", "disk i/o", 1);
+ if(do_pgfaults == -1) do_pgfaults = config_get_boolean("plugin:proc:/proc/vmstat", "memory page faults", 1);
- if(dt) {};
+ if(dt) {};
- if(!ff) {
- char filename[FILENAME_MAX + 1];
- snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/vmstat");
- ff = procfile_open(config_get("plugin:proc:/proc/vmstat", "filename to monitor", filename), " \t:", PROCFILE_FLAG_DEFAULT);
- }
- if(!ff) return 1;
+ if(!ff) {
+ char filename[FILENAME_MAX + 1];
+ snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/vmstat");
+ ff = procfile_open(config_get("plugin:proc:/proc/vmstat", "filename to monitor", filename), " \t:", PROCFILE_FLAG_DEFAULT);
+ }
+ if(!ff) return 1;
- ff = procfile_readall(ff);
- if(!ff) return 0; // we return 0, so that we will retry to open it next time
+ ff = procfile_readall(ff);
+ if(!ff) return 0; // we return 0, so that we will retry to open it next time
- uint32_t lines = procfile_lines(ff), l;
- uint32_t words;
+ uint32_t lines = procfile_lines(ff), l;
+ uint32_t words;
- // unsigned long long allocstall = 0ULL;
- // unsigned long long compact_blocks_moved = 0ULL;
- // unsigned long long compact_fail = 0ULL;
- // unsigned long long compact_pagemigrate_failed = 0ULL;
- // unsigned long long compact_pages_moved = 0ULL;
- // unsigned long long compact_stall = 0ULL;
- // unsigned long long compact_success = 0ULL;
- // unsigned long long htlb_buddy_alloc_fail = 0ULL;
- // unsigned long long htlb_buddy_alloc_success = 0ULL;
- // unsigned long long kswapd_high_wmark_hit_quickly = 0ULL;
- // unsigned long long kswapd_inodesteal = 0ULL;
- // unsigned long long kswapd_low_wmark_hit_quickly = 0ULL;
- // unsigned long long kswapd_skip_congestion_wait = 0ULL;
- // unsigned long long nr_active_anon = 0ULL;
- // unsigned long long nr_active_file = 0ULL;
- // unsigned long long nr_anon_pages = 0ULL;
- // unsigned long long nr_anon_transparent_hugepages = 0ULL;
- // unsigned long long nr_bounce = 0ULL;
- // unsigned long long nr_dirtied = 0ULL;
- // unsigned long long nr_dirty = 0ULL;
- // unsigned long long nr_dirty_background_threshold = 0ULL;
- // unsigned long long nr_dirty_threshold = 0ULL;
- // unsigned long long nr_file_pages = 0ULL;
- // unsigned long long nr_free_pages = 0ULL;
- // unsigned long long nr_inactive_anon = 0ULL;
- // unsigned long long nr_inactive_file = 0ULL;
- // unsigned long long nr_isolated_anon = 0ULL;
- // unsigned long long nr_isolated_file = 0ULL;
- // unsigned long long nr_kernel_stack = 0ULL;
- // unsigned long long nr_mapped = 0ULL;
- // unsigned long long nr_mlock = 0ULL;
- // unsigned long long nr_page_table_pages = 0ULL;
- // unsigned long long nr_shmem = 0ULL;
- // unsigned long long nr_slab_reclaimable = 0ULL;
- // unsigned long long nr_slab_unreclaimable = 0ULL;
- // unsigned long long nr_unevictable = 0ULL;
- // unsigned long long nr_unstable = 0ULL;
- // unsigned long long nr_vmscan_immediate_reclaim = 0ULL;
- // unsigned long long nr_vmscan_write = 0ULL;
- // unsigned long long nr_writeback = 0ULL;
- // unsigned long long nr_writeback_temp = 0ULL;
- // unsigned long long nr_written = 0ULL;
- // unsigned long long pageoutrun = 0ULL;
- // unsigned long long pgactivate = 0ULL;
- // unsigned long long pgalloc_dma = 0ULL;
- // unsigned long long pgalloc_dma32 = 0ULL;
- // unsigned long long pgalloc_movable = 0ULL;
- // unsigned long long pgalloc_normal = 0ULL;
- // unsigned long long pgdeactivate = 0ULL;
- unsigned long long pgfault = 0ULL;
- // unsigned long long pgfree = 0ULL;
- // unsigned long long pginodesteal = 0ULL;
- unsigned long long pgmajfault = 0ULL;
- unsigned long long pgpgin = 0ULL;
- unsigned long long pgpgout = 0ULL;
- // unsigned long long pgrefill_dma = 0ULL;
- // unsigned long long pgrefill_dma32 = 0ULL;
- // unsigned long long pgrefill_movable = 0ULL;
- // unsigned long long pgrefill_normal = 0ULL;
- // unsigned long long pgrotated = 0ULL;
- // unsigned long long pgscan_direct_dma = 0ULL;
- // unsigned long long pgscan_direct_dma32 = 0ULL;
- // unsigned long long pgscan_direct_movable = 0ULL;
- // unsigned long long pgscan_direct_normal = 0ULL;
- // unsigned long long pgscan_kswapd_dma = 0ULL;
- // unsigned long long pgscan_kswapd_dma32 = 0ULL;
- // unsigned long long pgscan_kswapd_movable = 0ULL;
- // unsigned long long pgscan_kswapd_normal = 0ULL;
- // unsigned long long pgsteal_direct_dma = 0ULL;
- // unsigned long long pgsteal_direct_dma32 = 0ULL;
- // unsigned long long pgsteal_direct_movable = 0ULL;
- // unsigned long long pgsteal_direct_normal = 0ULL;
- // unsigned long long pgsteal_kswapd_dma = 0ULL;
- // unsigned long long pgsteal_kswapd_dma32 = 0ULL;
- // unsigned long long pgsteal_kswapd_movable = 0ULL;
- // unsigned long long pgsteal_kswapd_normal = 0ULL;
- unsigned long long pswpin = 0ULL;
- unsigned long long pswpout = 0ULL;
- // unsigned long long slabs_scanned = 0ULL;
- // unsigned long long thp_collapse_alloc = 0ULL;
- // unsigned long long thp_collapse_alloc_failed = 0ULL;
- // unsigned long long thp_fault_alloc = 0ULL;
- // unsigned long long thp_fault_fallback = 0ULL;
- // unsigned long long thp_split = 0ULL;
- // unsigned long long unevictable_pgs_cleared = 0ULL;
- // unsigned long long unevictable_pgs_culled = 0ULL;
- // unsigned long long unevictable_pgs_mlocked = 0ULL;
- // unsigned long long unevictable_pgs_mlockfreed = 0ULL;
- // unsigned long long unevictable_pgs_munlocked = 0ULL;
- // unsigned long long unevictable_pgs_rescued = 0ULL;
- // unsigned long long unevictable_pgs_scanned = 0ULL;
- // unsigned long long unevictable_pgs_stranded = 0ULL;
+ // unsigned long long allocstall = 0ULL;
+ // unsigned long long compact_blocks_moved = 0ULL;
+ // unsigned long long compact_fail = 0ULL;
+ // unsigned long long compact_pagemigrate_failed = 0ULL;
+ // unsigned long long compact_pages_moved = 0ULL;
+ // unsigned long long compact_stall = 0ULL;
+ // unsigned long long compact_success = 0ULL;
+ // unsigned long long htlb_buddy_alloc_fail = 0ULL;
+ // unsigned long long htlb_buddy_alloc_success = 0ULL;
+ // unsigned long long kswapd_high_wmark_hit_quickly = 0ULL;
+ // unsigned long long kswapd_inodesteal = 0ULL;
+ // unsigned long long kswapd_low_wmark_hit_quickly = 0ULL;
+ // unsigned long long kswapd_skip_congestion_wait = 0ULL;
+ // unsigned long long nr_active_anon = 0ULL;
+ // unsigned long long nr_active_file = 0ULL;
+ // unsigned long long nr_anon_pages = 0ULL;
+ // unsigned long long nr_anon_transparent_hugepages = 0ULL;
+ // unsigned long long nr_bounce = 0ULL;
+ // unsigned long long nr_dirtied = 0ULL;
+ // unsigned long long nr_dirty = 0ULL;
+ // unsigned long long nr_dirty_background_threshold = 0ULL;
+ // unsigned long long nr_dirty_threshold = 0ULL;
+ // unsigned long long nr_file_pages = 0ULL;
+ // unsigned long long nr_free_pages = 0ULL;
+ // unsigned long long nr_inactive_anon = 0ULL;
+ // unsigned long long nr_inactive_file = 0ULL;
+ // unsigned long long nr_isolated_anon = 0ULL;
+ // unsigned long long nr_isolated_file = 0ULL;
+ // unsigned long long nr_kernel_stack = 0ULL;
+ // unsigned long long nr_mapped = 0ULL;
+ // unsigned long long nr_mlock = 0ULL;
+ // unsigned long long nr_page_table_pages = 0ULL;
+ // unsigned long long nr_shmem = 0ULL;
+ // unsigned long long nr_slab_reclaimable = 0ULL;
+ // unsigned long long nr_slab_unreclaimable = 0ULL;
+ // unsigned long long nr_unevictable = 0ULL;
+ // unsigned long long nr_unstable = 0ULL;
+ // unsigned long long nr_vmscan_immediate_reclaim = 0ULL;
+ // unsigned long long nr_vmscan_write = 0ULL;
+ // unsigned long long nr_writeback = 0ULL;
+ // unsigned long long nr_writeback_temp = 0ULL;
+ // unsigned long long nr_written = 0ULL;
+ // unsigned long long pageoutrun = 0ULL;
+ // unsigned long long pgactivate = 0ULL;
+ // unsigned long long pgalloc_dma = 0ULL;
+ // unsigned long long pgalloc_dma32 = 0ULL;
+ // unsigned long long pgalloc_movable = 0ULL;
+ // unsigned long long pgalloc_normal = 0ULL;
+ // unsigned long long pgdeactivate = 0ULL;
+ unsigned long long pgfault = 0ULL;
+ // unsigned long long pgfree = 0ULL;
+ // unsigned long long pginodesteal = 0ULL;
+ unsigned long long pgmajfault = 0ULL;
+ unsigned long long pgpgin = 0ULL;
+ unsigned long long pgpgout = 0ULL;
+ // unsigned long long pgrefill_dma = 0ULL;
+ // unsigned long long pgrefill_dma32 = 0ULL;
+ // unsigned long long pgrefill_movable = 0ULL;
+ // unsigned long long pgrefill_normal = 0ULL;
+ // unsigned long long pgrotated = 0ULL;
+ // unsigned long long pgscan_direct_dma = 0ULL;
+ // unsigned long long pgscan_direct_dma32 = 0ULL;
+ // unsigned long long pgscan_direct_movable = 0ULL;
+ // unsigned long long pgscan_direct_normal = 0ULL;
+ // unsigned long long pgscan_kswapd_dma = 0ULL;
+ // unsigned long long pgscan_kswapd_dma32 = 0ULL;
+ // unsigned long long pgscan_kswapd_movable = 0ULL;
+ // unsigned long long pgscan_kswapd_normal = 0ULL;
+ // unsigned long long pgsteal_direct_dma = 0ULL;
+ // unsigned long long pgsteal_direct_dma32 = 0ULL;
+ // unsigned long long pgsteal_direct_movable = 0ULL;
+ // unsigned long long pgsteal_direct_normal = 0ULL;
+ // unsigned long long pgsteal_kswapd_dma = 0ULL;
+ // unsigned long long pgsteal_kswapd_dma32 = 0ULL;
+ // unsigned long long pgsteal_kswapd_movable = 0ULL;
+ // unsigned long long pgsteal_kswapd_normal = 0ULL;
+ unsigned long long pswpin = 0ULL;
+ unsigned long long pswpout = 0ULL;
+ // unsigned long long slabs_scanned = 0ULL;
+ // unsigned long long thp_collapse_alloc = 0ULL;
+ // unsigned long long thp_collapse_alloc_failed = 0ULL;
+ // unsigned long long thp_fault_alloc = 0ULL;
+ // unsigned long long thp_fault_fallback = 0ULL;
+ // unsigned long long thp_split = 0ULL;
+ // unsigned long long unevictable_pgs_cleared = 0ULL;
+ // unsigned long long unevictable_pgs_culled = 0ULL;
+ // unsigned long long unevictable_pgs_mlocked = 0ULL;
+ // unsigned long long unevictable_pgs_mlockfreed = 0ULL;
+ // unsigned long long unevictable_pgs_munlocked = 0ULL;
+ // unsigned long long unevictable_pgs_rescued = 0ULL;
+ // unsigned long long unevictable_pgs_scanned = 0ULL;
+ // unsigned long long unevictable_pgs_stranded = 0ULL;
- for(l = 0; l < lines ;l++) {
- words = procfile_linewords(ff, l);
- if(words < 2) {
- if(words) error("Cannot read /proc/vmstat line %d. Expected 2 params, read %d.", l, words);
- continue;
- }
+ for(l = 0; l < lines ;l++) {
+ words = procfile_linewords(ff, l);
+ if(words < 2) {
+ if(words) error("Cannot read /proc/vmstat line %u. Expected 2 params, read %u.", l, words);
+ continue;
+ }
- char *name = procfile_lineword(ff, l, 0);
- char * value = procfile_lineword(ff, l, 1);
- if(!name || !*name || !value || !*value) continue;
+ char *name = procfile_lineword(ff, l, 0);
+ char * value = procfile_lineword(ff, l, 1);
+ if(!name || !*name || !value || !*value) continue;
- uint32_t hash = simple_hash(name);
+ uint32_t hash = simple_hash(name);
- if(0) ;
- // else if(hash == hash_allocstall && strcmp(name, "allocstall") == 0) allocstall = strtoull(value, NULL, 10);
- // else if(hash == hash_compact_blocks_moved && strcmp(name, "compact_blocks_moved") == 0) compact_blocks_moved = strtoull(value, NULL, 10);
- // else if(hash == hash_compact_fail && strcmp(name, "compact_fail") == 0) compact_fail = strtoull(value, NULL, 10);
- // else if(hash == hash_compact_pagemigrate_failed && strcmp(name, "compact_pagemigrate_failed") == 0) compact_pagemigrate_failed = strtoull(value, NULL, 10);
- // else if(hash == hash_compact_pages_moved && strcmp(name, "compact_pages_moved") == 0) compact_pages_moved = strtoull(value, NULL, 10);
- // else if(hash == hash_compact_stall && strcmp(name, "compact_stall") == 0) compact_stall = strtoull(value, NULL, 10);
- // else if(hash == hash_compact_success && strcmp(name, "compact_success") == 0) compact_success = strtoull(value, NULL, 10);
- // else if(hash == hash_htlb_buddy_alloc_fail && strcmp(name, "htlb_buddy_alloc_fail") == 0) htlb_buddy_alloc_fail = strtoull(value, NULL, 10);
- // else if(hash == hash_htlb_buddy_alloc_success && strcmp(name, "htlb_buddy_alloc_success") == 0) htlb_buddy_alloc_success = strtoull(value, NULL, 10);
- // else if(hash == hash_kswapd_high_wmark_hit_quickly && strcmp(name, "kswapd_high_wmark_hit_quickly") == 0) kswapd_high_wmark_hit_quickly = strtoull(value, NULL, 10);
- // else if(hash == hash_kswapd_inodesteal && strcmp(name, "kswapd_inodesteal") == 0) kswapd_inodesteal = strtoull(value, NULL, 10);
- // else if(hash == hash_kswapd_low_wmark_hit_quickly && strcmp(name, "kswapd_low_wmark_hit_quickly") == 0) kswapd_low_wmark_hit_quickly = strtoull(value, NULL, 10);
- // else if(hash == hash_kswapd_skip_congestion_wait && strcmp(name, "kswapd_skip_congestion_wait") == 0) kswapd_skip_congestion_wait = strtoull(value, NULL, 10);
- // else if(hash == hash_nr_active_anon && strcmp(name, "nr_active_anon") == 0) nr_active_anon = strtoull(value, NULL, 10);
- // else if(hash == hash_nr_active_file && strcmp(name, "nr_active_file") == 0) nr_active_file = strtoull(value, NULL, 10);
- // else if(hash == hash_nr_anon_pages && strcmp(name, "nr_anon_pages") == 0) nr_anon_pages = strtoull(value, NULL, 10);
- // else if(hash == hash_nr_anon_transparent_hugepages && strcmp(name, "nr_anon_transparent_hugepages") == 0) nr_anon_transparent_hugepages = strtoull(value, NULL, 10);
- // else if(hash == hash_nr_bounce && strcmp(name, "nr_bounce") == 0) nr_bounce = strtoull(value, NULL, 10);
- // else if(hash == hash_nr_dirtied && strcmp(name, "nr_dirtied") == 0) nr_dirtied = strtoull(value, NULL, 10);
- // else if(hash == hash_nr_dirty && strcmp(name, "nr_dirty") == 0) nr_dirty = strtoull(value, NULL, 10);
- // else if(hash == hash_nr_dirty_background_threshold && strcmp(name, "nr_dirty_background_threshold") == 0) nr_dirty_background_threshold = strtoull(value, NULL, 10);
- // else if(hash == hash_nr_dirty_threshold && strcmp(name, "nr_dirty_threshold") == 0) nr_dirty_threshold = strtoull(value, NULL, 10);
- // else if(hash == hash_nr_file_pages && strcmp(name, "nr_file_pages") == 0) nr_file_pages = strtoull(value, NULL, 10);
- // else if(hash == hash_nr_free_pages && strcmp(name, "nr_free_pages") == 0) nr_free_pages = strtoull(value, NULL, 10);
- // else if(hash == hash_nr_inactive_anon && strcmp(name, "nr_inactive_anon") == 0) nr_inactive_anon = strtoull(value, NULL, 10);
- // else if(hash == hash_nr_inactive_file && strcmp(name, "nr_inactive_file") == 0) nr_inactive_file = strtoull(value, NULL, 10);
- // else if(hash == hash_nr_isolated_anon && strcmp(name, "nr_isolated_anon") == 0) nr_isolated_anon = strtoull(value, NULL, 10);
- // else if(hash == hash_nr_isolated_file && strcmp(name, "nr_isolated_file") == 0) nr_isolated_file = strtoull(value, NULL, 10);
- // else if(hash == hash_nr_kernel_stack && strcmp(name, "nr_kernel_stack") == 0) nr_kernel_stack = strtoull(value, NULL, 10);
- // else if(hash == hash_nr_mapped && strcmp(name, "nr_mapped") == 0) nr_mapped = strtoull(value, NULL, 10);
- // else if(hash == hash_nr_mlock && strcmp(name, "nr_mlock") == 0) nr_mlock = strtoull(value, NULL, 10);
- // else if(hash == hash_nr_page_table_pages && strcmp(name, "nr_page_table_pages") == 0) nr_page_table_pages = strtoull(value, NULL, 10);
- // else if(hash == hash_nr_shmem && strcmp(name, "nr_shmem") == 0) nr_shmem = strtoull(value, NULL, 10);
- // else if(hash == hash_nr_slab_reclaimable && strcmp(name, "nr_slab_reclaimable") == 0) nr_slab_reclaimable = strtoull(value, NULL, 10);
- // else if(hash == hash_nr_slab_unreclaimable && strcmp(name, "nr_slab_unreclaimable") == 0) nr_slab_unreclaimable = strtoull(value, NULL, 10);
- // else if(hash == hash_nr_unevictable && strcmp(name, "nr_unevictable") == 0) nr_unevictable = strtoull(value, NULL, 10);
- // else if(hash == hash_nr_unstable && strcmp(name, "nr_unstable") == 0) nr_unstable = strtoull(value, NULL, 10);
- // else if(hash == hash_nr_vmscan_immediate_reclaim && strcmp(name, "nr_vmscan_immediate_reclaim") == 0) nr_vmscan_immediate_reclaim = strtoull(value, NULL, 10);
- // else if(hash == hash_nr_vmscan_write && strcmp(name, "nr_vmscan_write") == 0) nr_vmscan_write = strtoull(value, NULL, 10);
- // else if(hash == hash_nr_writeback && strcmp(name, "nr_writeback") == 0) nr_writeback = strtoull(value, NULL, 10);
- // else if(hash == hash_nr_writeback_temp && strcmp(name, "nr_writeback_temp") == 0) nr_writeback_temp = strtoull(value, NULL, 10);
- // else if(hash == hash_nr_written && strcmp(name, "nr_written") == 0) nr_written = strtoull(value, NULL, 10);
- // else if(hash == hash_pageoutrun && strcmp(name, "pageoutrun") == 0) pageoutrun = strtoull(value, NULL, 10);
- // else if(hash == hash_pgactivate && strcmp(name, "pgactivate") == 0) pgactivate = strtoull(value, NULL, 10);
- // else if(hash == hash_pgalloc_dma && strcmp(name, "pgalloc_dma") == 0) pgalloc_dma = strtoull(value, NULL, 10);
- // else if(hash == hash_pgalloc_dma32 && strcmp(name, "pgalloc_dma32") == 0) pgalloc_dma32 = strtoull(value, NULL, 10);
- // else if(hash == hash_pgalloc_movable && strcmp(name, "pgalloc_movable") == 0) pgalloc_movable = strtoull(value, NULL, 10);
- // else if(hash == hash_pgalloc_normal && strcmp(name, "pgalloc_normal") == 0) pgalloc_normal = strtoull(value, NULL, 10);
- // else if(hash == hash_pgdeactivate && strcmp(name, "pgdeactivate") == 0) pgdeactivate = strtoull(value, NULL, 10);
- else if(hash == hash_pgfault && strcmp(name, "pgfault") == 0) pgfault = strtoull(value, NULL, 10);
- // else if(hash == hash_pgfree && strcmp(name, "pgfree") == 0) pgfree = strtoull(value, NULL, 10);
- // else if(hash == hash_pginodesteal && strcmp(name, "pginodesteal") == 0) pginodesteal = strtoull(value, NULL, 10);
- else if(hash == hash_pgmajfault && strcmp(name, "pgmajfault") == 0) pgmajfault = strtoull(value, NULL, 10);
- else if(hash == hash_pgpgin && strcmp(name, "pgpgin") == 0) pgpgin = strtoull(value, NULL, 10);
- else if(hash == hash_pgpgout && strcmp(name, "pgpgout") == 0) pgpgout = strtoull(value, NULL, 10);
- // else if(hash == hash_pgrefill_dma && strcmp(name, "pgrefill_dma") == 0) pgrefill_dma = strtoull(value, NULL, 10);
- // else if(hash == hash_pgrefill_dma32 && strcmp(name, "pgrefill_dma32") == 0) pgrefill_dma32 = strtoull(value, NULL, 10);
- // else if(hash == hash_pgrefill_movable && strcmp(name, "pgrefill_movable") == 0) pgrefill_movable = strtoull(value, NULL, 10);
- // else if(hash == hash_pgrefill_normal && strcmp(name, "pgrefill_normal") == 0) pgrefill_normal = strtoull(value, NULL, 10);
- // else if(hash == hash_pgrotated && strcmp(name, "pgrotated") == 0) pgrotated = strtoull(value, NULL, 10);
- // else if(hash == hash_pgscan_direct_dma && strcmp(name, "pgscan_direct_dma") == 0) pgscan_direct_dma = strtoull(value, NULL, 10);
- // else if(hash == hash_pgscan_direct_dma32 && strcmp(name, "pgscan_direct_dma32") == 0) pgscan_direct_dma32 = strtoull(value, NULL, 10);
- // else if(hash == hash_pgscan_direct_movable && strcmp(name, "pgscan_direct_movable") == 0) pgscan_direct_movable = strtoull(value, NULL, 10);
- // else if(hash == hash_pgscan_direct_normal && strcmp(name, "pgscan_direct_normal") == 0) pgscan_direct_normal = strtoull(value, NULL, 10);
- // else if(hash == hash_pgscan_kswapd_dma && strcmp(name, "pgscan_kswapd_dma") == 0) pgscan_kswapd_dma = strtoull(value, NULL, 10);
- // else if(hash == hash_pgscan_kswapd_dma32 && strcmp(name, "pgscan_kswapd_dma32") == 0) pgscan_kswapd_dma32 = strtoull(value, NULL, 10);
- // else if(hash == hash_pgscan_kswapd_movable && strcmp(name, "pgscan_kswapd_movable") == 0) pgscan_kswapd_movable = strtoull(value, NULL, 10);
- // else if(hash == hash_pgscan_kswapd_normal && strcmp(name, "pgscan_kswapd_normal") == 0) pgscan_kswapd_normal = strtoull(value, NULL, 10);
- // else if(hash == hash_pgsteal_direct_dma && strcmp(name, "pgsteal_direct_dma") == 0) pgsteal_direct_dma = strtoull(value, NULL, 10);
- // else if(hash == hash_pgsteal_direct_dma32 && strcmp(name, "pgsteal_direct_dma32") == 0) pgsteal_direct_dma32 = strtoull(value, NULL, 10);
- // else if(hash == hash_pgsteal_direct_movable && strcmp(name, "pgsteal_direct_movable") == 0) pgsteal_direct_movable = strtoull(value, NULL, 10);
- // else if(hash == hash_pgsteal_direct_normal && strcmp(name, "pgsteal_direct_normal") == 0) pgsteal_direct_normal = strtoull(value, NULL, 10);
- // else if(hash == hash_pgsteal_kswapd_dma && strcmp(name, "pgsteal_kswapd_dma") == 0) pgsteal_kswapd_dma = strtoull(value, NULL, 10);
- // else if(hash == hash_pgsteal_kswapd_dma32 && strcmp(name, "pgsteal_kswapd_dma32") == 0) pgsteal_kswapd_dma32 = strtoull(value, NULL, 10);
- // else if(hash == hash_pgsteal_kswapd_movable && strcmp(name, "pgsteal_kswapd_movable") == 0) pgsteal_kswapd_movable = strtoull(value, NULL, 10);
- // else if(hash == hash_pgsteal_kswapd_normal && strcmp(name, "pgsteal_kswapd_normal") == 0) pgsteal_kswapd_normal = strtoull(value, NULL, 10);
- else if(hash == hash_pswpin && strcmp(name, "pswpin") == 0) pswpin = strtoull(value, NULL, 10);
- else if(hash == hash_pswpout && strcmp(name, "pswpout") == 0) pswpout = strtoull(value, NULL, 10);
- // else if(hash == hash_slabs_scanned && strcmp(name, "slabs_scanned") == 0) slabs_scanned = strtoull(value, NULL, 10);
- // else if(hash == hash_thp_collapse_alloc && strcmp(name, "thp_collapse_alloc") == 0) thp_collapse_alloc = strtoull(value, NULL, 10);
- // else if(hash == hash_thp_collapse_alloc_failed && strcmp(name, "thp_collapse_alloc_failed") == 0) thp_collapse_alloc_failed = strtoull(value, NULL, 10);
- // else if(hash == hash_thp_fault_alloc && strcmp(name, "thp_fault_alloc") == 0) thp_fault_alloc = strtoull(value, NULL, 10);
- // else if(hash == hash_thp_fault_fallback && strcmp(name, "thp_fault_fallback") == 0) thp_fault_fallback = strtoull(value, NULL, 10);
- // else if(hash == hash_thp_split && strcmp(name, "thp_split") == 0) thp_split = strtoull(value, NULL, 10);
- // else if(hash == hash_unevictable_pgs_cleared && strcmp(name, "unevictable_pgs_cleared") == 0) unevictable_pgs_cleared = strtoull(value, NULL, 10);
- // else if(hash == hash_unevictable_pgs_culled && strcmp(name, "unevictable_pgs_culled") == 0) unevictable_pgs_culled = strtoull(value, NULL, 10);
- // else if(hash == hash_unevictable_pgs_mlocked && strcmp(name, "unevictable_pgs_mlocked") == 0) unevictable_pgs_mlocked = strtoull(value, NULL, 10);
- // else if(hash == hash_unevictable_pgs_mlockfreed && strcmp(name, "unevictable_pgs_mlockfreed") == 0) unevictable_pgs_mlockfreed = strtoull(value, NULL, 10);
- // else if(hash == hash_unevictable_pgs_munlocked && strcmp(name, "unevictable_pgs_munlocked") == 0) unevictable_pgs_munlocked = strtoull(value, NULL, 10);
- // else if(hash == hash_unevictable_pgs_rescued && strcmp(name, "unevictable_pgs_rescued") == 0) unevictable_pgs_rescued = strtoull(value, NULL, 10);
- // else if(hash == hash_unevictable_pgs_scanned && strcmp(name, "unevictable_pgs_scanned") == 0) unevictable_pgs_scanned = strtoull(value, NULL, 10);
- // else if(hash == hash_unevictable_pgs_stranded && strcmp(name, "unevictable_pgs_stranded") == 0) unevictable_pgs_stranded = strtoull(value, NULL, 10);
- }
+ if(0) ;
+ // else if(hash == hash_allocstall && strcmp(name, "allocstall") == 0) allocstall = strtoull(value, NULL, 10);
+ // else if(hash == hash_compact_blocks_moved && strcmp(name, "compact_blocks_moved") == 0) compact_blocks_moved = strtoull(value, NULL, 10);
+ // else if(hash == hash_compact_fail && strcmp(name, "compact_fail") == 0) compact_fail = strtoull(value, NULL, 10);
+ // else if(hash == hash_compact_pagemigrate_failed && strcmp(name, "compact_pagemigrate_failed") == 0) compact_pagemigrate_failed = strtoull(value, NULL, 10);
+ // else if(hash == hash_compact_pages_moved && strcmp(name, "compact_pages_moved") == 0) compact_pages_moved = strtoull(value, NULL, 10);
+ // else if(hash == hash_compact_stall && strcmp(name, "compact_stall") == 0) compact_stall = strtoull(value, NULL, 10);
+ // else if(hash == hash_compact_success && strcmp(name, "compact_success") == 0) compact_success = strtoull(value, NULL, 10);
+ // else if(hash == hash_htlb_buddy_alloc_fail && strcmp(name, "htlb_buddy_alloc_fail") == 0) htlb_buddy_alloc_fail = strtoull(value, NULL, 10);
+ // else if(hash == hash_htlb_buddy_alloc_success && strcmp(name, "htlb_buddy_alloc_success") == 0) htlb_buddy_alloc_success = strtoull(value, NULL, 10);
+ // else if(hash == hash_kswapd_high_wmark_hit_quickly && strcmp(name, "kswapd_high_wmark_hit_quickly") == 0) kswapd_high_wmark_hit_quickly = strtoull(value, NULL, 10);
+ // else if(hash == hash_kswapd_inodesteal && strcmp(name, "kswapd_inodesteal") == 0) kswapd_inodesteal = strtoull(value, NULL, 10);
+ // else if(hash == hash_kswapd_low_wmark_hit_quickly && strcmp(name, "kswapd_low_wmark_hit_quickly") == 0) kswapd_low_wmark_hit_quickly = strtoull(value, NULL, 10);
+ // else if(hash == hash_kswapd_skip_congestion_wait && strcmp(name, "kswapd_skip_congestion_wait") == 0) kswapd_skip_congestion_wait = strtoull(value, NULL, 10);
+ // else if(hash == hash_nr_active_anon && strcmp(name, "nr_active_anon") == 0) nr_active_anon = strtoull(value, NULL, 10);
+ // else if(hash == hash_nr_active_file && strcmp(name, "nr_active_file") == 0) nr_active_file = strtoull(value, NULL, 10);
+ // else if(hash == hash_nr_anon_pages && strcmp(name, "nr_anon_pages") == 0) nr_anon_pages = strtoull(value, NULL, 10);
+ // else if(hash == hash_nr_anon_transparent_hugepages && strcmp(name, "nr_anon_transparent_hugepages") == 0) nr_anon_transparent_hugepages = strtoull(value, NULL, 10);
+ // else if(hash == hash_nr_bounce && strcmp(name, "nr_bounce") == 0) nr_bounce = strtoull(value, NULL, 10);
+ // else if(hash == hash_nr_dirtied && strcmp(name, "nr_dirtied") == 0) nr_dirtied = strtoull(value, NULL, 10);
+ // else if(hash == hash_nr_dirty && strcmp(name, "nr_dirty") == 0) nr_dirty = strtoull(value, NULL, 10);
+ // else if(hash == hash_nr_dirty_background_threshold && strcmp(name, "nr_dirty_background_threshold") == 0) nr_dirty_background_threshold = strtoull(value, NULL, 10);
+ // else if(hash == hash_nr_dirty_threshold && strcmp(name, "nr_dirty_threshold") == 0) nr_dirty_threshold = strtoull(value, NULL, 10);
+ // else if(hash == hash_nr_file_pages && strcmp(name, "nr_file_pages") == 0) nr_file_pages = strtoull(value, NULL, 10);
+ // else if(hash == hash_nr_free_pages && strcmp(name, "nr_free_pages") == 0) nr_free_pages = strtoull(value, NULL, 10);
+ // else if(hash == hash_nr_inactive_anon && strcmp(name, "nr_inactive_anon") == 0) nr_inactive_anon = strtoull(value, NULL, 10);
+ // else if(hash == hash_nr_inactive_file && strcmp(name, "nr_inactive_file") == 0) nr_inactive_file = strtoull(value, NULL, 10);
+ // else if(hash == hash_nr_isolated_anon && strcmp(name, "nr_isolated_anon") == 0) nr_isolated_anon = strtoull(value, NULL, 10);
+ // else if(hash == hash_nr_isolated_file && strcmp(name, "nr_isolated_file") == 0) nr_isolated_file = strtoull(value, NULL, 10);
+ // else if(hash == hash_nr_kernel_stack && strcmp(name, "nr_kernel_stack") == 0) nr_kernel_stack = strtoull(value, NULL, 10);
+ // else if(hash == hash_nr_mapped && strcmp(name, "nr_mapped") == 0) nr_mapped = strtoull(value, NULL, 10);
+ // else if(hash == hash_nr_mlock && strcmp(name, "nr_mlock") == 0) nr_mlock = strtoull(value, NULL, 10);
+ // else if(hash == hash_nr_page_table_pages && strcmp(name, "nr_page_table_pages") == 0) nr_page_table_pages = strtoull(value, NULL, 10);
+ // else if(hash == hash_nr_shmem && strcmp(name, "nr_shmem") == 0) nr_shmem = strtoull(value, NULL, 10);
+ // else if(hash == hash_nr_slab_reclaimable && strcmp(name, "nr_slab_reclaimable") == 0) nr_slab_reclaimable = strtoull(value, NULL, 10);
+ // else if(hash == hash_nr_slab_unreclaimable && strcmp(name, "nr_slab_unreclaimable") == 0) nr_slab_unreclaimable = strtoull(value, NULL, 10);
+ // else if(hash == hash_nr_unevictable && strcmp(name, "nr_unevictable") == 0) nr_unevictable = strtoull(value, NULL, 10);
+ // else if(hash == hash_nr_unstable && strcmp(name, "nr_unstable") == 0) nr_unstable = strtoull(value, NULL, 10);
+ // else if(hash == hash_nr_vmscan_immediate_reclaim && strcmp(name, "nr_vmscan_immediate_reclaim") == 0) nr_vmscan_immediate_reclaim = strtoull(value, NULL, 10);
+ // else if(hash == hash_nr_vmscan_write && strcmp(name, "nr_vmscan_write") == 0) nr_vmscan_write = strtoull(value, NULL, 10);
+ // else if(hash == hash_nr_writeback && strcmp(name, "nr_writeback") == 0) nr_writeback = strtoull(value, NULL, 10);
+ // else if(hash == hash_nr_writeback_temp && strcmp(name, "nr_writeback_temp") == 0) nr_writeback_temp = strtoull(value, NULL, 10);
+ // else if(hash == hash_nr_written && strcmp(name, "nr_written") == 0) nr_written = strtoull(value, NULL, 10);
+ // else if(hash == hash_pageoutrun && strcmp(name, "pageoutrun") == 0) pageoutrun = strtoull(value, NULL, 10);
+ // else if(hash == hash_pgactivate && strcmp(name, "pgactivate") == 0) pgactivate = strtoull(value, NULL, 10);
+ // else if(hash == hash_pgalloc_dma && strcmp(name, "pgalloc_dma") == 0) pgalloc_dma = strtoull(value, NULL, 10);
+ // else if(hash == hash_pgalloc_dma32 && strcmp(name, "pgalloc_dma32") == 0) pgalloc_dma32 = strtoull(value, NULL, 10);
+ // else if(hash == hash_pgalloc_movable && strcmp(name, "pgalloc_movable") == 0) pgalloc_movable = strtoull(value, NULL, 10);
+ // else if(hash == hash_pgalloc_normal && strcmp(name, "pgalloc_normal") == 0) pgalloc_normal = strtoull(value, NULL, 10);
+ // else if(hash == hash_pgdeactivate && strcmp(name, "pgdeactivate") == 0) pgdeactivate = strtoull(value, NULL, 10);
+ else if(hash == hash_pgfault && strcmp(name, "pgfault") == 0) pgfault = strtoull(value, NULL, 10);
+ // else if(hash == hash_pgfree && strcmp(name, "pgfree") == 0) pgfree = strtoull(value, NULL, 10);
+ // else if(hash == hash_pginodesteal && strcmp(name, "pginodesteal") == 0) pginodesteal = strtoull(value, NULL, 10);
+ else if(hash == hash_pgmajfault && strcmp(name, "pgmajfault") == 0) pgmajfault = strtoull(value, NULL, 10);
+ else if(hash == hash_pgpgin && strcmp(name, "pgpgin") == 0) pgpgin = strtoull(value, NULL, 10);
+ else if(hash == hash_pgpgout && strcmp(name, "pgpgout") == 0) pgpgout = strtoull(value, NULL, 10);
+ // else if(hash == hash_pgrefill_dma && strcmp(name, "pgrefill_dma") == 0) pgrefill_dma = strtoull(value, NULL, 10);
+ // else if(hash == hash_pgrefill_dma32 && strcmp(name, "pgrefill_dma32") == 0) pgrefill_dma32 = strtoull(value, NULL, 10);
+ // else if(hash == hash_pgrefill_movable && strcmp(name, "pgrefill_movable") == 0) pgrefill_movable = strtoull(value, NULL, 10);
+ // else if(hash == hash_pgrefill_normal && strcmp(name, "pgrefill_normal") == 0) pgrefill_normal = strtoull(value, NULL, 10);
+ // else if(hash == hash_pgrotated && strcmp(name, "pgrotated") == 0) pgrotated = strtoull(value, NULL, 10);
+ // else if(hash == hash_pgscan_direct_dma && strcmp(name, "pgscan_direct_dma") == 0) pgscan_direct_dma = strtoull(value, NULL, 10);
+ // else if(hash == hash_pgscan_direct_dma32 && strcmp(name, "pgscan_direct_dma32") == 0) pgscan_direct_dma32 = strtoull(value, NULL, 10);
+ // else if(hash == hash_pgscan_direct_movable && strcmp(name, "pgscan_direct_movable") == 0) pgscan_direct_movable = strtoull(value, NULL, 10);
+ // else if(hash == hash_pgscan_direct_normal && strcmp(name, "pgscan_direct_normal") == 0) pgscan_direct_normal = strtoull(value, NULL, 10);
+ // else if(hash == hash_pgscan_kswapd_dma && strcmp(name, "pgscan_kswapd_dma") == 0) pgscan_kswapd_dma = strtoull(value, NULL, 10);
+ // else if(hash == hash_pgscan_kswapd_dma32 && strcmp(name, "pgscan_kswapd_dma32") == 0) pgscan_kswapd_dma32 = strtoull(value, NULL, 10);
+ // else if(hash == hash_pgscan_kswapd_movable && strcmp(name, "pgscan_kswapd_movable") == 0) pgscan_kswapd_movable = strtoull(value, NULL, 10);
+ // else if(hash == hash_pgscan_kswapd_normal && strcmp(name, "pgscan_kswapd_normal") == 0) pgscan_kswapd_normal = strtoull(value, NULL, 10);
+ // else if(hash == hash_pgsteal_direct_dma && strcmp(name, "pgsteal_direct_dma") == 0) pgsteal_direct_dma = strtoull(value, NULL, 10);
+ // else if(hash == hash_pgsteal_direct_dma32 && strcmp(name, "pgsteal_direct_dma32") == 0) pgsteal_direct_dma32 = strtoull(value, NULL, 10);
+ // else if(hash == hash_pgsteal_direct_movable && strcmp(name, "pgsteal_direct_movable") == 0) pgsteal_direct_movable = strtoull(value, NULL, 10);
+ // else if(hash == hash_pgsteal_direct_normal && strcmp(name, "pgsteal_direct_normal") == 0) pgsteal_direct_normal = strtoull(value, NULL, 10);
+ // else if(hash == hash_pgsteal_kswapd_dma && strcmp(name, "pgsteal_kswapd_dma") == 0) pgsteal_kswapd_dma = strtoull(value, NULL, 10);
+ // else if(hash == hash_pgsteal_kswapd_dma32 && strcmp(name, "pgsteal_kswapd_dma32") == 0) pgsteal_kswapd_dma32 = strtoull(value, NULL, 10);
+ // else if(hash == hash_pgsteal_kswapd_movable && strcmp(name, "pgsteal_kswapd_movable") == 0) pgsteal_kswapd_movable = strtoull(value, NULL, 10);
+ // else if(hash == hash_pgsteal_kswapd_normal && strcmp(name, "pgsteal_kswapd_normal") == 0) pgsteal_kswapd_normal = strtoull(value, NULL, 10);
+ else if(hash == hash_pswpin && strcmp(name, "pswpin") == 0) pswpin = strtoull(value, NULL, 10);
+ else if(hash == hash_pswpout && strcmp(name, "pswpout") == 0) pswpout = strtoull(value, NULL, 10);
+ // else if(hash == hash_slabs_scanned && strcmp(name, "slabs_scanned") == 0) slabs_scanned = strtoull(value, NULL, 10);
+ // else if(hash == hash_thp_collapse_alloc && strcmp(name, "thp_collapse_alloc") == 0) thp_collapse_alloc = strtoull(value, NULL, 10);
+ // else if(hash == hash_thp_collapse_alloc_failed && strcmp(name, "thp_collapse_alloc_failed") == 0) thp_collapse_alloc_failed = strtoull(value, NULL, 10);
+ // else if(hash == hash_thp_fault_alloc && strcmp(name, "thp_fault_alloc") == 0) thp_fault_alloc = strtoull(value, NULL, 10);
+ // else if(hash == hash_thp_fault_fallback && strcmp(name, "thp_fault_fallback") == 0) thp_fault_fallback = strtoull(value, NULL, 10);
+ // else if(hash == hash_thp_split && strcmp(name, "thp_split") == 0) thp_split = strtoull(value, NULL, 10);
+ // else if(hash == hash_unevictable_pgs_cleared && strcmp(name, "unevictable_pgs_cleared") == 0) unevictable_pgs_cleared = strtoull(value, NULL, 10);
+ // else if(hash == hash_unevictable_pgs_culled && strcmp(name, "unevictable_pgs_culled") == 0) unevictable_pgs_culled = strtoull(value, NULL, 10);
+ // else if(hash == hash_unevictable_pgs_mlocked && strcmp(name, "unevictable_pgs_mlocked") == 0) unevictable_pgs_mlocked = strtoull(value, NULL, 10);
+ // else if(hash == hash_unevictable_pgs_mlockfreed && strcmp(name, "unevictable_pgs_mlockfreed") == 0) unevictable_pgs_mlockfreed = strtoull(value, NULL, 10);
+ // else if(hash == hash_unevictable_pgs_munlocked && strcmp(name, "unevictable_pgs_munlocked") == 0) unevictable_pgs_munlocked = strtoull(value, NULL, 10);
+ // else if(hash == hash_unevictable_pgs_rescued && strcmp(name, "unevictable_pgs_rescued") == 0) unevictable_pgs_rescued = strtoull(value, NULL, 10);
+ // else if(hash == hash_unevictable_pgs_scanned && strcmp(name, "unevictable_pgs_scanned") == 0) unevictable_pgs_scanned = strtoull(value, NULL, 10);
+ // else if(hash == hash_unevictable_pgs_stranded && strcmp(name, "unevictable_pgs_stranded") == 0) unevictable_pgs_stranded = strtoull(value, NULL, 10);
+ }
- // --------------------------------------------------------------------
+ // --------------------------------------------------------------------
- if(do_swapio) {
- static RRDSET *st_swapio = NULL;
- if(!st_swapio) {
- st_swapio = rrdset_create("system", "swapio", NULL, "swap", NULL, "Swap I/O", "kilobytes/s", 250, update_every, RRDSET_TYPE_AREA);
+ if(do_swapio) {
+ static RRDSET *st_swapio = NULL;
+ if(!st_swapio) {
+ st_swapio = rrdset_create("system", "swapio", NULL, "swap", NULL, "Swap I/O", "kilobytes/s", 250, update_every, RRDSET_TYPE_AREA);
- rrddim_add(st_swapio, "in", NULL, sysconf(_SC_PAGESIZE), 1024, RRDDIM_INCREMENTAL);
- rrddim_add(st_swapio, "out", NULL, -sysconf(_SC_PAGESIZE), 1024, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st_swapio);
+ rrddim_add(st_swapio, "in", NULL, sysconf(_SC_PAGESIZE), 1024, RRDDIM_INCREMENTAL);
+ rrddim_add(st_swapio, "out", NULL, -sysconf(_SC_PAGESIZE), 1024, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st_swapio);
- rrddim_set(st_swapio, "in", pswpin);
- rrddim_set(st_swapio, "out", pswpout);
- rrdset_done(st_swapio);
- }
+ rrddim_set(st_swapio, "in", pswpin);
+ rrddim_set(st_swapio, "out", pswpout);
+ rrdset_done(st_swapio);
+ }
- // --------------------------------------------------------------------
+ // --------------------------------------------------------------------
- if(do_io) {
- static RRDSET *st_io = NULL;
- if(!st_io) {
- st_io = rrdset_create("system", "io", NULL, "disk", NULL, "Disk I/O", "kilobytes/s", 150, update_every, RRDSET_TYPE_AREA);
+ if(do_io) {
+ static RRDSET *st_io = NULL;
+ if(!st_io) {
+ st_io = rrdset_create("system", "io", NULL, "disk", NULL, "Disk I/O", "kilobytes/s", 150, update_every, RRDSET_TYPE_AREA);
- rrddim_add(st_io, "in", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st_io, "out", NULL, -1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st_io);
+ rrddim_add(st_io, "in", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st_io, "out", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st_io);
- rrddim_set(st_io, "in", pgpgin);
- rrddim_set(st_io, "out", pgpgout);
- rrdset_done(st_io);
- }
+ rrddim_set(st_io, "in", pgpgin);
+ rrddim_set(st_io, "out", pgpgout);
+ rrdset_done(st_io);
+ }
- // --------------------------------------------------------------------
+ // --------------------------------------------------------------------
- if(do_pgfaults) {
- static RRDSET *st_pgfaults = NULL;
- if(!st_pgfaults) {
- st_pgfaults = rrdset_create("mem", "pgfaults", NULL, "system", NULL, "Memory Page Faults", "page faults/s", 500, update_every, RRDSET_TYPE_LINE);
- st_pgfaults->isdetail = 1;
+ if(do_pgfaults) {
+ static RRDSET *st_pgfaults = NULL;
+ if(!st_pgfaults) {
+ st_pgfaults = rrdset_create("mem", "pgfaults", NULL, "system", NULL, "Memory Page Faults", "page faults/s", 500, update_every, RRDSET_TYPE_LINE);
+ st_pgfaults->isdetail = 1;
- rrddim_add(st_pgfaults, "minor", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st_pgfaults, "major", NULL, -1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st_pgfaults);
+ rrddim_add(st_pgfaults, "minor", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st_pgfaults, "major", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st_pgfaults);
- rrddim_set(st_pgfaults, "minor", pgfault);
- rrddim_set(st_pgfaults, "major", pgmajfault);
- rrdset_done(st_pgfaults);
- }
+ rrddim_set(st_pgfaults, "minor", pgfault);
+ rrddim_set(st_pgfaults, "major", pgmajfault);
+ rrdset_done(st_pgfaults);
+ }
- return 0;
+ return 0;
}
diff --git a/src/procfile.c b/src/procfile.c
index 291f14519..e2aa60582 100644
--- a/src/procfile.c
+++ b/src/procfile.c
@@ -1,26 +1,4 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <string.h>
-#include <malloc.h>
-#include <ctype.h>
-#include <time.h>
-#include <sys/time.h>
-#include <sys/wait.h>
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-
#include "common.h"
-#include "log.h"
-#include "procfile.h"
-#include "../config.h"
#define PF_PREFIX "PROCFILE"
@@ -41,48 +19,40 @@ size_t procfile_max_allocation = PROCFILE_INCREMENT_BUFFER;
pfwords *pfwords_add(pfwords *fw, char *str) {
- // debug(D_PROCFILE, PF_PREFIX ": adding word No %d: '%s'", fw->len, str);
+ // debug(D_PROCFILE, PF_PREFIX ": adding word No %d: '%s'", fw->len, str);
- if(unlikely(fw->len == fw->size)) {
- // debug(D_PROCFILE, PF_PREFIX ": expanding words");
+ if(unlikely(fw->len == fw->size)) {
+ // debug(D_PROCFILE, PF_PREFIX ": expanding words");
- pfwords *new = realloc(fw, sizeof(pfwords) + (fw->size + PFWORDS_INCREASE_STEP) * sizeof(char *));
- if(unlikely(!new)) {
- error(PF_PREFIX ": failed to expand words");
- free(fw);
- return NULL;
- }
- fw = new;
- fw->size += PFWORDS_INCREASE_STEP;
- }
+ fw = reallocz(fw, sizeof(pfwords) + (fw->size + PFWORDS_INCREASE_STEP) * sizeof(char *));
+ fw->size += PFWORDS_INCREASE_STEP;
+ }
- fw->words[fw->len++] = str;
+ fw->words[fw->len++] = str;
- return fw;
+ return fw;
}
pfwords *pfwords_new(void) {
- // debug(D_PROCFILE, PF_PREFIX ": initializing words");
-
- uint32_t size = (procfile_adaptive_initial_allocation) ? procfile_max_words : PFWORDS_INCREASE_STEP;
+ // debug(D_PROCFILE, PF_PREFIX ": initializing words");
- pfwords *new = malloc(sizeof(pfwords) + size * sizeof(char *));
- if(unlikely(!new)) return NULL;
+ uint32_t size = (procfile_adaptive_initial_allocation) ? procfile_max_words : PFWORDS_INCREASE_STEP;
- new->len = 0;
- new->size = size;
- return new;
+ pfwords *new = mallocz(sizeof(pfwords) + size * sizeof(char *));
+ new->len = 0;
+ new->size = size;
+ return new;
}
void pfwords_reset(pfwords *fw) {
- // debug(D_PROCFILE, PF_PREFIX ": reseting words");
- fw->len = 0;
+ // debug(D_PROCFILE, PF_PREFIX ": reseting words");
+ fw->len = 0;
}
void pfwords_free(pfwords *fw) {
- // debug(D_PROCFILE, PF_PREFIX ": freeing words");
+ // debug(D_PROCFILE, PF_PREFIX ": freeing words");
- free(fw);
+ freez(fw);
}
@@ -90,422 +60,402 @@ void pfwords_free(pfwords *fw) {
// An array of lines
pflines *pflines_add(pflines *fl, uint32_t first_word) {
- // debug(D_PROCFILE, PF_PREFIX ": adding line %d at word %d", fl->len, first_word);
+ // debug(D_PROCFILE, PF_PREFIX ": adding line %d at word %d", fl->len, first_word);
- if(unlikely(fl->len == fl->size)) {
- // debug(D_PROCFILE, PF_PREFIX ": expanding lines");
+ if(unlikely(fl->len == fl->size)) {
+ // debug(D_PROCFILE, PF_PREFIX ": expanding lines");
- pflines *new = realloc(fl, sizeof(pflines) + (fl->size + PFLINES_INCREASE_STEP) * sizeof(ffline));
- if(unlikely(!new)) {
- error(PF_PREFIX ": failed to expand lines");
- free(fl);
- return NULL;
- }
- fl = new;
- fl->size += PFLINES_INCREASE_STEP;
- }
+ fl = reallocz(fl, sizeof(pflines) + (fl->size + PFLINES_INCREASE_STEP) * sizeof(ffline));
+ fl->size += PFLINES_INCREASE_STEP;
+ }
- fl->lines[fl->len].words = 0;
- fl->lines[fl->len++].first = first_word;
+ fl->lines[fl->len].words = 0;
+ fl->lines[fl->len++].first = first_word;
- return fl;
+ return fl;
}
pflines *pflines_new(void) {
- // debug(D_PROCFILE, PF_PREFIX ": initializing lines");
-
- uint32_t size = (unlikely(procfile_adaptive_initial_allocation)) ? procfile_max_words : PFLINES_INCREASE_STEP;
+ // debug(D_PROCFILE, PF_PREFIX ": initializing lines");
- pflines *new = malloc(sizeof(pflines) + size * sizeof(ffline));
- if(unlikely(!new)) return NULL;
+ uint32_t size = (unlikely(procfile_adaptive_initial_allocation)) ? procfile_max_words : PFLINES_INCREASE_STEP;
- new->len = 0;
- new->size = size;
- return new;
+ pflines *new = mallocz(sizeof(pflines) + size * sizeof(ffline));
+ new->len = 0;
+ new->size = size;
+ return new;
}
void pflines_reset(pflines *fl) {
- // debug(D_PROCFILE, PF_PREFIX ": reseting lines");
+ // debug(D_PROCFILE, PF_PREFIX ": reseting lines");
- fl->len = 0;
+ fl->len = 0;
}
void pflines_free(pflines *fl) {
- // debug(D_PROCFILE, PF_PREFIX ": freeing lines");
+ // debug(D_PROCFILE, PF_PREFIX ": freeing lines");
- free(fl);
+ freez(fl);
}
// ----------------------------------------------------------------------------
// The procfile
-#define PF_CHAR_IS_SEPARATOR ' '
-#define PF_CHAR_IS_NEWLINE 'N'
-#define PF_CHAR_IS_WORD 'W'
+#define PF_CHAR_IS_SEPARATOR ' '
+#define PF_CHAR_IS_NEWLINE 'N'
+#define PF_CHAR_IS_WORD 'W'
#define PF_CHAR_IS_QUOTE 'Q'
#define PF_CHAR_IS_OPEN 'O'
#define PF_CHAR_IS_CLOSE 'C'
void procfile_close(procfile *ff) {
- debug(D_PROCFILE, PF_PREFIX ": Closing file '%s'", ff->filename);
+ debug(D_PROCFILE, PF_PREFIX ": Closing file '%s'", ff->filename);
- if(likely(ff->lines)) pflines_free(ff->lines);
- if(likely(ff->words)) pfwords_free(ff->words);
+ if(likely(ff->lines)) pflines_free(ff->lines);
+ if(likely(ff->words)) pfwords_free(ff->words);
- if(likely(ff->fd != -1)) close(ff->fd);
- free(ff);
+ if(likely(ff->fd != -1)) close(ff->fd);
+ freez(ff);
}
procfile *procfile_parser(procfile *ff) {
- debug(D_PROCFILE, PF_PREFIX ": Parsing file '%s'", ff->filename);
-
- char *s = ff->data, *e = &ff->data[ff->len], *t = ff->data, quote = 0;
- uint32_t l = 0, w = 0;
- int opened = 0;
-
- ff->lines = pflines_add(ff->lines, w);
- if(unlikely(!ff->lines)) goto cleanup;
-
- while(likely(s < e)) {
- // we are not at the end
-
- switch(ff->separators[(int)(*s)]) {
- case PF_CHAR_IS_OPEN:
- if(s == t) {
- opened++;
- t = ++s;
- }
- else if(opened) {
- opened++;
- s++;
- }
- else
- s++;
- continue;
-
- case PF_CHAR_IS_CLOSE:
- if(opened) {
- opened--;
-
- if(!opened) {
- *s = '\0';
- ff->words = pfwords_add(ff->words, t);
- if(unlikely(!ff->words)) goto cleanup;
-
- ff->lines->lines[l].words++;
- w++;
-
- t = ++s;
- }
- else
- s++;
- }
- else
- s++;
- continue;
-
- case PF_CHAR_IS_QUOTE:
- if(unlikely(!quote && s == t)) {
- // quote opened at the beginning
- quote = *s;
- t = ++s;
- }
- else if(unlikely(quote && quote == *s)) {
- // quote closed
- quote = 0;
-
- *s = '\0';
- ff->words = pfwords_add(ff->words, t);
- if(unlikely(!ff->words)) goto cleanup;
-
- ff->lines->lines[l].words++;
- w++;
-
- t = ++s;
- }
- else
- s++;
- continue;
-
- case PF_CHAR_IS_SEPARATOR:
- if(unlikely(quote || opened)) {
- // we are inside a quote
- s++;
- continue;
- }
-
- if(unlikely(s == t)) {
- // skip all leading white spaces
- t = ++s;
- continue;
- }
-
- // end of word
- *s = '\0';
-
- ff->words = pfwords_add(ff->words, t);
- if(unlikely(!ff->words)) goto cleanup;
-
- ff->lines->lines[l].words++;
- w++;
-
- t = ++s;
- continue;
-
- case PF_CHAR_IS_NEWLINE:
- // end of line
- *s = '\0';
-
- ff->words = pfwords_add(ff->words, t);
- if(unlikely(!ff->words)) goto cleanup;
-
- ff->lines->lines[l].words++;
- w++;
-
- // debug(D_PROCFILE, PF_PREFIX ": ended line %d with %d words", l, ff->lines->lines[l].words);
-
- ff->lines = pflines_add(ff->lines, w);
- if(unlikely(!ff->lines)) goto cleanup;
- l++;
-
- t = ++s;
- continue;
-
- default:
- s++;
- continue;
- }
- }
-
- if(likely(s > t && t < e)) {
- // the last word
- if(likely(ff->len < ff->size))
- *s = '\0';
- else {
- // we are going to loose the last byte
- ff->data[ff->size - 1] = '\0';
- }
-
- ff->words = pfwords_add(ff->words, t);
- if(unlikely(!ff->words)) goto cleanup;
-
- ff->lines->lines[l].words++;
- w++;
- }
-
- return ff;
+ debug(D_PROCFILE, PF_PREFIX ": Parsing file '%s'", ff->filename);
+
+ char *s = ff->data, *e = &ff->data[ff->len], *t = ff->data, quote = 0;
+ uint32_t l = 0, w = 0;
+ int opened = 0;
+
+ ff->lines = pflines_add(ff->lines, w);
+ if(unlikely(!ff->lines)) goto cleanup;
+
+ while(likely(s < e)) {
+ // we are not at the end
+
+ switch(ff->separators[(uint8_t)(*s)]) {
+ case PF_CHAR_IS_OPEN:
+ if(s == t) {
+ opened++;
+ t = ++s;
+ }
+ else if(opened) {
+ opened++;
+ s++;
+ }
+ else
+ s++;
+ continue;
+
+ case PF_CHAR_IS_CLOSE:
+ if(opened) {
+ opened--;
+
+ if(!opened) {
+ *s = '\0';
+ ff->words = pfwords_add(ff->words, t);
+ if(unlikely(!ff->words)) goto cleanup;
+
+ ff->lines->lines[l].words++;
+ w++;
+
+ t = ++s;
+ }
+ else
+ s++;
+ }
+ else
+ s++;
+ continue;
+
+ case PF_CHAR_IS_QUOTE:
+ if(unlikely(!quote && s == t)) {
+ // quote opened at the beginning
+ quote = *s;
+ t = ++s;
+ }
+ else if(unlikely(quote && quote == *s)) {
+ // quote closed
+ quote = 0;
+
+ *s = '\0';
+ ff->words = pfwords_add(ff->words, t);
+ if(unlikely(!ff->words)) goto cleanup;
+
+ ff->lines->lines[l].words++;
+ w++;
+
+ t = ++s;
+ }
+ else
+ s++;
+ continue;
+
+ case PF_CHAR_IS_SEPARATOR:
+ if(unlikely(quote || opened)) {
+ // we are inside a quote
+ s++;
+ continue;
+ }
+
+ if(unlikely(s == t)) {
+ // skip all leading white spaces
+ t = ++s;
+ continue;
+ }
+
+ // end of word
+ *s = '\0';
+
+ ff->words = pfwords_add(ff->words, t);
+ if(unlikely(!ff->words)) goto cleanup;
+
+ ff->lines->lines[l].words++;
+ w++;
+
+ t = ++s;
+ continue;
+
+ case PF_CHAR_IS_NEWLINE:
+ // end of line
+ *s = '\0';
+
+ ff->words = pfwords_add(ff->words, t);
+ if(unlikely(!ff->words)) goto cleanup;
+
+ ff->lines->lines[l].words++;
+ w++;
+
+ // debug(D_PROCFILE, PF_PREFIX ": ended line %d with %d words", l, ff->lines->lines[l].words);
+
+ ff->lines = pflines_add(ff->lines, w);
+ if(unlikely(!ff->lines)) goto cleanup;
+ l++;
+
+ t = ++s;
+ continue;
+
+ default:
+ s++;
+ continue;
+ }
+ }
+
+ if(likely(s > t && t < e)) {
+ // the last word
+ if(likely(ff->len < ff->size))
+ *s = '\0';
+ else {
+ // we are going to loose the last byte
+ ff->data[ff->size - 1] = '\0';
+ }
+
+ ff->words = pfwords_add(ff->words, t);
+ if(unlikely(!ff->words)) goto cleanup;
+
+ ff->lines->lines[l].words++;
+ w++;
+ }
+
+ return ff;
cleanup:
- error(PF_PREFIX ": Failed to parse file '%s'", ff->filename);
- procfile_close(ff);
- return NULL;
+ error(PF_PREFIX ": Failed to parse file '%s'", ff->filename);
+ procfile_close(ff);
+ return NULL;
}
procfile *procfile_readall(procfile *ff) {
- debug(D_PROCFILE, PF_PREFIX ": Reading file '%s'.", ff->filename);
-
- ssize_t s, r = 1, x;
- ff->len = 0;
-
- while(likely(r > 0)) {
- s = ff->len;
- x = ff->size - s;
-
- if(!x) {
- debug(D_PROCFILE, PF_PREFIX ": Expanding data buffer for file '%s'.", ff->filename);
-
- procfile *new = realloc(ff, sizeof(procfile) + ff->size + PROCFILE_INCREMENT_BUFFER);
- if(unlikely(!new)) {
- error(PF_PREFIX ": Cannot allocate memory for file '%s'", ff->filename);
- procfile_close(ff);
- return NULL;
- }
- ff = new;
- ff->size += PROCFILE_INCREMENT_BUFFER;
- }
-
- debug(D_PROCFILE, "Reading file '%s', from position %ld with length %ld", ff->filename, s, ff->size - s);
- r = read(ff->fd, &ff->data[s], ff->size - s);
- if(unlikely(r == -1)) {
- if(unlikely(!(ff->flags & PROCFILE_FLAG_NO_ERROR_ON_FILE_IO))) error(PF_PREFIX ": Cannot read from file '%s'", ff->filename);
- procfile_close(ff);
- return NULL;
- }
-
- ff->len += r;
- }
-
- debug(D_PROCFILE, "Rewinding file '%s'", ff->filename);
- if(unlikely(lseek(ff->fd, 0, SEEK_SET) == -1)) {
- if(unlikely(!(ff->flags & PROCFILE_FLAG_NO_ERROR_ON_FILE_IO))) error(PF_PREFIX ": Cannot rewind on file '%s'.", ff->filename);
- procfile_close(ff);
- return NULL;
- }
-
- pflines_reset(ff->lines);
- pfwords_reset(ff->words);
-
- ff = procfile_parser(ff);
-
- if(unlikely(procfile_adaptive_initial_allocation)) {
- if(unlikely(ff->len > procfile_max_allocation)) procfile_max_allocation = ff->len;
- if(unlikely(ff->lines->len > procfile_max_lines)) procfile_max_lines = ff->lines->len;
- if(unlikely(ff->words->len > procfile_max_words)) procfile_max_words = ff->words->len;
- }
-
- debug(D_PROCFILE, "File '%s' updated.", ff->filename);
- return ff;
+ debug(D_PROCFILE, PF_PREFIX ": Reading file '%s'.", ff->filename);
+
+ ssize_t s, r = 1, x;
+ ff->len = 0;
+
+ while(likely(r > 0)) {
+ s = ff->len;
+ x = ff->size - s;
+
+ if(!x) {
+ debug(D_PROCFILE, PF_PREFIX ": Expanding data buffer for file '%s'.", ff->filename);
+
+ ff = reallocz(ff, sizeof(procfile) + ff->size + PROCFILE_INCREMENT_BUFFER);
+ ff->size += PROCFILE_INCREMENT_BUFFER;
+ }
+
+ debug(D_PROCFILE, "Reading file '%s', from position %ld with length %lu", ff->filename, s, ff->size - s);
+ r = read(ff->fd, &ff->data[s], ff->size - s);
+ if(unlikely(r == -1)) {
+ if(unlikely(!(ff->flags & PROCFILE_FLAG_NO_ERROR_ON_FILE_IO))) error(PF_PREFIX ": Cannot read from file '%s'", ff->filename);
+ procfile_close(ff);
+ return NULL;
+ }
+
+ ff->len += r;
+ }
+
+ debug(D_PROCFILE, "Rewinding file '%s'", ff->filename);
+ if(unlikely(lseek(ff->fd, 0, SEEK_SET) == -1)) {
+ if(unlikely(!(ff->flags & PROCFILE_FLAG_NO_ERROR_ON_FILE_IO))) error(PF_PREFIX ": Cannot rewind on file '%s'.", ff->filename);
+ procfile_close(ff);
+ return NULL;
+ }
+
+ pflines_reset(ff->lines);
+ pfwords_reset(ff->words);
+
+ ff = procfile_parser(ff);
+
+ if(unlikely(procfile_adaptive_initial_allocation)) {
+ if(unlikely(ff->len > procfile_max_allocation)) procfile_max_allocation = ff->len;
+ if(unlikely(ff->lines->len > procfile_max_lines)) procfile_max_lines = ff->lines->len;
+ if(unlikely(ff->words->len > procfile_max_words)) procfile_max_words = ff->words->len;
+ }
+
+ debug(D_PROCFILE, "File '%s' updated.", ff->filename);
+ return ff;
}
static void procfile_set_separators(procfile *ff, const char *separators) {
- static char def[256] = { [0 ... 255] = 0 };
- int i;
-
- if(unlikely(!def[255])) {
- // this is thread safe
- // we check that the last byte is non-zero
- // if it is zero, multiple threads may be executing this at the same time
- // setting in def[] the exact same values
- for(i = 0; likely(i < 256) ;i++) {
- if(unlikely(i == '\n' || i == '\r')) def[i] = PF_CHAR_IS_NEWLINE;
- else if(unlikely(isspace(i) || !isprint(i))) def[i] = PF_CHAR_IS_SEPARATOR;
- else def[i] = PF_CHAR_IS_WORD;
- }
- }
-
- // copy the default
- char *ffs = ff->separators, *ffd = def, *ffe = &def[256];
- while(likely(ffd != ffe)) *ffs++ = *ffd++;
-
- // set the separators
- if(unlikely(!separators))
- separators = " \t=|";
-
- ffs = ff->separators;
- const char *s = separators;
- while(likely(*s))
- ffs[(int)*s++] = PF_CHAR_IS_SEPARATOR;
+ static char def[256] = { [0 ... 255] = 0 };
+ int i;
+
+ if(unlikely(!def[255])) {
+ // this is thread safe
+ // we check that the last byte is non-zero
+ // if it is zero, multiple threads may be executing this at the same time
+ // setting in def[] the exact same values
+ for(i = 0; likely(i < 256) ;i++) {
+ if(unlikely(i == '\n' || i == '\r')) def[i] = PF_CHAR_IS_NEWLINE;
+ else if(unlikely(isspace(i) || !isprint(i))) def[i] = PF_CHAR_IS_SEPARATOR;
+ else def[i] = PF_CHAR_IS_WORD;
+ }
+ }
+
+ // copy the default
+ char *ffs = ff->separators, *ffd = def, *ffe = &def[256];
+ while(likely(ffd != ffe)) *ffs++ = *ffd++;
+
+ // set the separators
+ if(unlikely(!separators))
+ separators = " \t=|";
+
+ ffs = ff->separators;
+ const char *s = separators;
+ while(likely(*s))
+ ffs[(int)*s++] = PF_CHAR_IS_SEPARATOR;
}
void procfile_set_quotes(procfile *ff, const char *quotes) {
- // remove all quotes
- int i;
- for(i = 0; i < 256 ; i++)
- if(ff->separators[i] == PF_CHAR_IS_QUOTE)
- ff->separators[i] = PF_CHAR_IS_WORD;
-
- // if nothing given, return
- if(unlikely(!quotes || !*quotes))
- return;
-
- // set the quotes
- char *ffs = ff->separators;
- const char *s = quotes;
- while(likely(*s))
- ffs[(int)*s++] = PF_CHAR_IS_QUOTE;
+ // remove all quotes
+ int i;
+ for(i = 0; i < 256 ; i++)
+ if(ff->separators[i] == PF_CHAR_IS_QUOTE)
+ ff->separators[i] = PF_CHAR_IS_WORD;
+
+ // if nothing given, return
+ if(unlikely(!quotes || !*quotes))
+ return;
+
+ // set the quotes
+ char *ffs = ff->separators;
+ const char *s = quotes;
+ while(likely(*s))
+ ffs[(int)*s++] = PF_CHAR_IS_QUOTE;
}
void procfile_set_open_close(procfile *ff, const char *open, const char *close) {
- // remove all open/close
- int i;
- for(i = 0; i < 256 ; i++)
- if(ff->separators[i] == PF_CHAR_IS_OPEN || ff->separators[i] == PF_CHAR_IS_CLOSE)
- ff->separators[i] = PF_CHAR_IS_WORD;
-
- // if nothing given, return
- if(unlikely(!open || !*open || !close || !*close))
- return;
-
- // set the openings
- char *ffs = ff->separators;
- const char *s = open;
- while(likely(*s))
- ffs[(int)*s++] = PF_CHAR_IS_OPEN;
-
- s = close;
- while(likely(*s))
- ffs[(int)*s++] = PF_CHAR_IS_CLOSE;
+ // remove all open/close
+ int i;
+ for(i = 0; i < 256 ; i++)
+ if(ff->separators[i] == PF_CHAR_IS_OPEN || ff->separators[i] == PF_CHAR_IS_CLOSE)
+ ff->separators[i] = PF_CHAR_IS_WORD;
+
+ // if nothing given, return
+ if(unlikely(!open || !*open || !close || !*close))
+ return;
+
+ // set the openings
+ char *ffs = ff->separators;
+ const char *s = open;
+ while(likely(*s))
+ ffs[(int)*s++] = PF_CHAR_IS_OPEN;
+
+ s = close;
+ while(likely(*s))
+ ffs[(int)*s++] = PF_CHAR_IS_CLOSE;
}
procfile *procfile_open(const char *filename, const char *separators, uint32_t flags) {
- debug(D_PROCFILE, PF_PREFIX ": Opening file '%s'", filename);
-
- int fd = open(filename, O_RDONLY, 0666);
- if(unlikely(fd == -1)) {
- if(unlikely(!(flags & PROCFILE_FLAG_NO_ERROR_ON_FILE_IO))) error(PF_PREFIX ": Cannot open file '%s'", filename);
- return NULL;
- }
-
- size_t size = (unlikely(procfile_adaptive_initial_allocation)) ? procfile_max_allocation : PROCFILE_INCREMENT_BUFFER;
- procfile *ff = malloc(sizeof(procfile) + size);
- if(unlikely(!ff)) {
- error(PF_PREFIX ": Cannot allocate memory for file '%s'", filename);
- close(fd);
- return NULL;
- }
-
- strncpyz(ff->filename, filename, FILENAME_MAX);
-
- ff->fd = fd;
- ff->size = size;
- ff->len = 0;
- ff->flags = flags;
-
- ff->lines = pflines_new();
- ff->words = pfwords_new();
-
- if(unlikely(!ff->lines || !ff->words)) {
- error(PF_PREFIX ": Cannot initialize parser for file '%s'", filename);
- procfile_close(ff);
- return NULL;
- }
-
- procfile_set_separators(ff, separators);
-
- debug(D_PROCFILE, "File '%s' opened.", filename);
- return ff;
+ debug(D_PROCFILE, PF_PREFIX ": Opening file '%s'", filename);
+
+ int fd = open(filename, O_RDONLY, 0666);
+ if(unlikely(fd == -1)) {
+ if(unlikely(!(flags & PROCFILE_FLAG_NO_ERROR_ON_FILE_IO))) error(PF_PREFIX ": Cannot open file '%s'", filename);
+ return NULL;
+ }
+
+ size_t size = (unlikely(procfile_adaptive_initial_allocation)) ? procfile_max_allocation : PROCFILE_INCREMENT_BUFFER;
+ procfile *ff = mallocz(sizeof(procfile) + size);
+ strncpyz(ff->filename, filename, FILENAME_MAX);
+
+ ff->fd = fd;
+ ff->size = size;
+ ff->len = 0;
+ ff->flags = flags;
+
+ ff->lines = pflines_new();
+ ff->words = pfwords_new();
+
+ if(unlikely(!ff->lines || !ff->words)) {
+ error(PF_PREFIX ": Cannot initialize parser for file '%s'", filename);
+ procfile_close(ff);
+ return NULL;
+ }
+
+ procfile_set_separators(ff, separators);
+
+ debug(D_PROCFILE, "File '%s' opened.", filename);
+ return ff;
}
procfile *procfile_reopen(procfile *ff, const char *filename, const char *separators, uint32_t flags) {
- if(unlikely(!ff)) return procfile_open(filename, separators, flags);
+ if(unlikely(!ff)) return procfile_open(filename, separators, flags);
- if(likely(ff->fd != -1)) close(ff->fd);
+ if(likely(ff->fd != -1)) close(ff->fd);
- ff->fd = open(filename, O_RDONLY, 0666);
- if(unlikely(ff->fd == -1)) {
- procfile_close(ff);
- return NULL;
- }
+ ff->fd = open(filename, O_RDONLY, 0666);
+ if(unlikely(ff->fd == -1)) {
+ procfile_close(ff);
+ return NULL;
+ }
- strncpyz(ff->filename, filename, FILENAME_MAX);
+ strncpyz(ff->filename, filename, FILENAME_MAX);
- ff->flags = flags;
+ ff->flags = flags;
- // do not do the separators again if NULL is given
- if(likely(separators)) procfile_set_separators(ff, separators);
+ // do not do the separators again if NULL is given
+ if(likely(separators)) procfile_set_separators(ff, separators);
- return ff;
+ return ff;
}
// ----------------------------------------------------------------------------
// example parsing of procfile data
void procfile_print(procfile *ff) {
- uint32_t lines = procfile_lines(ff), l;
- uint32_t words, w;
- char *s;
+ uint32_t lines = procfile_lines(ff), l;
+ uint32_t words, w;
+ char *s;
- debug(D_PROCFILE, "File '%s' with %d lines and %d words", ff->filename, ff->lines->len, ff->words->len);
+ debug(D_PROCFILE, "File '%s' with %u lines and %u words", ff->filename, ff->lines->len, ff->words->len);
- for(l = 0; likely(l < lines) ;l++) {
- words = procfile_linewords(ff, l);
+ for(l = 0; likely(l < lines) ;l++) {
+ words = procfile_linewords(ff, l);
- debug(D_PROCFILE, " line %d starts at word %d and has %d words", l, ff->lines->lines[l].first, ff->lines->lines[l].words);
+ debug(D_PROCFILE, " line %u starts at word %u and has %u words", l, ff->lines->lines[l].first, ff->lines->lines[l].words);
- for(w = 0; likely(w < words) ;w++) {
- s = procfile_lineword(ff, l, w);
- debug(D_PROCFILE, " [%d.%d] '%s'", l, w, s);
- }
- }
+ for(w = 0; likely(w < words) ;w++) {
+ s = procfile_lineword(ff, l, w);
+ debug(D_PROCFILE, " [%u.%u] '%s'", l, w, s);
+ }
+ }
}
diff --git a/src/procfile.h b/src/procfile.h
index 122e153f1..5e00b2584 100644
--- a/src/procfile.h
+++ b/src/procfile.h
@@ -30,9 +30,9 @@
// An array of words
typedef struct {
- uint32_t len; // used entries
- uint32_t size; // capacity
- char *words[]; // array of pointers
+ uint32_t len; // used entries
+ uint32_t size; // capacity
+ char *words[]; // array of pointers
} pfwords;
@@ -40,15 +40,15 @@ typedef struct {
// An array of lines
typedef struct {
- uint32_t words; // how many words this line has
- uint32_t first; // the id of the first word of this line
- // in the words array
+ uint32_t words; // how many words this line has
+ uint32_t first; // the id of the first word of this line
+ // in the words array
} ffline;
typedef struct {
- uint32_t len; // used entries
- uint32_t size; // capacity
- ffline lines[]; // array of lines
+ uint32_t len; // used entries
+ uint32_t size; // capacity
+ ffline lines[]; // array of lines
} pflines;
@@ -59,15 +59,15 @@ typedef struct {
#define PROCFILE_FLAG_NO_ERROR_ON_FILE_IO 0x00000001
typedef struct {
- char filename[FILENAME_MAX + 1];
- uint32_t flags;
- int fd; // the file desriptor
- size_t len; // the bytes we have placed into data
- size_t size; // the bytes we have allocated for data
- pflines *lines;
- pfwords *words;
- char separators[256];
- char data[]; // allocated buffer to keep file contents
+ char filename[FILENAME_MAX + 1];
+ uint32_t flags;
+ int fd; // the file desriptor
+ size_t len; // the bytes we have placed into data
+ size_t size; // the bytes we have allocated for data
+ pflines *lines;
+ pfwords *words;
+ char separators[256];
+ char data[]; // allocated buffer to keep file contents
} procfile;
// close the proc file and free all related memory
diff --git a/src/registry.c b/src/registry.c
index f39ce3e2e..f2319c478 100644
--- a/src/registry.c
+++ b/src/registry.c
@@ -1,28 +1,4 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <uuid/uuid.h>
-#include <inttypes.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-#include <unistd.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <errno.h>
-#include <fcntl.h>
-
-#include "log.h"
#include "common.h"
-#include "dictionary.h"
-#include "appconfig.h"
-
-#include "web_client.h"
-#include "rrd.h"
-#include "rrd2json.h"
-#include "registry.h"
-
// ----------------------------------------------------------------------------
// TODO
@@ -63,59 +39,60 @@
// COMMON structures
struct registry {
- int enabled;
-
- char machine_guid[36 + 1];
-
- // entries counters / statistics
- unsigned long long persons_count;
- unsigned long long machines_count;
- unsigned long long usages_count;
- unsigned long long urls_count;
- unsigned long long persons_urls_count;
- unsigned long long machines_urls_count;
- unsigned long long log_count;
-
- // memory counters / statistics
- unsigned long long persons_memory;
- unsigned long long machines_memory;
- unsigned long long urls_memory;
- unsigned long long persons_urls_memory;
- unsigned long long machines_urls_memory;
-
- // configuration
- unsigned long long save_registry_every_entries;
- char *registry_domain;
- char *hostname;
- char *registry_to_announce;
- time_t persons_expiration; // seconds to expire idle persons
-
- size_t max_url_length;
- size_t max_name_length;
-
- // file/path names
- char *pathname;
- char *db_filename;
- char *log_filename;
- char *machine_guid_filename;
-
- // open files
- FILE *log_fp;
-
- // the database
- DICTIONARY *persons; // dictionary of PERSON *, with key the PERSON.guid
- DICTIONARY *machines; // dictionary of MACHINE *, with key the MACHINE.guid
- DICTIONARY *urls; // dictionary of URL *, with key the URL.url
-
- // concurrency locking
- // we keep different locks for different things
- // so that many tasks can be completed in parallel
- pthread_mutex_t persons_lock;
- pthread_mutex_t machines_lock;
- pthread_mutex_t urls_lock;
- pthread_mutex_t person_urls_lock;
- pthread_mutex_t machine_urls_lock;
- pthread_mutex_t log_lock;
+ int enabled;
+
+ char machine_guid[36 + 1];
+
+ // entries counters / statistics
+ unsigned long long persons_count;
+ unsigned long long machines_count;
+ unsigned long long usages_count;
+ unsigned long long urls_count;
+ unsigned long long persons_urls_count;
+ unsigned long long machines_urls_count;
+ unsigned long long log_count;
+
+ // memory counters / statistics
+ unsigned long long persons_memory;
+ unsigned long long machines_memory;
+ unsigned long long urls_memory;
+ unsigned long long persons_urls_memory;
+ unsigned long long machines_urls_memory;
+
+ // configuration
+ unsigned long long save_registry_every_entries;
+ char *registry_domain;
+ char *hostname;
+ char *registry_to_announce;
+ time_t persons_expiration; // seconds to expire idle persons
+ int verify_cookies_redirects;
+
+ size_t max_url_length;
+ size_t max_name_length;
+
+ // file/path names
+ char *pathname;
+ char *db_filename;
+ char *log_filename;
+ char *machine_guid_filename;
+
+ // open files
+ FILE *log_fp;
+
+ // the database
+ DICTIONARY *persons; // dictionary of PERSON *, with key the PERSON.guid
+ DICTIONARY *machines; // dictionary of MACHINE *, with key the MACHINE.guid
+ DICTIONARY *urls; // dictionary of URL *, with key the URL.url
+
+ // concurrency locking
+ // we keep different locks for different things
+ // so that many tasks can be completed in parallel
+ pthread_mutex_t persons_lock;
+ pthread_mutex_t machines_lock;
+ pthread_mutex_t urls_lock;
+ pthread_mutex_t person_urls_lock;
+ pthread_mutex_t machine_urls_lock;
+ pthread_mutex_t log_lock;
} registry;
@@ -126,9 +103,9 @@ struct registry {
// we store them here and we keep pointers elsewhere
struct url {
- uint32_t links; // the number of links to this URL - when none is left, we free it
- uint16_t len; // the length of the URL in bytes
- char url[1]; // the URL - dynamically allocated to more size
+ uint32_t links; // the number of links to this URL - when none is left, we free it
+ uint16_t len; // the length of the URL in bytes
+ char url[1]; // the URL - dynamically allocated to more size
};
typedef struct url URL;
@@ -138,27 +115,27 @@ typedef struct url URL;
// For each MACHINE-URL pair we keep this
struct machine_url {
- URL *url; // de-duplicated URL
-// DICTIONARY *persons; // dictionary of PERSON *
+ URL *url; // de-duplicated URL
+// DICTIONARY *persons; // dictionary of PERSON *
- uint8_t flags;
- uint32_t first_t; // the first time we saw this
- uint32_t last_t; // the last time we saw this
- uint32_t usages; // how many times this has been accessed
+ uint8_t flags;
+ uint32_t first_t; // the first time we saw this
+ uint32_t last_t; // the last time we saw this
+ uint32_t usages; // how many times this has been accessed
};
typedef struct machine_url MACHINE_URL;
// A machine
struct machine {
- char guid[36 + 1]; // the GUID
+ char guid[36 + 1]; // the GUID
- uint32_t links; // the number of PERSON_URLs linked to this machine
+ uint32_t links; // the number of PERSON_URLs linked to this machine
- DICTIONARY *urls; // MACHINE_URL *
+ DICTIONARY *urls; // MACHINE_URL *
- uint32_t first_t; // the first time we saw this
- uint32_t last_t; // the last time we saw this
- uint32_t usages; // how many times this has been accessed
+ uint32_t first_t; // the first time we saw this
+ uint32_t last_t; // the last time we saw this
+ uint32_t usages; // how many times this has been accessed
};
typedef struct machine MACHINE;
@@ -168,28 +145,28 @@ typedef struct machine MACHINE;
// for each PERSON-URL pair we keep this
struct person_url {
- URL *url; // de-duplicated URL
- MACHINE *machine; // link the MACHINE of this URL
+ URL *url; // de-duplicated URL
+ MACHINE *machine; // link the MACHINE of this URL
- uint8_t flags;
- uint32_t first_t; // the first time we saw this
- uint32_t last_t; // the last time we saw this
- uint32_t usages; // how many times this has been accessed
+ uint8_t flags;
+ uint32_t first_t; // the first time we saw this
+ uint32_t last_t; // the last time we saw this
+ uint32_t usages; // how many times this has been accessed
- char name[1]; // the name of the URL, as known by the user
- // dynamically allocated to fit properly
+ char name[1]; // the name of the URL, as known by the user
+ // dynamically allocated to fit properly
};
typedef struct person_url PERSON_URL;
// A person
struct person {
- char guid[36 + 1]; // the person GUID
+ char guid[36 + 1]; // the person GUID
- DICTIONARY *urls; // dictionary of PERSON_URL *
+ DICTIONARY *urls; // dictionary of PERSON_URL *
- uint32_t first_t; // the first time we saw this
- uint32_t last_t; // the last time we saw this
- uint32_t usages; // how many times this has been accessed
+ uint32_t first_t; // the first time we saw this
+ uint32_t last_t; // the last time we saw this
+ uint32_t usages; // how many times this has been accessed
};
typedef struct person PERSON;
@@ -198,27 +175,27 @@ typedef struct person PERSON;
// REGISTRY concurrency locking
static inline void registry_persons_lock(void) {
- pthread_mutex_lock(&registry.persons_lock);
+ pthread_mutex_lock(&registry.persons_lock);
}
static inline void registry_persons_unlock(void) {
- pthread_mutex_unlock(&registry.persons_lock);
+ pthread_mutex_unlock(&registry.persons_lock);
}
static inline void registry_machines_lock(void) {
- pthread_mutex_lock(&registry.machines_lock);
+ pthread_mutex_lock(&registry.machines_lock);
}
static inline void registry_machines_unlock(void) {
- pthread_mutex_unlock(&registry.machines_lock);
+ pthread_mutex_unlock(&registry.machines_lock);
}
static inline void registry_urls_lock(void) {
- pthread_mutex_lock(&registry.urls_lock);
+ pthread_mutex_lock(&registry.urls_lock);
}
static inline void registry_urls_unlock(void) {
- pthread_mutex_unlock(&registry.urls_lock);
+ pthread_mutex_unlock(&registry.urls_lock);
}
// ideally, we should not lock the whole registry for
@@ -226,13 +203,13 @@ static inline void registry_urls_unlock(void) {
// however, to save the memory required for keeping a
// mutex (40 bytes) per person, we do...
static inline void registry_person_urls_lock(PERSON *p) {
- (void)p;
- pthread_mutex_lock(&registry.person_urls_lock);
+ (void)p;
+ pthread_mutex_lock(&registry.person_urls_lock);
}
static inline void registry_person_urls_unlock(PERSON *p) {
- (void)p;
- pthread_mutex_unlock(&registry.person_urls_lock);
+ (void)p;
+ pthread_mutex_unlock(&registry.person_urls_lock);
}
// ideally, we should not lock the whole registry for
@@ -240,21 +217,21 @@ static inline void registry_person_urls_unlock(PERSON *p) {
// however, to save the memory required for keeping a
// mutex (40 bytes) per machine, we do...
static inline void registry_machine_urls_lock(MACHINE *m) {
- (void)m;
- pthread_mutex_lock(&registry.machine_urls_lock);
+ (void)m;
+ pthread_mutex_lock(&registry.machine_urls_lock);
}
static inline void registry_machine_urls_unlock(MACHINE *m) {
- (void)m;
- pthread_mutex_unlock(&registry.machine_urls_lock);
+ (void)m;
+ pthread_mutex_unlock(&registry.machine_urls_lock);
}
static inline void registry_log_lock(void) {
- pthread_mutex_lock(&registry.log_lock);
+ pthread_mutex_lock(&registry.log_lock);
}
static inline void registry_log_unlock(void) {
- pthread_mutex_unlock(&registry.log_lock);
+ pthread_mutex_unlock(&registry.log_lock);
}
@@ -264,58 +241,58 @@ static inline void registry_log_unlock(void) {
// parse a GUID and re-generated to be always lower case
// this is used as a protection against the variations of GUIDs
static inline int registry_regenerate_guid(const char *guid, char *result) {
- uuid_t uuid;
- if(unlikely(uuid_parse(guid, uuid) == -1)) {
- info("Registry: GUID '%s' is not a valid GUID.", guid);
- return -1;
- }
- else {
- uuid_unparse_lower(uuid, result);
+ uuid_t uuid;
+ if(unlikely(uuid_parse(guid, uuid) == -1)) {
+ info("Registry: GUID '%s' is not a valid GUID.", guid);
+ return -1;
+ }
+ else {
+ uuid_unparse_lower(uuid, result);
#ifdef NETDATA_INTERNAL_CHECKS
- if(strcmp(guid, result))
- info("Registry: source GUID '%s' and re-generated GUID '%s' differ!", guid, result);
+ if(strcmp(guid, result))
+ info("Registry: source GUID '%s' and re-generated GUID '%s' differ!", guid, result);
#endif /* NETDATA_INTERNAL_CHECKS */
- }
+ }
- return 0;
+ return 0;
}
// make sure the names of the machines / URLs do not contain any tabs
// (which are used as our separator in the database files)
// and are properly trimmed (before and after)
static inline char *registry_fix_machine_name(char *name, size_t *len) {
- char *s = name?name:"";
+ char *s = name?name:"";
- // skip leading spaces
- while(*s && isspace(*s)) s++;
+ // skip leading spaces
+ while(*s && isspace(*s)) s++;
- // make sure all spaces are a SPACE
- char *t = s;
- while(*t) {
- if(unlikely(isspace(*t)))
- *t = ' ';
+ // make sure all spaces are a SPACE
+ char *t = s;
+ while(*t) {
+ if(unlikely(isspace(*t)))
+ *t = ' ';
- t++;
- }
+ t++;
+ }
- // remove trailing spaces
- while(--t >= s) {
- if(*t == ' ')
- *t = '\0';
- else
- break;
- }
- t++;
+ // remove trailing spaces
+ while(--t >= s) {
+ if(*t == ' ')
+ *t = '\0';
+ else
+ break;
+ }
+ t++;
- if(likely(len))
- *len = (t - s);
+ if(likely(len))
+ *len = (t - s);
- return s;
+ return s;
}
static inline char *registry_fix_url(char *url, size_t *len) {
- return registry_fix_machine_name(url, len);
+ return registry_fix_machine_name(url, len);
}
@@ -330,59 +307,57 @@ extern PERSON *registry_request_delete(char *person_guid, char *machine_guid, ch
// URL
static inline URL *registry_url_allocate_nolock(const char *url, size_t urllen) {
- // protection from too big URLs
- if(urllen > registry.max_url_length)
- urllen = registry.max_url_length;
-
- debug(D_REGISTRY, "Registry: registry_url_allocate_nolock('%s'): allocating %zu bytes", url, sizeof(URL) + urllen);
- URL *u = malloc(sizeof(URL) + urllen);
- if(!u) fatal("Cannot allocate %zu bytes for URL '%s'", sizeof(URL) + urllen);
+ // protection from too big URLs
+ if(urllen > registry.max_url_length)
+ urllen = registry.max_url_length;
- // a simple strcpy() should do the job
- // but I prefer to be safe, since the caller specified urllen
- strncpyz(u->url, url, urllen);
+ debug(D_REGISTRY, "Registry: registry_url_allocate_nolock('%s'): allocating %zu bytes", url, sizeof(URL) + urllen);
+ URL *u = mallocz(sizeof(URL) + urllen);
- u->len = urllen;
- u->links = 0;
+ // a simple strcpy() should do the job
+ // but I prefer to be safe, since the caller specified urllen
+ u->len = (uint16_t)urllen;
+ strncpyz(u->url, url, u->len);
+ u->links = 0;
- registry.urls_memory += sizeof(URL) + urllen;
+ registry.urls_memory += sizeof(URL) + urllen;
- debug(D_REGISTRY, "Registry: registry_url_allocate_nolock('%s'): indexing it", url);
- dictionary_set(registry.urls, u->url, u, sizeof(URL));
+ debug(D_REGISTRY, "Registry: registry_url_allocate_nolock('%s'): indexing it", url);
+ dictionary_set(registry.urls, u->url, u, sizeof(URL));
- return u;
+ return u;
}
static inline URL *registry_url_get(const char *url, size_t urllen) {
- debug(D_REGISTRY, "Registry: registry_url_get('%s')", url);
+ debug(D_REGISTRY, "Registry: registry_url_get('%s')", url);
- registry_urls_lock();
+ registry_urls_lock();
- URL *u = dictionary_get(registry.urls, url);
- if(!u) {
- u = registry_url_allocate_nolock(url, urllen);
- registry.urls_count++;
- }
+ URL *u = dictionary_get(registry.urls, url);
+ if(!u) {
+ u = registry_url_allocate_nolock(url, urllen);
+ registry.urls_count++;
+ }
- registry_urls_unlock();
+ registry_urls_unlock();
- return u;
+ return u;
}
static inline void registry_url_link_nolock(URL *u) {
- u->links++;
- debug(D_REGISTRY, "Registry: registry_url_link_nolock('%s'): URL has now %u links", u->url, u->links);
+ u->links++;
+ debug(D_REGISTRY, "Registry: registry_url_link_nolock('%s'): URL has now %u links", u->url, u->links);
}
static inline void registry_url_unlink_nolock(URL *u) {
- u->links--;
- if(!u->links) {
- debug(D_REGISTRY, "Registry: registry_url_unlink_nolock('%s'): No more links for this URL", u->url);
- dictionary_del(registry.urls, u->url);
- free(u);
- }
- else
- debug(D_REGISTRY, "Registry: registry_url_unlink_nolock('%s'): URL has %u links left", u->url, u->links);
+ u->links--;
+ if(!u->links) {
+ debug(D_REGISTRY, "Registry: registry_url_unlink_nolock('%s'): No more links for this URL", u->url);
+ dictionary_del(registry.urls, u->url);
+ freez(u);
+ }
+ else
+ debug(D_REGISTRY, "Registry: registry_url_unlink_nolock('%s'): URL has %u links left", u->url, u->links);
}
@@ -390,78 +365,76 @@ static inline void registry_url_unlink_nolock(URL *u) {
// MACHINE
static inline MACHINE *registry_machine_find(const char *machine_guid) {
- debug(D_REGISTRY, "Registry: registry_machine_find('%s')", machine_guid);
- return dictionary_get(registry.machines, machine_guid);
+ debug(D_REGISTRY, "Registry: registry_machine_find('%s')", machine_guid);
+ return dictionary_get(registry.machines, machine_guid);
}
static inline MACHINE_URL *registry_machine_url_allocate(MACHINE *m, URL *u, time_t when) {
- debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s'): allocating %zu bytes", m->guid, u->url, sizeof(MACHINE_URL));
+ debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s'): allocating %zu bytes", m->guid, u->url, sizeof(MACHINE_URL));
- MACHINE_URL *mu = malloc(sizeof(MACHINE_URL));
- if(!mu) fatal("registry_machine_link_to_url('%s', '%s'): cannot allocate %zu bytes.", m->guid, u->url, sizeof(MACHINE_URL));
+ MACHINE_URL *mu = mallocz(sizeof(MACHINE_URL));
- // mu->persons = dictionary_create(DICTIONARY_FLAGS);
- // dictionary_set(mu->persons, p->guid, p, sizeof(PERSON));
+ // mu->persons = dictionary_create(DICTIONARY_FLAGS);
+ // dictionary_set(mu->persons, p->guid, p, sizeof(PERSON));
- mu->first_t = mu->last_t = when;
- mu->usages = 1;
- mu->url = u;
- mu->flags = REGISTRY_URL_FLAGS_DEFAULT;
+ mu->first_t = mu->last_t = (uint32_t)when;
+ mu->usages = 1;
+ mu->url = u;
+ mu->flags = REGISTRY_URL_FLAGS_DEFAULT;
- registry.machines_urls_memory += sizeof(MACHINE_URL);
+ registry.machines_urls_memory += sizeof(MACHINE_URL);
- debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s'): indexing URL in machine", m->guid, u->url);
- dictionary_set(m->urls, u->url, mu, sizeof(MACHINE_URL));
- registry_url_link_nolock(u);
+ debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s'): indexing URL in machine", m->guid, u->url);
+ dictionary_set(m->urls, u->url, mu, sizeof(MACHINE_URL));
+ registry_url_link_nolock(u);
- return mu;
+ return mu;
}
static inline MACHINE *registry_machine_allocate(const char *machine_guid, time_t when) {
- debug(D_REGISTRY, "Registry: registry_machine_allocate('%s'): creating new machine, sizeof(MACHINE)=%zu", machine_guid, sizeof(MACHINE));
+ debug(D_REGISTRY, "Registry: registry_machine_allocate('%s'): creating new machine, sizeof(MACHINE)=%zu", machine_guid, sizeof(MACHINE));
- MACHINE *m = malloc(sizeof(MACHINE));
- if(!m) fatal("Registry: cannot allocate memory for new machine '%s'", machine_guid);
+ MACHINE *m = mallocz(sizeof(MACHINE));
- strncpyz(m->guid, machine_guid, 36);
+ strncpyz(m->guid, machine_guid, 36);
- debug(D_REGISTRY, "Registry: registry_machine_allocate('%s'): creating dictionary of urls", machine_guid);
- m->urls = dictionary_create(DICTIONARY_FLAGS);
+ debug(D_REGISTRY, "Registry: registry_machine_allocate('%s'): creating dictionary of urls", machine_guid);
+ m->urls = dictionary_create(DICTIONARY_FLAGS);
- m->first_t = m->last_t = when;
- m->usages = 0;
+ m->first_t = m->last_t = (uint32_t)when;
+ m->usages = 0;
- registry.machines_memory += sizeof(MACHINE);
+ registry.machines_memory += sizeof(MACHINE);
- registry.machines_count++;
- dictionary_set(registry.machines, m->guid, m, sizeof(MACHINE));
+ registry.machines_count++;
+ dictionary_set(registry.machines, m->guid, m, sizeof(MACHINE));
- return m;
+ return m;
}
// 1. validate machine GUID
// 2. if it is valid, find it or create it and return it
// 3. if it is not valid, return NULL
static inline MACHINE *registry_machine_get(const char *machine_guid, time_t when) {
- MACHINE *m = NULL;
+ MACHINE *m = NULL;
- registry_machines_lock();
+ registry_machines_lock();
- if(likely(machine_guid && *machine_guid)) {
- // validate it is a GUID
- char buf[36 + 1];
- if(unlikely(registry_regenerate_guid(machine_guid, buf) == -1))
- info("Registry: machine guid '%s' is not a valid guid. Ignoring it.", machine_guid);
- else {
- machine_guid = buf;
- m = registry_machine_find(machine_guid);
- if(!m) m = registry_machine_allocate(machine_guid, when);
- }
- }
+ if(likely(machine_guid && *machine_guid)) {
+ // validate it is a GUID
+ char buf[36 + 1];
+ if(unlikely(registry_regenerate_guid(machine_guid, buf) == -1))
+ info("Registry: machine guid '%s' is not a valid guid. Ignoring it.", machine_guid);
+ else {
+ machine_guid = buf;
+ m = registry_machine_find(machine_guid);
+ if(!m) m = registry_machine_allocate(machine_guid, when);
+ }
+ }
- registry_machines_unlock();
+ registry_machines_unlock();
- return m;
+ return m;
}
@@ -469,101 +442,99 @@ static inline MACHINE *registry_machine_get(const char *machine_guid, time_t whe
// PERSON
static inline PERSON *registry_person_find(const char *person_guid) {
- debug(D_REGISTRY, "Registry: registry_person_find('%s')", person_guid);
- return dictionary_get(registry.persons, person_guid);
+ debug(D_REGISTRY, "Registry: registry_person_find('%s')", person_guid);
+ return dictionary_get(registry.persons, person_guid);
}
static inline PERSON_URL *registry_person_url_allocate(PERSON *p, MACHINE *m, URL *u, char *name, size_t namelen, time_t when) {
- // protection from too big names
- if(namelen > registry.max_name_length)
- namelen = registry.max_name_length;
+ // protection from too big names
+ if(namelen > registry.max_name_length)
+ namelen = registry.max_name_length;
- debug(D_REGISTRY, "registry_person_url_allocate('%s', '%s', '%s'): allocating %zu bytes", p->guid, m->guid, u->url,
- sizeof(PERSON_URL) + namelen);
+ debug(D_REGISTRY, "registry_person_url_allocate('%s', '%s', '%s'): allocating %zu bytes", p->guid, m->guid, u->url,
+ sizeof(PERSON_URL) + namelen);
- PERSON_URL *pu = malloc(sizeof(PERSON_URL) + namelen);
- if(!pu) fatal("registry_person_url_allocate('%s', '%s', '%s'): cannot allocate %zu bytes.", p->guid, m->guid, u->url, sizeof(PERSON_URL) + namelen);
+ PERSON_URL *pu = mallocz(sizeof(PERSON_URL) + namelen);
- // a simple strcpy() should do the job
- // but I prefer to be safe, since the caller specified urllen
- strncpyz(pu->name, name, namelen);
+ // a simple strcpy() should do the job
+ // but I prefer to be safe, since the caller specified urllen
+ strncpyz(pu->name, name, namelen);
- pu->machine = m;
- pu->first_t = pu->last_t = when;
- pu->usages = 1;
- pu->url = u;
- pu->flags = REGISTRY_URL_FLAGS_DEFAULT;
- m->links++;
+ pu->machine = m;
+ pu->first_t = pu->last_t = when;
+ pu->usages = 1;
+ pu->url = u;
+ pu->flags = REGISTRY_URL_FLAGS_DEFAULT;
+ m->links++;
- registry.persons_urls_memory += sizeof(PERSON_URL) + namelen;
+ registry.persons_urls_memory += sizeof(PERSON_URL) + namelen;
- debug(D_REGISTRY, "registry_person_url_allocate('%s', '%s', '%s'): indexing URL in person", p->guid, m->guid, u->url);
- dictionary_set(p->urls, u->url, pu, sizeof(PERSON_URL));
- registry_url_link_nolock(u);
+ debug(D_REGISTRY, "registry_person_url_allocate('%s', '%s', '%s'): indexing URL in person", p->guid, m->guid, u->url);
+ dictionary_set(p->urls, u->url, pu, sizeof(PERSON_URL));
+ registry_url_link_nolock(u);
- return pu;
+ return pu;
}
static inline PERSON_URL *registry_person_url_reallocate(PERSON *p, MACHINE *m, URL *u, char *name, size_t namelen, time_t when, PERSON_URL *pu) {
- // this function is needed to change the name of a PERSON_URL
+ // this function is needed to change the name of a PERSON_URL
- debug(D_REGISTRY, "registry_person_url_reallocate('%s', '%s', '%s'): allocating %zu bytes", p->guid, m->guid, u->url,
- sizeof(PERSON_URL) + namelen);
+ debug(D_REGISTRY, "registry_person_url_reallocate('%s', '%s', '%s'): allocating %zu bytes", p->guid, m->guid, u->url,
+ sizeof(PERSON_URL) + namelen);
- PERSON_URL *tpu = registry_person_url_allocate(p, m, u, name, namelen, when);
- tpu->first_t = pu->first_t;
- tpu->last_t = pu->last_t;
- tpu->usages = pu->usages;
+ PERSON_URL *tpu = registry_person_url_allocate(p, m, u, name, namelen, when);
+ tpu->first_t = pu->first_t;
+ tpu->last_t = pu->last_t;
+ tpu->usages = pu->usages;
- // ok, these are a hack - since the registry_person_url_allocate() is
- // adding these, we have to subtract them
- tpu->machine->links--;
- registry.persons_urls_memory -= sizeof(PERSON_URL) + strlen(pu->name);
- registry_url_unlink_nolock(u);
+ // ok, these are a hack - since the registry_person_url_allocate() is
+ // adding these, we have to subtract them
+ tpu->machine->links--;
+ registry.persons_urls_memory -= sizeof(PERSON_URL) + strlen(pu->name);
+ registry_url_unlink_nolock(u);
- free(pu);
+ freez(pu);
- return tpu;
+ return tpu;
}
static inline PERSON *registry_person_allocate(const char *person_guid, time_t when) {
- PERSON *p = NULL;
+ PERSON *p = NULL;
- debug(D_REGISTRY, "Registry: registry_person_allocate('%s'): allocating new person, sizeof(PERSON)=%zu", (person_guid)?person_guid:"", sizeof(PERSON));
+ debug(D_REGISTRY, "Registry: registry_person_allocate('%s'): allocating new person, sizeof(PERSON)=%zu", (person_guid)?person_guid:"", sizeof(PERSON));
- p = malloc(sizeof(PERSON));
- if(!p) fatal("Registry: cannot allocate memory for new person.");
+ p = mallocz(sizeof(PERSON));
- if(!person_guid) {
- for (; ;) {
- uuid_t uuid;
- uuid_generate(uuid);
- uuid_unparse_lower(uuid, p->guid);
+ if(!person_guid) {
+ for (; ;) {
+ uuid_t uuid;
+ uuid_generate(uuid);
+ uuid_unparse_lower(uuid, p->guid);
- debug(D_REGISTRY, "Registry: Checking if the generated person guid '%s' is unique", p->guid);
- if (!dictionary_get(registry.persons, p->guid)) {
- debug(D_REGISTRY, "Registry: generated person guid '%s' is unique", p->guid);
- break;
- }
- else
- info("Registry: generated person guid '%s' found in the registry. Retrying...", p->guid);
- }
- }
- else
- strncpyz(p->guid, person_guid, 36);
+ debug(D_REGISTRY, "Registry: Checking if the generated person guid '%s' is unique", p->guid);
+ if (!dictionary_get(registry.persons, p->guid)) {
+ debug(D_REGISTRY, "Registry: generated person guid '%s' is unique", p->guid);
+ break;
+ }
+ else
+ info("Registry: generated person guid '%s' found in the registry. Retrying...", p->guid);
+ }
+ }
+ else
+ strncpyz(p->guid, person_guid, 36);
- debug(D_REGISTRY, "Registry: registry_person_allocate('%s'): creating dictionary of urls", p->guid);
- p->urls = dictionary_create(DICTIONARY_FLAGS);
+ debug(D_REGISTRY, "Registry: registry_person_allocate('%s'): creating dictionary of urls", p->guid);
+ p->urls = dictionary_create(DICTIONARY_FLAGS);
- p->first_t = p->last_t = when;
- p->usages = 0;
+ p->first_t = p->last_t = when;
+ p->usages = 0;
- registry.persons_memory += sizeof(PERSON);
+ registry.persons_memory += sizeof(PERSON);
- registry.persons_count++;
- dictionary_set(registry.persons, p->guid, p, sizeof(PERSON));
+ registry.persons_count++;
+ dictionary_set(registry.persons, p->guid, p, sizeof(PERSON));
- return p;
+ return p;
}
@@ -572,258 +543,260 @@ static inline PERSON *registry_person_allocate(const char *person_guid, time_t w
// 3. if it is not valid, create a new one
// 4. return it
static inline PERSON *registry_person_get(const char *person_guid, time_t when) {
- PERSON *p = NULL;
+ PERSON *p = NULL;
- registry_persons_lock();
+ registry_persons_lock();
- if(person_guid && *person_guid) {
- char buf[36 + 1];
- // validate it is a GUID
- if(unlikely(registry_regenerate_guid(person_guid, buf) == -1))
- info("Registry: person guid '%s' is not a valid guid. Ignoring it.", person_guid);
- else {
- person_guid = buf;
- p = registry_person_find(person_guid);
- if(!p) person_guid = NULL;
- }
- }
+ if(person_guid && *person_guid) {
+ char buf[36 + 1];
+ // validate it is a GUID
+ if(unlikely(registry_regenerate_guid(person_guid, buf) == -1))
+ info("Registry: person guid '%s' is not a valid guid. Ignoring it.", person_guid);
+ else {
+ person_guid = buf;
+ p = registry_person_find(person_guid);
+ if(!p) person_guid = NULL;
+ }
+ }
- if(!p) p = registry_person_allocate(NULL, when);
+ if(!p) p = registry_person_allocate(NULL, when);
- registry_persons_unlock();
+ registry_persons_unlock();
- return p;
+ return p;
}
// ----------------------------------------------------------------------------
// LINKING OF OBJECTS
static inline PERSON_URL *registry_person_link_to_url(PERSON *p, MACHINE *m, URL *u, char *name, size_t namelen, time_t when) {
- debug(D_REGISTRY, "registry_person_link_to_url('%s', '%s', '%s'): searching for URL in person", p->guid, m->guid, u->url);
-
- registry_person_urls_lock(p);
-
- PERSON_URL *pu = dictionary_get(p->urls, u->url);
- if(!pu) {
- debug(D_REGISTRY, "registry_person_link_to_url('%s', '%s', '%s'): not found", p->guid, m->guid, u->url);
- pu = registry_person_url_allocate(p, m, u, name, namelen, when);
- registry.persons_urls_count++;
- }
- else {
- debug(D_REGISTRY, "registry_person_link_to_url('%s', '%s', '%s'): found", p->guid, m->guid, u->url);
- pu->usages++;
- if(likely(pu->last_t < when)) pu->last_t = when;
-
- if(pu->machine != m) {
- MACHINE_URL *mu = dictionary_get(pu->machine->urls, u->url);
- if(mu) {
- info("registry_person_link_to_url('%s', '%s', '%s'): URL switched machines (old was '%s') - expiring it from previous machine.",
- p->guid, m->guid, u->url, pu->machine->guid);
- mu->flags |= REGISTRY_URL_FLAGS_EXPIRED;
- }
- else {
- info("registry_person_link_to_url('%s', '%s', '%s'): URL switched machines (old was '%s') - but the URL is not linked to the old machine.",
- p->guid, m->guid, u->url, pu->machine->guid);
- }
-
- pu->machine->links--;
- pu->machine = m;
- }
-
- if(strcmp(pu->name, name)) {
- // the name of the PERSON_URL has changed !
- pu = registry_person_url_reallocate(p, m, u, name, namelen, when, pu);
- }
- }
-
- p->usages++;
- if(likely(p->last_t < when)) p->last_t = when;
-
- if(pu->flags & REGISTRY_URL_FLAGS_EXPIRED) {
- info("registry_person_link_to_url('%s', '%s', '%s'): accessing an expired URL. Re-enabling URL.", p->guid, m->guid, u->url);
- pu->flags &= ~REGISTRY_URL_FLAGS_EXPIRED;
- }
-
- registry_person_urls_unlock(p);
-
- return pu;
+ debug(D_REGISTRY, "registry_person_link_to_url('%s', '%s', '%s'): searching for URL in person", p->guid, m->guid, u->url);
+
+ registry_person_urls_lock(p);
+
+ PERSON_URL *pu = dictionary_get(p->urls, u->url);
+ if(!pu) {
+ debug(D_REGISTRY, "registry_person_link_to_url('%s', '%s', '%s'): not found", p->guid, m->guid, u->url);
+ pu = registry_person_url_allocate(p, m, u, name, namelen, when);
+ registry.persons_urls_count++;
+ }
+ else {
+ debug(D_REGISTRY, "registry_person_link_to_url('%s', '%s', '%s'): found", p->guid, m->guid, u->url);
+ pu->usages++;
+ if(likely(pu->last_t < (uint32_t)when)) pu->last_t = when;
+
+ if(pu->machine != m) {
+ MACHINE_URL *mu = dictionary_get(pu->machine->urls, u->url);
+ if(mu) {
+ info("registry_person_link_to_url('%s', '%s', '%s'): URL switched machines (old was '%s') - expiring it from previous machine.",
+ p->guid, m->guid, u->url, pu->machine->guid);
+ mu->flags |= REGISTRY_URL_FLAGS_EXPIRED;
+ }
+ else {
+ info("registry_person_link_to_url('%s', '%s', '%s'): URL switched machines (old was '%s') - but the URL is not linked to the old machine.",
+ p->guid, m->guid, u->url, pu->machine->guid);
+ }
+
+ pu->machine->links--;
+ pu->machine = m;
+ }
+
+ if(strcmp(pu->name, name)) {
+ // the name of the PERSON_URL has changed !
+ pu = registry_person_url_reallocate(p, m, u, name, namelen, when, pu);
+ }
+ }
+
+ p->usages++;
+ if(likely(p->last_t < (uint32_t)when)) p->last_t = when;
+
+ if(pu->flags & REGISTRY_URL_FLAGS_EXPIRED) {
+ info("registry_person_link_to_url('%s', '%s', '%s'): accessing an expired URL. Re-enabling URL.", p->guid, m->guid, u->url);
+ pu->flags &= ~REGISTRY_URL_FLAGS_EXPIRED;
+ }
+
+ registry_person_urls_unlock(p);
+
+ return pu;
}
static inline MACHINE_URL *registry_machine_link_to_url(PERSON *p, MACHINE *m, URL *u, time_t when) {
- debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s', '%s'): searching for URL in machine", p->guid, m->guid, u->url);
+ debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s', '%s'): searching for URL in machine", p->guid, m->guid, u->url);
- registry_machine_urls_lock(m);
+ registry_machine_urls_lock(m);
- MACHINE_URL *mu = dictionary_get(m->urls, u->url);
- if(!mu) {
- debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s', '%s'): not found", p->guid, m->guid, u->url);
- mu = registry_machine_url_allocate(m, u, when);
- registry.machines_urls_count++;
- }
- else {
- debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s', '%s'): found", p->guid, m->guid, u->url);
- mu->usages++;
- if(likely(mu->last_t < when)) mu->last_t = when;
- }
+ MACHINE_URL *mu = dictionary_get(m->urls, u->url);
+ if(!mu) {
+ debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s', '%s'): not found", p->guid, m->guid, u->url);
+ mu = registry_machine_url_allocate(m, u, when);
+ registry.machines_urls_count++;
+ }
+ else {
+ debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s', '%s'): found", p->guid, m->guid, u->url);
+ mu->usages++;
+ if(likely(mu->last_t < (uint32_t)when)) mu->last_t = when;
+ }
- //debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s', '%s'): indexing person in machine", p->guid, m->guid, u->url);
- //dictionary_set(mu->persons, p->guid, p, sizeof(PERSON));
+ //debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s', '%s'): indexing person in machine", p->guid, m->guid, u->url);
+ //dictionary_set(mu->persons, p->guid, p, sizeof(PERSON));
- m->usages++;
- if(likely(m->last_t < when)) m->last_t = when;
+ m->usages++;
+ if(likely(m->last_t < (uint32_t)when)) m->last_t = when;
- if(mu->flags & REGISTRY_URL_FLAGS_EXPIRED) {
- info("registry_machine_link_to_url('%s', '%s', '%s'): accessing an expired URL.", p->guid, m->guid, u->url);
- mu->flags &= ~REGISTRY_URL_FLAGS_EXPIRED;
- }
+ if(mu->flags & REGISTRY_URL_FLAGS_EXPIRED) {
+ info("registry_machine_link_to_url('%s', '%s', '%s'): accessing an expired URL.", p->guid, m->guid, u->url);
+ mu->flags &= ~REGISTRY_URL_FLAGS_EXPIRED;
+ }
- registry_machine_urls_unlock(m);
+ registry_machine_urls_unlock(m);
- return mu;
+ return mu;
}
// ----------------------------------------------------------------------------
// REGISTRY LOG LOAD/SAVE
static inline int registry_should_save_db(void) {
- debug(D_REGISTRY, "log entries %llu, max %llu", registry.log_count, registry.save_registry_every_entries);
- return registry.log_count > registry.save_registry_every_entries;
+ debug(D_REGISTRY, "log entries %llu, max %llu", registry.log_count, registry.save_registry_every_entries);
+ return registry.log_count > registry.save_registry_every_entries;
}
static inline void registry_log(const char action, PERSON *p, MACHINE *m, URL *u, char *name) {
- if(likely(registry.log_fp)) {
- // we lock only if the file is open
- // to allow replaying the log at registry_log_load()
- registry_log_lock();
-
- if(unlikely(fprintf(registry.log_fp, "%c\t%08x\t%s\t%s\t%s\t%s\n",
- action,
- p->last_t,
- p->guid,
- m->guid,
- name,
- u->url) < 0))
- error("Registry: failed to save log. Registry data may be lost in case of abnormal restart.");
-
- // we increase the counter even on failures
- // so that the registry will be saved periodically
- registry.log_count++;
-
- registry_log_unlock();
-
- // this must be outside the log_lock(), or a deadlock will happen.
- // registry_save() checks the same inside the log_lock, so only
- // one thread will save the db
- if(unlikely(registry_should_save_db()))
- registry_save();
- }
+ if(likely(registry.log_fp)) {
+ // we lock only if the file is open
+ // to allow replaying the log at registry_log_load()
+ registry_log_lock();
+
+ if(unlikely(fprintf(registry.log_fp, "%c\t%08x\t%s\t%s\t%s\t%s\n",
+ action,
+ p->last_t,
+ p->guid,
+ m->guid,
+ name,
+ u->url) < 0))
+ error("Registry: failed to save log. Registry data may be lost in case of abnormal restart.");
+
+ // we increase the counter even on failures
+ // so that the registry will be saved periodically
+ registry.log_count++;
+
+ registry_log_unlock();
+
+ // this must be outside the log_lock(), or a deadlock will happen.
+ // registry_save() checks the same inside the log_lock, so only
+ // one thread will save the db
+ if(unlikely(registry_should_save_db()))
+ registry_save();
+ }
}
static inline int registry_log_open_nolock(void) {
- if(registry.log_fp)
- fclose(registry.log_fp);
+ if(registry.log_fp)
+ fclose(registry.log_fp);
- registry.log_fp = fopen(registry.log_filename, "a");
+ registry.log_fp = fopen(registry.log_filename, "a");
- if(registry.log_fp) {
- if (setvbuf(registry.log_fp, NULL, _IOLBF, 0) != 0)
- error("Cannot set line buffering on registry log file.");
- return 0;
- }
+ if(registry.log_fp) {
+ if (setvbuf(registry.log_fp, NULL, _IOLBF, 0) != 0)
+ error("Cannot set line buffering on registry log file.");
+ return 0;
+ }
- error("Cannot open registry log file '%s'. Registry data will be lost in case of netdata or server crash.", registry.log_filename);
- return -1;
+ error("Cannot open registry log file '%s'. Registry data will be lost in case of netdata or server crash.", registry.log_filename);
+ return -1;
}
static inline void registry_log_close_nolock(void) {
- if(registry.log_fp) {
- fclose(registry.log_fp);
- registry.log_fp = NULL;
- }
+ if(registry.log_fp) {
+ fclose(registry.log_fp);
+ registry.log_fp = NULL;
+ }
}
static inline void registry_log_recreate_nolock(void) {
- if(registry.log_fp != NULL) {
- registry_log_close_nolock();
+ if(registry.log_fp != NULL) {
+ registry_log_close_nolock();
- // open it with truncate
- registry.log_fp = fopen(registry.log_filename, "w");
- if(registry.log_fp) fclose(registry.log_fp);
- else error("Cannot truncate registry log '%s'", registry.log_filename);
+ // open it with truncate
+ registry.log_fp = fopen(registry.log_filename, "w");
+ if(registry.log_fp) fclose(registry.log_fp);
+ else error("Cannot truncate registry log '%s'", registry.log_filename);
- registry.log_fp = NULL;
+ registry.log_fp = NULL;
- registry_log_open_nolock();
- }
+ registry_log_open_nolock();
+ }
}
int registry_log_load(void) {
- char *s, buf[4096 + 1];
- size_t line = -1;
-
- // closing the log is required here
- // otherwise we will append to it the values we read
- registry_log_close_nolock();
-
- debug(D_REGISTRY, "Registry: loading active db from: %s", registry.log_filename);
- FILE *fp = fopen(registry.log_filename, "r");
- if(!fp)
- error("Registry: cannot open registry file: %s", registry.log_filename);
- else {
- line = 0;
- size_t len = 0;
- while ((s = fgets_trim_len(buf, 4096, fp, &len))) {
- line++;
-
- switch (s[0]) {
- case 'A': // accesses
- case 'D': // deletes
-
- // verify it is valid
- if (unlikely(len < 85 || s[1] != '\t' || s[10] != '\t' || s[47] != '\t' || s[84] != '\t')) {
- error("Registry: log line %u is wrong (len = %zu).", line, len);
- continue;
- }
- s[1] = s[10] = s[47] = s[84] = '\0';
-
- // get the variables
- time_t when = strtoul(&s[2], NULL, 16);
- char *person_guid = &s[11];
- char *machine_guid = &s[48];
- char *name = &s[85];
-
- // skip the name to find the url
- char *url = name;
- while(*url && *url != '\t') url++;
- if(!*url) {
- error("Registry: log line %u does not have a url.", line);
- continue;
- }
- *url++ = '\0';
-
- // make sure the person exists
- // without this, a new person guid will be created
- PERSON *p = registry_person_find(person_guid);
- if(!p) p = registry_person_allocate(person_guid, when);
-
- if(s[0] == 'A')
- registry_request_access(p->guid, machine_guid, url, name, when);
- else
- registry_request_delete(p->guid, machine_guid, url, name, when);
-
- break;
-
- default:
- error("Registry: ignoring line %zu of filename '%s': %s.", line, registry.log_filename, s);
- break;
- }
- }
- }
-
- // open the log again
- registry_log_open_nolock();
-
- return line;
+ char *s, buf[4096 + 1];
+ size_t line = -1;
+
+ // closing the log is required here
+ // otherwise we will append to it the values we read
+ registry_log_close_nolock();
+
+ debug(D_REGISTRY, "Registry: loading active db from: %s", registry.log_filename);
+ FILE *fp = fopen(registry.log_filename, "r");
+ if(!fp)
+ error("Registry: cannot open registry file: %s", registry.log_filename);
+ else {
+ line = 0;
+ size_t len = 0;
+ while ((s = fgets_trim_len(buf, 4096, fp, &len))) {
+ line++;
+
+ switch (s[0]) {
+ case 'A': // accesses
+ case 'D': // deletes
+
+ // verify it is valid
+ if (unlikely(len < 85 || s[1] != '\t' || s[10] != '\t' || s[47] != '\t' || s[84] != '\t')) {
+ error("Registry: log line %zu is wrong (len = %zu).", line, len);
+ continue;
+ }
+ s[1] = s[10] = s[47] = s[84] = '\0';
+
+ // get the variables
+ time_t when = strtoul(&s[2], NULL, 16);
+ char *person_guid = &s[11];
+ char *machine_guid = &s[48];
+ char *name = &s[85];
+
+ // skip the name to find the url
+ char *url = name;
+ while(*url && *url != '\t') url++;
+ if(!*url) {
+ error("Registry: log line %zu does not have a url.", line);
+ continue;
+ }
+ *url++ = '\0';
+
+ // make sure the person exists
+ // without this, a new person guid will be created
+ PERSON *p = registry_person_find(person_guid);
+ if(!p) p = registry_person_allocate(person_guid, when);
+
+ if(s[0] == 'A')
+ registry_request_access(p->guid, machine_guid, url, name, when);
+ else
+ registry_request_delete(p->guid, machine_guid, url, name, when);
+
+ break;
+
+ default:
+ error("Registry: ignoring line %zu of filename '%s': %s.", line, registry.log_filename, s);
+ break;
+ }
+ }
+
+ fclose(fp);
+ }
+
+ // open the log again
+ registry_log_open_nolock();
+
+ return line;
}
@@ -831,176 +804,176 @@ int registry_log_load(void) {
// REGISTRY REQUESTS
PERSON *registry_request_access(char *person_guid, char *machine_guid, char *url, char *name, time_t when) {
- debug(D_REGISTRY, "registry_request_access('%s', '%s', '%s'): NEW REQUEST", (person_guid)?person_guid:"", machine_guid, url);
+ debug(D_REGISTRY, "registry_request_access('%s', '%s', '%s'): NEW REQUEST", (person_guid)?person_guid:"", machine_guid, url);
- MACHINE *m = registry_machine_get(machine_guid, when);
- if(!m) return NULL;
+ MACHINE *m = registry_machine_get(machine_guid, when);
+ if(!m) return NULL;
- // make sure the name is valid
- size_t namelen;
- name = registry_fix_machine_name(name, &namelen);
+ // make sure the name is valid
+ size_t namelen;
+ name = registry_fix_machine_name(name, &namelen);
- size_t urllen;
- url = registry_fix_url(url, &urllen);
+ size_t urllen;
+ url = registry_fix_url(url, &urllen);
- URL *u = registry_url_get(url, urllen);
- PERSON *p = registry_person_get(person_guid, when);
+ URL *u = registry_url_get(url, urllen);
+ PERSON *p = registry_person_get(person_guid, when);
- registry_person_link_to_url(p, m, u, name, namelen, when);
- registry_machine_link_to_url(p, m, u, when);
+ registry_person_link_to_url(p, m, u, name, namelen, when);
+ registry_machine_link_to_url(p, m, u, when);
- registry_log('A', p, m, u, name);
+ registry_log('A', p, m, u, name);
- registry.usages_count++;
- return p;
+ registry.usages_count++;
+ return p;
}
// verify the person, the machine and the URL exist in our DB
PERSON_URL *registry_verify_request(char *person_guid, char *machine_guid, char *url, PERSON **pp, MACHINE **mm) {
- char pbuf[36 + 1], mbuf[36 + 1];
-
- if(!person_guid || !*person_guid || !machine_guid || !*machine_guid || !url || !*url) {
- info("Registry Request Verification: invalid request! person: '%s', machine '%s', url '%s'", person_guid?person_guid:"UNSET", machine_guid?machine_guid:"UNSET", url?url:"UNSET");
- return NULL;
- }
-
- // normalize the url
- url = registry_fix_url(url, NULL);
-
- // make sure the person GUID is valid
- if(registry_regenerate_guid(person_guid, pbuf) == -1) {
- info("Registry Request Verification: invalid person GUID, person: '%s', machine '%s', url '%s'", person_guid, machine_guid, url);
- return NULL;
- }
- person_guid = pbuf;
-
- // make sure the machine GUID is valid
- if(registry_regenerate_guid(machine_guid, mbuf) == -1) {
- info("Registry Request Verification: invalid machine GUID, person: '%s', machine '%s', url '%s'", person_guid, machine_guid, url);
- return NULL;
- }
- machine_guid = mbuf;
-
- // make sure the machine exists
- MACHINE *m = registry_machine_find(machine_guid);
- if(!m) {
- info("Registry Request Verification: machine not found, person: '%s', machine '%s', url '%s'", person_guid, machine_guid, url);
- return NULL;
- }
- if(mm) *mm = m;
-
- // make sure the person exist
- PERSON *p = registry_person_find(person_guid);
- if(!p) {
- info("Registry Request Verification: person not found, person: '%s', machine '%s', url '%s'", person_guid, machine_guid, url);
- return NULL;
- }
- if(pp) *pp = p;
-
- PERSON_URL *pu = dictionary_get(p->urls, url);
- if(!pu) {
- info("Registry Request Verification: URL not found for person, person: '%s', machine '%s', url '%s'", person_guid, machine_guid, url);
- return NULL;
- }
- return pu;
+ char pbuf[36 + 1], mbuf[36 + 1];
+
+ if(!person_guid || !*person_guid || !machine_guid || !*machine_guid || !url || !*url) {
+ info("Registry Request Verification: invalid request! person: '%s', machine '%s', url '%s'", person_guid?person_guid:"UNSET", machine_guid?machine_guid:"UNSET", url?url:"UNSET");
+ return NULL;
+ }
+
+ // normalize the url
+ url = registry_fix_url(url, NULL);
+
+ // make sure the person GUID is valid
+ if(registry_regenerate_guid(person_guid, pbuf) == -1) {
+ info("Registry Request Verification: invalid person GUID, person: '%s', machine '%s', url '%s'", person_guid, machine_guid, url);
+ return NULL;
+ }
+ person_guid = pbuf;
+
+ // make sure the machine GUID is valid
+ if(registry_regenerate_guid(machine_guid, mbuf) == -1) {
+ info("Registry Request Verification: invalid machine GUID, person: '%s', machine '%s', url '%s'", person_guid, machine_guid, url);
+ return NULL;
+ }
+ machine_guid = mbuf;
+
+ // make sure the machine exists
+ MACHINE *m = registry_machine_find(machine_guid);
+ if(!m) {
+ info("Registry Request Verification: machine not found, person: '%s', machine '%s', url '%s'", person_guid, machine_guid, url);
+ return NULL;
+ }
+ if(mm) *mm = m;
+
+ // make sure the person exist
+ PERSON *p = registry_person_find(person_guid);
+ if(!p) {
+ info("Registry Request Verification: person not found, person: '%s', machine '%s', url '%s'", person_guid, machine_guid, url);
+ return NULL;
+ }
+ if(pp) *pp = p;
+
+ PERSON_URL *pu = dictionary_get(p->urls, url);
+ if(!pu) {
+ info("Registry Request Verification: URL not found for person, person: '%s', machine '%s', url '%s'", person_guid, machine_guid, url);
+ return NULL;
+ }
+ return pu;
}
PERSON *registry_request_delete(char *person_guid, char *machine_guid, char *url, char *delete_url, time_t when) {
- (void)when;
+ (void)when;
- PERSON *p = NULL;
- MACHINE *m = NULL;
- PERSON_URL *pu = registry_verify_request(person_guid, machine_guid, url, &p, &m);
- if(!pu || !p || !m) return NULL;
+ PERSON *p = NULL;
+ MACHINE *m = NULL;
+ PERSON_URL *pu = registry_verify_request(person_guid, machine_guid, url, &p, &m);
+ if(!pu || !p || !m) return NULL;
- // normalize the url
- delete_url = registry_fix_url(delete_url, NULL);
+ // normalize the url
+ delete_url = registry_fix_url(delete_url, NULL);
- // make sure the user is not deleting the url it uses
- if(!strcmp(delete_url, pu->url->url)) {
- info("Registry Delete Request: delete URL is the one currently accessed, person: '%s', machine '%s', url '%s', delete url '%s'", p->guid, m->guid, pu->url->url, delete_url);
- return NULL;
- }
+ // make sure the user is not deleting the url it uses
+ if(!strcmp(delete_url, pu->url->url)) {
+ info("Registry Delete Request: delete URL is the one currently accessed, person: '%s', machine '%s', url '%s', delete url '%s'", p->guid, m->guid, pu->url->url, delete_url);
+ return NULL;
+ }
- registry_person_urls_lock(p);
+ registry_person_urls_lock(p);
- PERSON_URL *dpu = dictionary_get(p->urls, delete_url);
- if(!dpu) {
- info("Registry Delete Request: URL not found for person: '%s', machine '%s', url '%s', delete url '%s'", p->guid, m->guid, pu->url->url, delete_url);
- registry_person_urls_unlock(p);
- return NULL;
- }
+ PERSON_URL *dpu = dictionary_get(p->urls, delete_url);
+ if(!dpu) {
+ info("Registry Delete Request: URL not found for person: '%s', machine '%s', url '%s', delete url '%s'", p->guid, m->guid, pu->url->url, delete_url);
+ registry_person_urls_unlock(p);
+ return NULL;
+ }
- registry_log('D', p, m, pu->url, dpu->url->url);
+ registry_log('D', p, m, pu->url, dpu->url->url);
- dictionary_del(p->urls, dpu->url->url);
- registry_url_unlink_nolock(dpu->url);
- free(dpu);
+ dictionary_del(p->urls, dpu->url->url);
+ registry_url_unlink_nolock(dpu->url);
+ freez(dpu);
- registry_person_urls_unlock(p);
- return p;
+ registry_person_urls_unlock(p);
+ return p;
}
// a structure to pass to the dictionary_get_all() callback handler
struct machine_request_callback_data {
- MACHINE *find_this_machine;
- PERSON_URL *result;
+ MACHINE *find_this_machine;
+ PERSON_URL *result;
};
// the callback function
// this will be run for every PERSON_URL of this PERSON
int machine_request_callback(void *entry, void *data) {
- PERSON_URL *mypu = (PERSON_URL *)entry;
- struct machine_request_callback_data *myrdata = (struct machine_request_callback_data *)data;
+ PERSON_URL *mypu = (PERSON_URL *)entry;
+ struct machine_request_callback_data *myrdata = (struct machine_request_callback_data *)data;
- if(mypu->machine == myrdata->find_this_machine) {
- myrdata->result = mypu;
- return -1; // this will also stop the walk through
- }
+ if(mypu->machine == myrdata->find_this_machine) {
+ myrdata->result = mypu;
+ return -1; // this will also stop the walk through
+ }
- return 0; // continue
+ return 0; // continue
}
MACHINE *registry_request_machine(char *person_guid, char *machine_guid, char *url, char *request_machine, time_t when) {
- (void)when;
+ (void)when;
- char mbuf[36 + 1];
+ char mbuf[36 + 1];
- PERSON *p = NULL;
- MACHINE *m = NULL;
- PERSON_URL *pu = registry_verify_request(person_guid, machine_guid, url, &p, &m);
- if(!pu || !p || !m) return NULL;
+ PERSON *p = NULL;
+ MACHINE *m = NULL;
+ PERSON_URL *pu = registry_verify_request(person_guid, machine_guid, url, &p, &m);
+ if(!pu || !p || !m) return NULL;
- // make sure the machine GUID is valid
- if(registry_regenerate_guid(request_machine, mbuf) == -1) {
- info("Registry Machine URLs request: invalid machine GUID, person: '%s', machine '%s', url '%s', request machine '%s'", p->guid, m->guid, pu->url->url, request_machine);
- return NULL;
- }
- request_machine = mbuf;
+ // make sure the machine GUID is valid
+ if(registry_regenerate_guid(request_machine, mbuf) == -1) {
+ info("Registry Machine URLs request: invalid machine GUID, person: '%s', machine '%s', url '%s', request machine '%s'", p->guid, m->guid, pu->url->url, request_machine);
+ return NULL;
+ }
+ request_machine = mbuf;
- // make sure the machine exists
- m = registry_machine_find(request_machine);
- if(!m) {
- info("Registry Machine URLs request: machine not found, person: '%s', machine '%s', url '%s', request machine '%s'", p->guid, m->guid, pu->url->url, request_machine);
- return NULL;
- }
+ // make sure the machine exists
+ m = registry_machine_find(request_machine);
+ if(!m) {
+ info("Registry Machine URLs request: machine not found, person: '%s', machine '%s', url '%s', request machine '%s'", p->guid, m->guid, pu->url->url, request_machine);
+ return NULL;
+ }
- // Verify the user has in the past accessed this machine
- // We will walk through the PERSON_URLs to find the machine
- // linking to our machine
+ // Verify the user has in the past accessed this machine
+ // We will walk through the PERSON_URLs to find the machine
+ // linking to our machine
- // a structure to pass to the dictionary_get_all() callback handler
- struct machine_request_callback_data rdata = { m, NULL };
+ // a structure to pass to the dictionary_get_all() callback handler
+ struct machine_request_callback_data rdata = { m, NULL };
- // request a walk through on the dictionary
- // no need for locking here, the underlying dictionary has its own
- dictionary_get_all(p->urls, machine_request_callback, &rdata);
+ // request a walk through on the dictionary
+ // no need for locking here, the underlying dictionary has its own
+ dictionary_get_all(p->urls, machine_request_callback, &rdata);
- if(rdata.result)
- return m;
+ if(rdata.result)
+ return m;
- return NULL;
+ return NULL;
}
@@ -1011,231 +984,243 @@ MACHINE *registry_request_machine(char *person_guid, char *machine_guid, char *u
#define REGISTRY_STATUS_FAILED "failed"
#define REGISTRY_STATUS_DISABLED "disabled"
-static inline void registry_set_person_cookie(struct web_client *w, PERSON *p) {
- char edate[100];
- time_t et = time(NULL) + registry.persons_expiration;
- struct tm etmbuf, *etm = gmtime_r(&et, &etmbuf);
- strftime(edate, sizeof(edate), "%a, %d %b %Y %H:%M:%S %Z", etm);
+int registry_verify_cookies_redirects(void) {
+ return registry.verify_cookies_redirects;
+}
- snprintfz(w->cookie1, COOKIE_MAX, NETDATA_REGISTRY_COOKIE_NAME "=%s; Expires=%s", p->guid, edate);
+const char *registry_to_announce(void) {
+ return registry.registry_to_announce;
+}
- if(registry.registry_domain && registry.registry_domain[0])
- snprintfz(w->cookie2, COOKIE_MAX, NETDATA_REGISTRY_COOKIE_NAME "=%s; Domain=%s; Expires=%s", p->guid, registry.registry_domain, edate);
+void registry_set_cookie(struct web_client *w, const char *guid) {
+ char edate[100];
+ time_t et = time(NULL) + registry.persons_expiration;
+ struct tm etmbuf, *etm = gmtime_r(&et, &etmbuf);
+ strftime(edate, sizeof(edate), "%a, %d %b %Y %H:%M:%S %Z", etm);
+
+ snprintfz(w->cookie1, COOKIE_MAX, NETDATA_REGISTRY_COOKIE_NAME "=%s; Expires=%s", guid, edate);
+
+ if(registry.registry_domain && registry.registry_domain[0])
+ snprintfz(w->cookie2, COOKIE_MAX, NETDATA_REGISTRY_COOKIE_NAME "=%s; Domain=%s; Expires=%s", guid, registry.registry_domain, edate);
+}
+
+static inline void registry_set_person_cookie(struct web_client *w, PERSON *p) {
+ registry_set_cookie(w, p->guid);
}
static inline void registry_json_header(struct web_client *w, const char *action, const char *status) {
- w->response.data->contenttype = CT_APPLICATION_JSON;
- buffer_flush(w->response.data);
- buffer_sprintf(w->response.data, "{\n\t\"action\": \"%s\",\n\t\"status\": \"%s\",\n\t\"hostname\": \"%s\",\n\t\"machine_guid\": \"%s\"",
- action, status, registry.hostname, registry.machine_guid);
+ buffer_flush(w->response.data);
+ w->response.data->contenttype = CT_APPLICATION_JSON;
+ buffer_sprintf(w->response.data, "{\n\t\"action\": \"%s\",\n\t\"status\": \"%s\",\n\t\"hostname\": \"%s\",\n\t\"machine_guid\": \"%s\"",
+ action, status, registry.hostname, registry.machine_guid);
}
static inline void registry_json_footer(struct web_client *w) {
- buffer_strcat(w->response.data, "\n}\n");
+ buffer_strcat(w->response.data, "\n}\n");
}
int registry_request_hello_json(struct web_client *w) {
- registry_json_header(w, "hello", REGISTRY_STATUS_OK);
+ registry_json_header(w, "hello", REGISTRY_STATUS_OK);
- buffer_sprintf(w->response.data, ",\n\t\"registry\": \"%s\"",
- registry.registry_to_announce);
+ buffer_sprintf(w->response.data, ",\n\t\"registry\": \"%s\"",
+ registry.registry_to_announce);
- registry_json_footer(w);
- return 200;
+ registry_json_footer(w);
+ return 200;
}
static inline int registry_json_disabled(struct web_client *w, const char *action) {
- registry_json_header(w, action, REGISTRY_STATUS_DISABLED);
+ registry_json_header(w, action, REGISTRY_STATUS_DISABLED);
- buffer_sprintf(w->response.data, ",\n\t\"registry\": \"%s\"",
- registry.registry_to_announce);
+ buffer_sprintf(w->response.data, ",\n\t\"registry\": \"%s\"",
+ registry.registry_to_announce);
- registry_json_footer(w);
- return 200;
+ registry_json_footer(w);
+ return 200;
}
// structure used be the callbacks below
struct registry_json_walk_person_urls_callback {
- PERSON *p;
- MACHINE *m;
- struct web_client *w;
- int count;
+ PERSON *p;
+ MACHINE *m;
+ struct web_client *w;
+ int count;
};
// callback for rendering PERSON_URLs
static inline int registry_json_person_url_callback(void *entry, void *data) {
- PERSON_URL *pu = (PERSON_URL *)entry;
- struct registry_json_walk_person_urls_callback *c = (struct registry_json_walk_person_urls_callback *)data;
- struct web_client *w = c->w;
+ PERSON_URL *pu = (PERSON_URL *)entry;
+ struct registry_json_walk_person_urls_callback *c = (struct registry_json_walk_person_urls_callback *)data;
+ struct web_client *w = c->w;
- if(unlikely(c->count++))
- buffer_strcat(w->response.data, ",");
+ if(unlikely(c->count++))
+ buffer_strcat(w->response.data, ",");
- buffer_sprintf(w->response.data, "\n\t\t[ \"%s\", \"%s\", %u000, %u, \"%s\" ]",
- pu->machine->guid, pu->url->url, pu->last_t, pu->usages, pu->name);
+ buffer_sprintf(w->response.data, "\n\t\t[ \"%s\", \"%s\", %u000, %u, \"%s\" ]",
+ pu->machine->guid, pu->url->url, pu->last_t, pu->usages, pu->name);
- return 1;
+ return 1;
}
// callback for rendering MACHINE_URLs
static inline int registry_json_machine_url_callback(void *entry, void *data) {
- MACHINE_URL *mu = (MACHINE_URL *)entry;
- struct registry_json_walk_person_urls_callback *c = (struct registry_json_walk_person_urls_callback *)data;
- struct web_client *w = c->w;
- MACHINE *m = c->m;
+ MACHINE_URL *mu = (MACHINE_URL *)entry;
+ struct registry_json_walk_person_urls_callback *c = (struct registry_json_walk_person_urls_callback *)data;
+ struct web_client *w = c->w;
+ MACHINE *m = c->m;
- if(unlikely(c->count++))
- buffer_strcat(w->response.data, ",");
+ if(unlikely(c->count++))
+ buffer_strcat(w->response.data, ",");
- buffer_sprintf(w->response.data, "\n\t\t[ \"%s\", \"%s\", %u000, %u ]",
- m->guid, mu->url->url, mu->last_t, mu->usages);
+ buffer_sprintf(w->response.data, "\n\t\t[ \"%s\", \"%s\", %u000, %u ]",
+ m->guid, mu->url->url, mu->last_t, mu->usages);
- return 1;
+ return 1;
}
// the main method for registering an access
int registry_request_access_json(struct web_client *w, char *person_guid, char *machine_guid, char *url, char *name, time_t when) {
- if(!registry.enabled)
- return registry_json_disabled(w, "access");
+ if(!registry.enabled)
+ return registry_json_disabled(w, "access");
- PERSON *p = registry_request_access(person_guid, machine_guid, url, name, when);
- if(!p) {
- registry_json_header(w, "access", REGISTRY_STATUS_FAILED);
- registry_json_footer(w);
- return 412;
- }
+ PERSON *p = registry_request_access(person_guid, machine_guid, url, name, when);
+ if(!p) {
+ registry_json_header(w, "access", REGISTRY_STATUS_FAILED);
+ registry_json_footer(w);
+ return 412;
+ }
- // set the cookie
- registry_set_person_cookie(w, p);
+ // set the cookie
+ registry_set_person_cookie(w, p);
- // generate the response
- registry_json_header(w, "access", REGISTRY_STATUS_OK);
+ // generate the response
+ registry_json_header(w, "access", REGISTRY_STATUS_OK);
- buffer_sprintf(w->response.data, ",\n\t\"person_guid\": \"%s\",\n\t\"urls\": [", p->guid);
- struct registry_json_walk_person_urls_callback c = { p, NULL, w, 0 };
- dictionary_get_all(p->urls, registry_json_person_url_callback, &c);
- buffer_strcat(w->response.data, "\n\t]\n");
+ buffer_sprintf(w->response.data, ",\n\t\"person_guid\": \"%s\",\n\t\"urls\": [", p->guid);
+ struct registry_json_walk_person_urls_callback c = { p, NULL, w, 0 };
+ dictionary_get_all(p->urls, registry_json_person_url_callback, &c);
+ buffer_strcat(w->response.data, "\n\t]\n");
- registry_json_footer(w);
- return 200;
+ registry_json_footer(w);
+ return 200;
}
// the main method for deleting a URL from a person
int registry_request_delete_json(struct web_client *w, char *person_guid, char *machine_guid, char *url, char *delete_url, time_t when) {
- if(!registry.enabled)
- return registry_json_disabled(w, "delete");
-
- PERSON *p = registry_request_delete(person_guid, machine_guid, url, delete_url, when);
- if(!p) {
- registry_json_header(w, "delete", REGISTRY_STATUS_FAILED);
- registry_json_footer(w);
- return 412;
- }
-
- // generate the response
- registry_json_header(w, "delete", REGISTRY_STATUS_OK);
- registry_json_footer(w);
- return 200;
+ if(!registry.enabled)
+ return registry_json_disabled(w, "delete");
+
+ PERSON *p = registry_request_delete(person_guid, machine_guid, url, delete_url, when);
+ if(!p) {
+ registry_json_header(w, "delete", REGISTRY_STATUS_FAILED);
+ registry_json_footer(w);
+ return 412;
+ }
+
+ // generate the response
+ registry_json_header(w, "delete", REGISTRY_STATUS_OK);
+ registry_json_footer(w);
+ return 200;
}
// the main method for searching the URLs of a netdata
int registry_request_search_json(struct web_client *w, char *person_guid, char *machine_guid, char *url, char *request_machine, time_t when) {
- if(!registry.enabled)
- return registry_json_disabled(w, "search");
+ if(!registry.enabled)
+ return registry_json_disabled(w, "search");
- MACHINE *m = registry_request_machine(person_guid, machine_guid, url, request_machine, when);
- if(!m) {
- registry_json_header(w, "search", REGISTRY_STATUS_FAILED);
- registry_json_footer(w);
- return 404;
- }
+ MACHINE *m = registry_request_machine(person_guid, machine_guid, url, request_machine, when);
+ if(!m) {
+ registry_json_header(w, "search", REGISTRY_STATUS_FAILED);
+ registry_json_footer(w);
+ return 404;
+ }
- registry_json_header(w, "search", REGISTRY_STATUS_OK);
+ registry_json_header(w, "search", REGISTRY_STATUS_OK);
- buffer_strcat(w->response.data, ",\n\t\"urls\": [");
- struct registry_json_walk_person_urls_callback c = { NULL, m, w, 0 };
- dictionary_get_all(m->urls, registry_json_machine_url_callback, &c);
- buffer_strcat(w->response.data, "\n\t]\n");
+ buffer_strcat(w->response.data, ",\n\t\"urls\": [");
+ struct registry_json_walk_person_urls_callback c = { NULL, m, w, 0 };
+ dictionary_get_all(m->urls, registry_json_machine_url_callback, &c);
+ buffer_strcat(w->response.data, "\n\t]\n");
- registry_json_footer(w);
- return 200;
+ registry_json_footer(w);
+ return 200;
}
// structure used be the callbacks below
struct registry_person_url_callback_verify_machine_exists_data {
- MACHINE *m;
- int count;
+ MACHINE *m;
+ int count;
};
int registry_person_url_callback_verify_machine_exists(void *entry, void *data) {
- struct registry_person_url_callback_verify_machine_exists_data *d = (struct registry_person_url_callback_verify_machine_exists_data *)data;
- PERSON_URL *pu = (PERSON_URL *)entry;
- MACHINE *m = d->m;
+ struct registry_person_url_callback_verify_machine_exists_data *d = (struct registry_person_url_callback_verify_machine_exists_data *)data;
+ PERSON_URL *pu = (PERSON_URL *)entry;
+ MACHINE *m = d->m;
- if(pu->machine == m)
- d->count++;
+ if(pu->machine == m)
+ d->count++;
- return 0;
+ return 0;
}
// the main method for switching user identity
int registry_request_switch_json(struct web_client *w, char *person_guid, char *machine_guid, char *url, char *new_person_guid, time_t when) {
- (void)url;
- (void)when;
-
- if(!registry.enabled)
- return registry_json_disabled(w, "switch");
-
- PERSON *op = registry_person_find(person_guid);
- if(!op) {
- registry_json_header(w, "switch", REGISTRY_STATUS_FAILED);
- registry_json_footer(w);
- return 430;
- }
-
- PERSON *np = registry_person_find(new_person_guid);
- if(!np) {
- registry_json_header(w, "switch", REGISTRY_STATUS_FAILED);
- registry_json_footer(w);
- return 431;
- }
-
- MACHINE *m = registry_machine_find(machine_guid);
- if(!m) {
- registry_json_header(w, "switch", REGISTRY_STATUS_FAILED);
- registry_json_footer(w);
- return 432;
- }
-
- struct registry_person_url_callback_verify_machine_exists_data data = { m, 0 };
-
- // verify the old person has access to this machine
- dictionary_get_all(op->urls, registry_person_url_callback_verify_machine_exists, &data);
- if(!data.count) {
- registry_json_header(w, "switch", REGISTRY_STATUS_FAILED);
- registry_json_footer(w);
- return 433;
- }
-
- // verify the new person has access to this machine
- data.count = 0;
- dictionary_get_all(np->urls, registry_person_url_callback_verify_machine_exists, &data);
- if(!data.count) {
- registry_json_header(w, "switch", REGISTRY_STATUS_FAILED);
- registry_json_footer(w);
- return 434;
- }
-
- // set the cookie of the new person
- // the user just switched identity
- registry_set_person_cookie(w, np);
-
- // generate the response
- registry_json_header(w, "switch", REGISTRY_STATUS_OK);
- buffer_sprintf(w->response.data, ",\n\t\"person_guid\": \"%s\"", np->guid);
- registry_json_footer(w);
- return 200;
+ (void)url;
+ (void)when;
+
+ if(!registry.enabled)
+ return registry_json_disabled(w, "switch");
+
+ PERSON *op = registry_person_find(person_guid);
+ if(!op) {
+ registry_json_header(w, "switch", REGISTRY_STATUS_FAILED);
+ registry_json_footer(w);
+ return 430;
+ }
+
+ PERSON *np = registry_person_find(new_person_guid);
+ if(!np) {
+ registry_json_header(w, "switch", REGISTRY_STATUS_FAILED);
+ registry_json_footer(w);
+ return 431;
+ }
+
+ MACHINE *m = registry_machine_find(machine_guid);
+ if(!m) {
+ registry_json_header(w, "switch", REGISTRY_STATUS_FAILED);
+ registry_json_footer(w);
+ return 432;
+ }
+
+ struct registry_person_url_callback_verify_machine_exists_data data = { m, 0 };
+
+ // verify the old person has access to this machine
+ dictionary_get_all(op->urls, registry_person_url_callback_verify_machine_exists, &data);
+ if(!data.count) {
+ registry_json_header(w, "switch", REGISTRY_STATUS_FAILED);
+ registry_json_footer(w);
+ return 433;
+ }
+
+ // verify the new person has access to this machine
+ data.count = 0;
+ dictionary_get_all(np->urls, registry_person_url_callback_verify_machine_exists, &data);
+ if(!data.count) {
+ registry_json_header(w, "switch", REGISTRY_STATUS_FAILED);
+ registry_json_footer(w);
+ return 434;
+ }
+
+ // set the cookie of the new person
+ // the user just switched identity
+ registry_set_person_cookie(w, np);
+
+ // generate the response
+ registry_json_header(w, "switch", REGISTRY_STATUS_OK);
+ buffer_sprintf(w->response.data, ",\n\t\"person_guid\": \"%s\"", np->guid);
+ registry_json_footer(w);
+ return 200;
}
@@ -1243,47 +1228,49 @@ int registry_request_switch_json(struct web_client *w, char *person_guid, char *
// REGISTRY THIS MACHINE UNIQUE ID
char *registry_get_this_machine_guid(void) {
- if(likely(registry.machine_guid[0]))
- return registry.machine_guid;
-
- // read it from disk
- int fd = open(registry.machine_guid_filename, O_RDONLY);
- if(fd != -1) {
- char buf[36 + 1];
- if(read(fd, buf, 36) != 36)
- error("Failed to read machine GUID from '%s'", registry.machine_guid_filename);
- else {
- buf[36] = '\0';
- if(registry_regenerate_guid(buf, registry.machine_guid) == -1) {
- error("Failed to validate machine GUID '%s' from '%s'. Ignoring it - this might mean this netdata will appear as duplicate in the registry.",
- buf, registry.machine_guid_filename);
-
- registry.machine_guid[0] = '\0';
- }
- }
- close(fd);
- }
-
- // generate a new one?
- if(!registry.machine_guid[0]) {
- uuid_t uuid;
-
- uuid_generate_time(uuid);
- uuid_unparse_lower(uuid, registry.machine_guid);
- registry.machine_guid[36] = '\0';
-
- // save it
- fd = open(registry.machine_guid_filename, O_WRONLY|O_CREAT|O_TRUNC, 444);
- if(fd == -1)
- fatal("Cannot create unique machine id file '%s'. Please fix this.", registry.machine_guid_filename);
-
- if(write(fd, registry.machine_guid, 36) != 36)
- fatal("Cannot write the unique machine id file '%s'. Please fix this.", registry.machine_guid_filename);
-
- close(fd);
- }
-
- return registry.machine_guid;
+ if(likely(registry.machine_guid[0]))
+ return registry.machine_guid;
+
+ // read it from disk
+ int fd = open(registry.machine_guid_filename, O_RDONLY);
+ if(fd != -1) {
+ char buf[36 + 1];
+ if(read(fd, buf, 36) != 36)
+ error("Failed to read machine GUID from '%s'", registry.machine_guid_filename);
+ else {
+ buf[36] = '\0';
+ if(registry_regenerate_guid(buf, registry.machine_guid) == -1) {
+ error("Failed to validate machine GUID '%s' from '%s'. Ignoring it - this might mean this netdata will appear as duplicate in the registry.",
+ buf, registry.machine_guid_filename);
+
+ registry.machine_guid[0] = '\0';
+ }
+ }
+ close(fd);
+ }
+
+ // generate a new one?
+ if(!registry.machine_guid[0]) {
+ uuid_t uuid;
+
+ uuid_generate_time(uuid);
+ uuid_unparse_lower(uuid, registry.machine_guid);
+ registry.machine_guid[36] = '\0';
+
+ // save it
+ fd = open(registry.machine_guid_filename, O_WRONLY|O_CREAT|O_TRUNC, 444);
+ if(fd == -1)
+ fatal("Cannot create unique machine id file '%s'. Please fix this.", registry.machine_guid_filename);
+
+ if(write(fd, registry.machine_guid, 36) != 36)
+ fatal("Cannot write the unique machine id file '%s'. Please fix this.", registry.machine_guid_filename);
+
+ close(fd);
+ }
+
+ setenv("NETDATA_REGISTRY_UNIQUE_ID", registry.machine_guid, 1);
+
+ return registry.machine_guid;
}
@@ -1291,548 +1278,557 @@ char *registry_get_this_machine_guid(void) {
// REGISTRY LOAD/SAVE
int registry_machine_save_url(void *entry, void *file) {
- MACHINE_URL *mu = entry;
- FILE *fp = file;
+ MACHINE_URL *mu = entry;
+ FILE *fp = file;
- debug(D_REGISTRY, "Registry: registry_machine_save_url('%s')", mu->url->url);
+ debug(D_REGISTRY, "Registry: registry_machine_save_url('%s')", mu->url->url);
- int ret = fprintf(fp, "V\t%08x\t%08x\t%08x\t%02x\t%s\n",
- mu->first_t,
- mu->last_t,
- mu->usages,
- mu->flags,
- mu->url->url
- );
+ int ret = fprintf(fp, "V\t%08x\t%08x\t%08x\t%02x\t%s\n",
+ mu->first_t,
+ mu->last_t,
+ mu->usages,
+ mu->flags,
+ mu->url->url
+ );
- // error handling is done at registry_save()
+ // error handling is done at registry_save()
- return ret;
+ return ret;
}
int registry_machine_save(void *entry, void *file) {
- MACHINE *m = entry;
- FILE *fp = file;
+ MACHINE *m = entry;
+ FILE *fp = file;
- debug(D_REGISTRY, "Registry: registry_machine_save('%s')", m->guid);
+ debug(D_REGISTRY, "Registry: registry_machine_save('%s')", m->guid);
- int ret = fprintf(fp, "M\t%08x\t%08x\t%08x\t%s\n",
- m->first_t,
- m->last_t,
- m->usages,
- m->guid
- );
+ int ret = fprintf(fp, "M\t%08x\t%08x\t%08x\t%s\n",
+ m->first_t,
+ m->last_t,
+ m->usages,
+ m->guid
+ );
- if(ret >= 0) {
- int ret2 = dictionary_get_all(m->urls, registry_machine_save_url, fp);
- if(ret2 < 0) return ret2;
- ret += ret2;
- }
+ if(ret >= 0) {
+ int ret2 = dictionary_get_all(m->urls, registry_machine_save_url, fp);
+ if(ret2 < 0) return ret2;
+ ret += ret2;
+ }
- // error handling is done at registry_save()
+ // error handling is done at registry_save()
- return ret;
+ return ret;
}
static inline int registry_person_save_url(void *entry, void *file) {
- PERSON_URL *pu = entry;
- FILE *fp = file;
+ PERSON_URL *pu = entry;
+ FILE *fp = file;
- debug(D_REGISTRY, "Registry: registry_person_save_url('%s')", pu->url->url);
+ debug(D_REGISTRY, "Registry: registry_person_save_url('%s')", pu->url->url);
- int ret = fprintf(fp, "U\t%08x\t%08x\t%08x\t%02x\t%s\t%s\t%s\n",
- pu->first_t,
- pu->last_t,
- pu->usages,
- pu->flags,
- pu->machine->guid,
- pu->name,
- pu->url->url
- );
+ int ret = fprintf(fp, "U\t%08x\t%08x\t%08x\t%02x\t%s\t%s\t%s\n",
+ pu->first_t,
+ pu->last_t,
+ pu->usages,
+ pu->flags,
+ pu->machine->guid,
+ pu->name,
+ pu->url->url
+ );
- // error handling is done at registry_save()
+ // error handling is done at registry_save()
- return ret;
+ return ret;
}
static inline int registry_person_save(void *entry, void *file) {
- PERSON *p = entry;
- FILE *fp = file;
+ PERSON *p = entry;
+ FILE *fp = file;
- debug(D_REGISTRY, "Registry: registry_person_save('%s')", p->guid);
+ debug(D_REGISTRY, "Registry: registry_person_save('%s')", p->guid);
- int ret = fprintf(fp, "P\t%08x\t%08x\t%08x\t%s\n",
- p->first_t,
- p->last_t,
- p->usages,
- p->guid
- );
+ int ret = fprintf(fp, "P\t%08x\t%08x\t%08x\t%s\n",
+ p->first_t,
+ p->last_t,
+ p->usages,
+ p->guid
+ );
- if(ret >= 0) {
- int ret2 = dictionary_get_all(p->urls, registry_person_save_url, fp);
- if (ret2 < 0) return ret2;
- ret += ret2;
- }
+ if(ret >= 0) {
+ int ret2 = dictionary_get_all(p->urls, registry_person_save_url, fp);
+ if (ret2 < 0) return ret2;
+ ret += ret2;
+ }
- // error handling is done at registry_save()
+ // error handling is done at registry_save()
- return ret;
+ return ret;
}
int registry_save(void) {
- if(!registry.enabled) return -1;
-
- // make sure the log is not updated
- registry_log_lock();
-
- if(unlikely(!registry_should_save_db())) {
- registry_log_unlock();
- return -2;
- }
-
- char tmp_filename[FILENAME_MAX + 1];
- char old_filename[FILENAME_MAX + 1];
-
- snprintfz(old_filename, FILENAME_MAX, "%s.old", registry.db_filename);
- snprintfz(tmp_filename, FILENAME_MAX, "%s.tmp", registry.db_filename);
-
- debug(D_REGISTRY, "Registry: Creating file '%s'", tmp_filename);
- FILE *fp = fopen(tmp_filename, "w");
- if(!fp) {
- error("Registry: Cannot create file: %s", tmp_filename);
- registry_log_unlock();
- return -1;
- }
-
- // dictionary_get_all() has its own locking, so this is safe to do
-
- debug(D_REGISTRY, "Saving all machines");
- int bytes1 = dictionary_get_all(registry.machines, registry_machine_save, fp);
- if(bytes1 < 0) {
- error("Registry: Cannot save registry machines - return value %d", bytes1);
- fclose(fp);
- registry_log_unlock();
- return bytes1;
- }
- debug(D_REGISTRY, "Registry: saving machines took %d bytes", bytes1);
-
- debug(D_REGISTRY, "Saving all persons");
- int bytes2 = dictionary_get_all(registry.persons, registry_person_save, fp);
- if(bytes2 < 0) {
- error("Registry: Cannot save registry persons - return value %d", bytes2);
- fclose(fp);
- registry_log_unlock();
- return bytes2;
- }
- debug(D_REGISTRY, "Registry: saving persons took %d bytes", bytes2);
-
- // save the totals
- fprintf(fp, "T\t%016llx\t%016llx\t%016llx\t%016llx\t%016llx\t%016llx\n",
- registry.persons_count,
- registry.machines_count,
- registry.usages_count + 1, // this is required - it is lost on db rotation
- registry.urls_count,
- registry.persons_urls_count,
- registry.machines_urls_count
- );
-
- fclose(fp);
-
- errno = 0;
-
- // remove the .old db
- debug(D_REGISTRY, "Registry: Removing old db '%s'", old_filename);
- if(unlink(old_filename) == -1 && errno != ENOENT)
- error("Registry: cannot remove old registry file '%s'", old_filename);
-
- // rename the db to .old
- debug(D_REGISTRY, "Registry: Link current db '%s' to .old: '%s'", registry.db_filename, old_filename);
- if(link(registry.db_filename, old_filename) == -1 && errno != ENOENT)
- error("Registry: cannot move file '%s' to '%s'. Saving registry DB failed!", tmp_filename, registry.db_filename);
-
- else {
- // remove the database (it is saved in .old)
- debug(D_REGISTRY, "Registry: removing db '%s'", registry.db_filename);
- if (unlink(registry.db_filename) == -1 && errno != ENOENT)
- error("Registry: cannot remove old registry file '%s'", registry.db_filename);
-
- // move the .tmp to make it active
- debug(D_REGISTRY, "Registry: linking tmp db '%s' to active db '%s'", tmp_filename, registry.db_filename);
- if (link(tmp_filename, registry.db_filename) == -1) {
- error("Registry: cannot move file '%s' to '%s'. Saving registry DB failed!", tmp_filename,
- registry.db_filename);
-
- // move the .old back
- debug(D_REGISTRY, "Registry: linking old db '%s' to active db '%s'", old_filename, registry.db_filename);
- if(link(old_filename, registry.db_filename) == -1)
- error("Registry: cannot move file '%s' to '%s'. Recovering the old registry DB failed!", old_filename, registry.db_filename);
- }
- else {
- debug(D_REGISTRY, "Registry: removing tmp db '%s'", tmp_filename);
- if(unlink(tmp_filename) == -1)
- error("Registry: cannot remove tmp registry file '%s'", tmp_filename);
-
- // it has been moved successfully
- // discard the current registry log
- registry_log_recreate_nolock();
-
- registry.log_count = 0;
- }
- }
-
- // continue operations
- registry_log_unlock();
-
- return -1;
+ if(!registry.enabled) return -1;
+
+ // make sure the log is not updated
+ registry_log_lock();
+
+ if(unlikely(!registry_should_save_db())) {
+ registry_log_unlock();
+ return -2;
+ }
+
+ char tmp_filename[FILENAME_MAX + 1];
+ char old_filename[FILENAME_MAX + 1];
+
+ snprintfz(old_filename, FILENAME_MAX, "%s.old", registry.db_filename);
+ snprintfz(tmp_filename, FILENAME_MAX, "%s.tmp", registry.db_filename);
+
+ debug(D_REGISTRY, "Registry: Creating file '%s'", tmp_filename);
+ FILE *fp = fopen(tmp_filename, "w");
+ if(!fp) {
+ error("Registry: Cannot create file: %s", tmp_filename);
+ registry_log_unlock();
+ return -1;
+ }
+
+ // dictionary_get_all() has its own locking, so this is safe to do
+
+ debug(D_REGISTRY, "Saving all machines");
+ int bytes1 = dictionary_get_all(registry.machines, registry_machine_save, fp);
+ if(bytes1 < 0) {
+ error("Registry: Cannot save registry machines - return value %d", bytes1);
+ fclose(fp);
+ registry_log_unlock();
+ return bytes1;
+ }
+ debug(D_REGISTRY, "Registry: saving machines took %d bytes", bytes1);
+
+ debug(D_REGISTRY, "Saving all persons");
+ int bytes2 = dictionary_get_all(registry.persons, registry_person_save, fp);
+ if(bytes2 < 0) {
+ error("Registry: Cannot save registry persons - return value %d", bytes2);
+ fclose(fp);
+ registry_log_unlock();
+ return bytes2;
+ }
+ debug(D_REGISTRY, "Registry: saving persons took %d bytes", bytes2);
+
+ // save the totals
+ fprintf(fp, "T\t%016llx\t%016llx\t%016llx\t%016llx\t%016llx\t%016llx\n",
+ registry.persons_count,
+ registry.machines_count,
+ registry.usages_count + 1, // this is required - it is lost on db rotation
+ registry.urls_count,
+ registry.persons_urls_count,
+ registry.machines_urls_count
+ );
+
+ fclose(fp);
+
+ errno = 0;
+
+ // remove the .old db
+ debug(D_REGISTRY, "Registry: Removing old db '%s'", old_filename);
+ if(unlink(old_filename) == -1 && errno != ENOENT)
+ error("Registry: cannot remove old registry file '%s'", old_filename);
+
+ // rename the db to .old
+ debug(D_REGISTRY, "Registry: Link current db '%s' to .old: '%s'", registry.db_filename, old_filename);
+ if(link(registry.db_filename, old_filename) == -1 && errno != ENOENT)
+ error("Registry: cannot move file '%s' to '%s'. Saving registry DB failed!", tmp_filename, registry.db_filename);
+
+ else {
+ // remove the database (it is saved in .old)
+ debug(D_REGISTRY, "Registry: removing db '%s'", registry.db_filename);
+ if (unlink(registry.db_filename) == -1 && errno != ENOENT)
+ error("Registry: cannot remove old registry file '%s'", registry.db_filename);
+
+ // move the .tmp to make it active
+ debug(D_REGISTRY, "Registry: linking tmp db '%s' to active db '%s'", tmp_filename, registry.db_filename);
+ if (link(tmp_filename, registry.db_filename) == -1) {
+ error("Registry: cannot move file '%s' to '%s'. Saving registry DB failed!", tmp_filename,
+ registry.db_filename);
+
+ // move the .old back
+ debug(D_REGISTRY, "Registry: linking old db '%s' to active db '%s'", old_filename, registry.db_filename);
+ if(link(old_filename, registry.db_filename) == -1)
+ error("Registry: cannot move file '%s' to '%s'. Recovering the old registry DB failed!", old_filename, registry.db_filename);
+ }
+ else {
+ debug(D_REGISTRY, "Registry: removing tmp db '%s'", tmp_filename);
+ if(unlink(tmp_filename) == -1)
+ error("Registry: cannot remove tmp registry file '%s'", tmp_filename);
+
+ // it has been moved successfully
+ // discard the current registry log
+ registry_log_recreate_nolock();
+
+ registry.log_count = 0;
+ }
+ }
+
+ // continue operations
+ registry_log_unlock();
+
+ return -1;
}
static inline size_t registry_load(void) {
- char *s, buf[4096 + 1];
- PERSON *p = NULL;
- MACHINE *m = NULL;
- URL *u = NULL;
- size_t line = 0;
-
- debug(D_REGISTRY, "Registry: loading active db from: '%s'", registry.db_filename);
- FILE *fp = fopen(registry.db_filename, "r");
- if(!fp) {
- error("Registry: cannot open registry file: '%s'", registry.db_filename);
- return 0;
- }
-
- size_t len = 0;
- buf[4096] = '\0';
- while((s = fgets_trim_len(buf, 4096, fp, &len))) {
- line++;
-
- debug(D_REGISTRY, "Registry: read line %zu to length %zu: %s", line, len, s);
- switch(*s) {
- case 'T': // totals
- if(unlikely(len != 103 || s[1] != '\t' || s[18] != '\t' || s[35] != '\t' || s[52] != '\t' || s[69] != '\t' || s[86] != '\t' || s[103] != '\0')) {
- error("Registry totals line %u is wrong (len = %zu).", line, len);
- continue;
- }
- registry.persons_count = strtoull(&s[2], NULL, 16);
- registry.machines_count = strtoull(&s[19], NULL, 16);
- registry.usages_count = strtoull(&s[36], NULL, 16);
- registry.urls_count = strtoull(&s[53], NULL, 16);
- registry.persons_urls_count = strtoull(&s[70], NULL, 16);
- registry.machines_urls_count = strtoull(&s[87], NULL, 16);
- break;
-
- case 'P': // person
- m = NULL;
- // verify it is valid
- if(unlikely(len != 65 || s[1] != '\t' || s[10] != '\t' || s[19] != '\t' || s[28] != '\t' || s[65] != '\0')) {
- error("Registry person line %u is wrong (len = %zu).", line, len);
- continue;
- }
-
- s[1] = s[10] = s[19] = s[28] = '\0';
- p = registry_person_allocate(&s[29], strtoul(&s[2], NULL, 16));
- p->last_t = strtoul(&s[11], NULL, 16);
- p->usages = strtoul(&s[20], NULL, 16);
- debug(D_REGISTRY, "Registry loaded person '%s', first: %u, last: %u, usages: %u", p->guid, p->first_t, p->last_t, p->usages);
- break;
-
- case 'M': // machine
- p = NULL;
- // verify it is valid
- if(unlikely(len != 65 || s[1] != '\t' || s[10] != '\t' || s[19] != '\t' || s[28] != '\t' || s[65] != '\0')) {
- error("Registry person line %u is wrong (len = %zu).", line, len);
- continue;
- }
-
- s[1] = s[10] = s[19] = s[28] = '\0';
- m = registry_machine_allocate(&s[29], strtoul(&s[2], NULL, 16));
- m->last_t = strtoul(&s[11], NULL, 16);
- m->usages = strtoul(&s[20], NULL, 16);
- debug(D_REGISTRY, "Registry loaded machine '%s', first: %u, last: %u, usages: %u", m->guid, m->first_t, m->last_t, m->usages);
- break;
-
- case 'U': // person URL
- if(unlikely(!p)) {
- error("Registry: ignoring line %zu, no person loaded: %s", line, s);
- continue;
- }
-
- // verify it is valid
- if(len < 69 || s[1] != '\t' || s[10] != '\t' || s[19] != '\t' || s[28] != '\t' || s[31] != '\t' || s[68] != '\t') {
- error("Registry person URL line %u is wrong (len = %zu).", line, len);
- continue;
- }
-
- s[1] = s[10] = s[19] = s[28] = s[31] = s[68] = '\0';
-
- // skip the name to find the url
- char *url = &s[69];
- while(*url && *url != '\t') url++;
- if(!*url) {
- error("Registry person URL line %u does not have a url.", line);
- continue;
- }
- *url++ = '\0';
-
- u = registry_url_allocate_nolock(url, strlen(url));
-
- time_t first_t = strtoul(&s[2], NULL, 16);
-
- m = registry_machine_find(&s[32]);
- if(!m) m = registry_machine_allocate(&s[32], first_t);
-
- PERSON_URL *pu = registry_person_url_allocate(p, m, u, &s[69], strlen(&s[69]), first_t);
- pu->last_t = strtoul(&s[11], NULL, 16);
- pu->usages = strtoul(&s[20], NULL, 16);
- pu->flags = strtoul(&s[29], NULL, 16);
- debug(D_REGISTRY, "Registry loaded person URL '%s' with name '%s' of machine '%s', first: %u, last: %u, usages: %u, flags: %02x", u->url, pu->name, m->guid, pu->first_t, pu->last_t, pu->usages, pu->flags);
- break;
-
- case 'V': // machine URL
- if(unlikely(!m)) {
- error("Registry: ignoring line %zu, no machine loaded: %s", line, s);
- continue;
- }
-
- // verify it is valid
- if(len < 32 || s[1] != '\t' || s[10] != '\t' || s[19] != '\t' || s[28] != '\t' || s[31] != '\t') {
- error("Registry person URL line %u is wrong (len = %zu).", line, len);
- continue;
- }
-
- s[1] = s[10] = s[19] = s[28] = s[31] = '\0';
- u = registry_url_allocate_nolock(&s[32], strlen(&s[32]));
-
- MACHINE_URL *mu = registry_machine_url_allocate(m, u, strtoul(&s[2], NULL, 16));
- mu->last_t = strtoul(&s[11], NULL, 16);
- mu->usages = strtoul(&s[20], NULL, 16);
- mu->flags = strtoul(&s[29], NULL, 16);
- debug(D_REGISTRY, "Registry loaded machine URL '%s', machine '%s', first: %u, last: %u, usages: %u, flags: %02x", u->url, m->guid, mu->first_t, mu->last_t, mu->usages, mu->flags);
- break;
-
- default:
- error("Registry: ignoring line %zu of filename '%s': %s.", line, registry.db_filename, s);
- break;
- }
- }
- fclose(fp);
-
- return line;
+ char *s, buf[4096 + 1];
+ PERSON *p = NULL;
+ MACHINE *m = NULL;
+ URL *u = NULL;
+ size_t line = 0;
+
+ debug(D_REGISTRY, "Registry: loading active db from: '%s'", registry.db_filename);
+ FILE *fp = fopen(registry.db_filename, "r");
+ if(!fp) {
+ error("Registry: cannot open registry file: '%s'", registry.db_filename);
+ return 0;
+ }
+
+ size_t len = 0;
+ buf[4096] = '\0';
+ while((s = fgets_trim_len(buf, 4096, fp, &len))) {
+ line++;
+
+ debug(D_REGISTRY, "Registry: read line %zu to length %zu: %s", line, len, s);
+ switch(*s) {
+ case 'T': // totals
+ if(unlikely(len != 103 || s[1] != '\t' || s[18] != '\t' || s[35] != '\t' || s[52] != '\t' || s[69] != '\t' || s[86] != '\t' || s[103] != '\0')) {
+ error("Registry totals line %zu is wrong (len = %zu).", line, len);
+ continue;
+ }
+ registry.persons_count = strtoull(&s[2], NULL, 16);
+ registry.machines_count = strtoull(&s[19], NULL, 16);
+ registry.usages_count = strtoull(&s[36], NULL, 16);
+ registry.urls_count = strtoull(&s[53], NULL, 16);
+ registry.persons_urls_count = strtoull(&s[70], NULL, 16);
+ registry.machines_urls_count = strtoull(&s[87], NULL, 16);
+ break;
+
+ case 'P': // person
+ m = NULL;
+ // verify it is valid
+ if(unlikely(len != 65 || s[1] != '\t' || s[10] != '\t' || s[19] != '\t' || s[28] != '\t' || s[65] != '\0')) {
+ error("Registry person line %zu is wrong (len = %zu).", line, len);
+ continue;
+ }
+
+ s[1] = s[10] = s[19] = s[28] = '\0';
+ p = registry_person_allocate(&s[29], strtoul(&s[2], NULL, 16));
+ p->last_t = strtoul(&s[11], NULL, 16);
+ p->usages = strtoul(&s[20], NULL, 16);
+ debug(D_REGISTRY, "Registry loaded person '%s', first: %u, last: %u, usages: %u", p->guid, p->first_t, p->last_t, p->usages);
+ break;
+
+ case 'M': // machine
+ p = NULL;
+ // verify it is valid
+ if(unlikely(len != 65 || s[1] != '\t' || s[10] != '\t' || s[19] != '\t' || s[28] != '\t' || s[65] != '\0')) {
+ error("Registry person line %zu is wrong (len = %zu).", line, len);
+ continue;
+ }
+
+ s[1] = s[10] = s[19] = s[28] = '\0';
+ m = registry_machine_allocate(&s[29], strtoul(&s[2], NULL, 16));
+ m->last_t = strtoul(&s[11], NULL, 16);
+ m->usages = strtoul(&s[20], NULL, 16);
+ debug(D_REGISTRY, "Registry loaded machine '%s', first: %u, last: %u, usages: %u", m->guid, m->first_t, m->last_t, m->usages);
+ break;
+
+ case 'U': // person URL
+ if(unlikely(!p)) {
+ error("Registry: ignoring line %zu, no person loaded: %s", line, s);
+ continue;
+ }
+
+ // verify it is valid
+ if(len < 69 || s[1] != '\t' || s[10] != '\t' || s[19] != '\t' || s[28] != '\t' || s[31] != '\t' || s[68] != '\t') {
+ error("Registry person URL line %zu is wrong (len = %zu).", line, len);
+ continue;
+ }
+
+ s[1] = s[10] = s[19] = s[28] = s[31] = s[68] = '\0';
+
+ // skip the name to find the url
+ char *url = &s[69];
+ while(*url && *url != '\t') url++;
+ if(!*url) {
+ error("Registry person URL line %zu does not have a url.", line);
+ continue;
+ }
+ *url++ = '\0';
+
+ u = registry_url_allocate_nolock(url, strlen(url));
+
+ time_t first_t = strtoul(&s[2], NULL, 16);
+
+ m = registry_machine_find(&s[32]);
+ if(!m) m = registry_machine_allocate(&s[32], first_t);
+
+ PERSON_URL *pu = registry_person_url_allocate(p, m, u, &s[69], strlen(&s[69]), first_t);
+ pu->last_t = strtoul(&s[11], NULL, 16);
+ pu->usages = strtoul(&s[20], NULL, 16);
+ pu->flags = strtoul(&s[29], NULL, 16);
+ debug(D_REGISTRY, "Registry loaded person URL '%s' with name '%s' of machine '%s', first: %u, last: %u, usages: %u, flags: %02x", u->url, pu->name, m->guid, pu->first_t, pu->last_t, pu->usages, pu->flags);
+ break;
+
+ case 'V': // machine URL
+ if(unlikely(!m)) {
+ error("Registry: ignoring line %zu, no machine loaded: %s", line, s);
+ continue;
+ }
+
+ // verify it is valid
+ if(len < 32 || s[1] != '\t' || s[10] != '\t' || s[19] != '\t' || s[28] != '\t' || s[31] != '\t') {
+ error("Registry person URL line %zu is wrong (len = %zu).", line, len);
+ continue;
+ }
+
+ s[1] = s[10] = s[19] = s[28] = s[31] = '\0';
+ u = registry_url_allocate_nolock(&s[32], strlen(&s[32]));
+
+ MACHINE_URL *mu = registry_machine_url_allocate(m, u, strtoul(&s[2], NULL, 16));
+ mu->last_t = strtoul(&s[11], NULL, 16);
+ mu->usages = strtoul(&s[20], NULL, 16);
+ mu->flags = strtoul(&s[29], NULL, 16);
+ debug(D_REGISTRY, "Registry loaded machine URL '%s', machine '%s', first: %u, last: %u, usages: %u, flags: %02x", u->url, m->guid, mu->first_t, mu->last_t, mu->usages, mu->flags);
+ break;
+
+ default:
+ error("Registry: ignoring line %zu of filename '%s': %s.", line, registry.db_filename, s);
+ break;
+ }
+ }
+ fclose(fp);
+
+ return line;
}
// ----------------------------------------------------------------------------
// REGISTRY
int registry_init(void) {
- char filename[FILENAME_MAX + 1];
-
- // registry enabled?
- registry.enabled = config_get_boolean("registry", "enabled", 0);
-
- // pathnames
- registry.pathname = config_get("registry", "registry db directory", VARLIB_DIR "/registry");
- if(mkdir(registry.pathname, 0755) == -1 && errno != EEXIST) {
- error("Cannot create directory '%s'. Registry disabled.", registry.pathname);
- registry.enabled = 0;
- return -1;
- }
-
- // filenames
- snprintfz(filename, FILENAME_MAX, "%s/netdata.public.unique.id", registry.pathname);
- registry.machine_guid_filename = config_get("registry", "netdata unique id file", filename);
- registry_get_this_machine_guid();
-
- snprintfz(filename, FILENAME_MAX, "%s/registry.db", registry.pathname);
- registry.db_filename = config_get("registry", "registry db file", filename);
-
- snprintfz(filename, FILENAME_MAX, "%s/registry-log.db", registry.pathname);
- registry.log_filename = config_get("registry", "registry log file", filename);
-
- // configuration options
- registry.save_registry_every_entries = config_get_number("registry", "registry save db every new entries", 1000000);
- registry.persons_expiration = config_get_number("registry", "registry expire idle persons days", 365) * 86400;
- registry.registry_domain = config_get("registry", "registry domain", "");
- registry.registry_to_announce = config_get("registry", "registry to announce", "https://registry.my-netdata.io");
- registry.hostname = config_get("registry", "registry hostname", config_get("global", "hostname", hostname));
-
- registry.max_url_length = config_get_number("registry", "max URL length", 1024);
- registry.max_name_length = config_get_number("registry", "max URL name length", 50);
-
-
- // initialize entries counters
- registry.persons_count = 0;
- registry.machines_count = 0;
- registry.usages_count = 0;
- registry.urls_count = 0;
- registry.persons_urls_count = 0;
- registry.machines_urls_count = 0;
-
- // initialize memory counters
- registry.persons_memory = 0;
- registry.machines_memory = 0;
- registry.urls_memory = 0;
- registry.persons_urls_memory = 0;
- registry.machines_urls_memory = 0;
-
- // initialize locks
- pthread_mutex_init(&registry.persons_lock, NULL);
- pthread_mutex_init(&registry.machines_lock, NULL);
- pthread_mutex_init(&registry.urls_lock, NULL);
- pthread_mutex_init(&registry.person_urls_lock, NULL);
- pthread_mutex_init(&registry.machine_urls_lock, NULL);
-
- // create dictionaries
- registry.persons = dictionary_create(DICTIONARY_FLAGS);
- registry.machines = dictionary_create(DICTIONARY_FLAGS);
- registry.urls = dictionary_create(DICTIONARY_FLAGS);
-
- // load the registry database
- if(registry.enabled) {
- registry_log_open_nolock();
- registry_load();
- registry_log_load();
- }
-
- return 0;
+ char filename[FILENAME_MAX + 1];
+
+ // registry enabled?
+ registry.enabled = config_get_boolean("registry", "enabled", 0);
+
+ // pathnames
+ registry.pathname = config_get("registry", "registry db directory", VARLIB_DIR "/registry");
+ if(mkdir(registry.pathname, 0770) == -1 && errno != EEXIST)
+ fatal("Cannot create directory '%s'.", registry.pathname);
+
+ // filenames
+ snprintfz(filename, FILENAME_MAX, "%s/netdata.public.unique.id", registry.pathname);
+ registry.machine_guid_filename = config_get("registry", "netdata unique id file", filename);
+ registry_get_this_machine_guid();
+
+ snprintfz(filename, FILENAME_MAX, "%s/registry.db", registry.pathname);
+ registry.db_filename = config_get("registry", "registry db file", filename);
+
+ snprintfz(filename, FILENAME_MAX, "%s/registry-log.db", registry.pathname);
+ registry.log_filename = config_get("registry", "registry log file", filename);
+
+ // configuration options
+ registry.save_registry_every_entries = config_get_number("registry", "registry save db every new entries", 1000000);
+ registry.persons_expiration = config_get_number("registry", "registry expire idle persons days", 365) * 86400;
+ registry.registry_domain = config_get("registry", "registry domain", "");
+ registry.registry_to_announce = config_get("registry", "registry to announce", "https://registry.my-netdata.io");
+ registry.hostname = config_get("registry", "registry hostname", config_get("global", "hostname", hostname));
+ registry.verify_cookies_redirects = config_get_boolean("registry", "verify browser cookies support", 1);
+
+ setenv("NETDATA_REGISTRY_HOSTNAME", registry.hostname, 1);
+ setenv("NETDATA_REGISTRY_URL", registry.registry_to_announce, 1);
+
+ registry.max_url_length = config_get_number("registry", "max URL length", 1024);
+ if(registry.max_url_length < 10) {
+ registry.max_url_length = 10;
+ config_set_number("registry", "max URL length", registry.max_url_length);
+ }
+
+ registry.max_name_length = config_get_number("registry", "max URL name length", 50);
+ if(registry.max_name_length < 10) {
+ registry.max_name_length = 10;
+ config_set_number("registry", "max URL name length", registry.max_name_length);
+ }
+
+ // initialize entries counters
+ registry.persons_count = 0;
+ registry.machines_count = 0;
+ registry.usages_count = 0;
+ registry.urls_count = 0;
+ registry.persons_urls_count = 0;
+ registry.machines_urls_count = 0;
+
+ // initialize memory counters
+ registry.persons_memory = 0;
+ registry.machines_memory = 0;
+ registry.urls_memory = 0;
+ registry.persons_urls_memory = 0;
+ registry.machines_urls_memory = 0;
+
+ // initialize locks
+ pthread_mutex_init(&registry.persons_lock, NULL);
+ pthread_mutex_init(&registry.machines_lock, NULL);
+ pthread_mutex_init(&registry.urls_lock, NULL);
+ pthread_mutex_init(&registry.person_urls_lock, NULL);
+ pthread_mutex_init(&registry.machine_urls_lock, NULL);
+
+ // create dictionaries
+ registry.persons = dictionary_create(DICTIONARY_FLAGS);
+ registry.machines = dictionary_create(DICTIONARY_FLAGS);
+ registry.urls = dictionary_create(DICTIONARY_FLAGS);
+
+ // load the registry database
+ if(registry.enabled) {
+ registry_log_open_nolock();
+ registry_load();
+ registry_log_load();
+ }
+
+ return 0;
}
void registry_free(void) {
- if(!registry.enabled) return;
+ if(!registry.enabled) return;
- // we need to destroy the dictionaries ourselves
- // since the dictionaries use memory we allocated
+ // we need to destroy the dictionaries ourselves
+ // since the dictionaries use memory we allocated
- while(registry.persons->values_index.root) {
- PERSON *p = ((NAME_VALUE *)registry.persons->values_index.root)->value;
+ while(registry.persons->values_index.root) {
+ PERSON *p = ((NAME_VALUE *)registry.persons->values_index.root)->value;
- // fprintf(stderr, "\nPERSON: '%s', first: %u, last: %u, usages: %u\n", p->guid, p->first_t, p->last_t, p->usages);
+ // fprintf(stderr, "\nPERSON: '%s', first: %u, last: %u, usages: %u\n", p->guid, p->first_t, p->last_t, p->usages);
- while(p->urls->values_index.root) {
- PERSON_URL *pu = ((NAME_VALUE *)p->urls->values_index.root)->value;
+ while(p->urls->values_index.root) {
+ PERSON_URL *pu = ((NAME_VALUE *)p->urls->values_index.root)->value;
- // fprintf(stderr, "\tURL: '%s', first: %u, last: %u, usages: %u, flags: 0x%02x\n", pu->url->url, pu->first_t, pu->last_t, pu->usages, pu->flags);
+ // fprintf(stderr, "\tURL: '%s', first: %u, last: %u, usages: %u, flags: 0x%02x\n", pu->url->url, pu->first_t, pu->last_t, pu->usages, pu->flags);
- debug(D_REGISTRY, "Registry: deleting url '%s' from person '%s'", pu->url->url, p->guid);
- dictionary_del(p->urls, pu->url->url);
+ debug(D_REGISTRY, "Registry: deleting url '%s' from person '%s'", pu->url->url, p->guid);
+ dictionary_del(p->urls, pu->url->url);
- debug(D_REGISTRY, "Registry: unlinking url '%s' from person", pu->url->url);
- registry_url_unlink_nolock(pu->url);
+ debug(D_REGISTRY, "Registry: unlinking url '%s' from person", pu->url->url);
+ registry_url_unlink_nolock(pu->url);
- debug(D_REGISTRY, "Registry: freeing person url");
- free(pu);
- }
+ debug(D_REGISTRY, "Registry: freeing person url");
+ freez(pu);
+ }
- debug(D_REGISTRY, "Registry: deleting person '%s' from persons registry", p->guid);
- dictionary_del(registry.persons, p->guid);
+ debug(D_REGISTRY, "Registry: deleting person '%s' from persons registry", p->guid);
+ dictionary_del(registry.persons, p->guid);
- debug(D_REGISTRY, "Registry: destroying URL dictionary of person '%s'", p->guid);
- dictionary_destroy(p->urls);
+ debug(D_REGISTRY, "Registry: destroying URL dictionary of person '%s'", p->guid);
+ dictionary_destroy(p->urls);
- debug(D_REGISTRY, "Registry: freeing person '%s'", p->guid);
- free(p);
- }
+ debug(D_REGISTRY, "Registry: freeing person '%s'", p->guid);
+ freez(p);
+ }
- while(registry.machines->values_index.root) {
- MACHINE *m = ((NAME_VALUE *)registry.machines->values_index.root)->value;
+ while(registry.machines->values_index.root) {
+ MACHINE *m = ((NAME_VALUE *)registry.machines->values_index.root)->value;
- // fprintf(stderr, "\nMACHINE: '%s', first: %u, last: %u, usages: %u\n", m->guid, m->first_t, m->last_t, m->usages);
+ // fprintf(stderr, "\nMACHINE: '%s', first: %u, last: %u, usages: %u\n", m->guid, m->first_t, m->last_t, m->usages);
- while(m->urls->values_index.root) {
- MACHINE_URL *mu = ((NAME_VALUE *)m->urls->values_index.root)->value;
+ while(m->urls->values_index.root) {
+ MACHINE_URL *mu = ((NAME_VALUE *)m->urls->values_index.root)->value;
- // fprintf(stderr, "\tURL: '%s', first: %u, last: %u, usages: %u, flags: 0x%02x\n", mu->url->url, mu->first_t, mu->last_t, mu->usages, mu->flags);
+ // fprintf(stderr, "\tURL: '%s', first: %u, last: %u, usages: %u, flags: 0x%02x\n", mu->url->url, mu->first_t, mu->last_t, mu->usages, mu->flags);
- //debug(D_REGISTRY, "Registry: destroying persons dictionary from url '%s'", mu->url->url);
- //dictionary_destroy(mu->persons);
+ //debug(D_REGISTRY, "Registry: destroying persons dictionary from url '%s'", mu->url->url);
+ //dictionary_destroy(mu->persons);
- debug(D_REGISTRY, "Registry: deleting url '%s' from person '%s'", mu->url->url, m->guid);
- dictionary_del(m->urls, mu->url->url);
+ debug(D_REGISTRY, "Registry: deleting url '%s' from person '%s'", mu->url->url, m->guid);
+ dictionary_del(m->urls, mu->url->url);
- debug(D_REGISTRY, "Registry: unlinking url '%s' from machine", mu->url->url);
- registry_url_unlink_nolock(mu->url);
+ debug(D_REGISTRY, "Registry: unlinking url '%s' from machine", mu->url->url);
+ registry_url_unlink_nolock(mu->url);
- debug(D_REGISTRY, "Registry: freeing machine url");
- free(mu);
- }
+ debug(D_REGISTRY, "Registry: freeing machine url");
+ freez(mu);
+ }
- debug(D_REGISTRY, "Registry: deleting machine '%s' from machines registry", m->guid);
- dictionary_del(registry.machines, m->guid);
+ debug(D_REGISTRY, "Registry: deleting machine '%s' from machines registry", m->guid);
+ dictionary_del(registry.machines, m->guid);
- debug(D_REGISTRY, "Registry: destroying URL dictionary of machine '%s'", m->guid);
- dictionary_destroy(m->urls);
+ debug(D_REGISTRY, "Registry: destroying URL dictionary of machine '%s'", m->guid);
+ dictionary_destroy(m->urls);
- debug(D_REGISTRY, "Registry: freeing machine '%s'", m->guid);
- free(m);
- }
+ debug(D_REGISTRY, "Registry: freeing machine '%s'", m->guid);
+ freez(m);
+ }
- // and free the memory of remaining dictionary structures
+ // and free the memory of remaining dictionary structures
- debug(D_REGISTRY, "Registry: destroying persons dictionary");
- dictionary_destroy(registry.persons);
+ debug(D_REGISTRY, "Registry: destroying persons dictionary");
+ dictionary_destroy(registry.persons);
- debug(D_REGISTRY, "Registry: destroying machines dictionary");
- dictionary_destroy(registry.machines);
+ debug(D_REGISTRY, "Registry: destroying machines dictionary");
+ dictionary_destroy(registry.machines);
- debug(D_REGISTRY, "Registry: destroying urls dictionary");
- dictionary_destroy(registry.urls);
+ debug(D_REGISTRY, "Registry: destroying urls dictionary");
+ dictionary_destroy(registry.urls);
}
// ----------------------------------------------------------------------------
// STATISTICS
void registry_statistics(void) {
- if(!registry.enabled) return;
-
- static RRDSET *sts = NULL, *stc = NULL, *stm = NULL;
-
- if(!sts) sts = rrdset_find("netdata.registry_sessions");
- if(!sts) {
- sts = rrdset_create("netdata", "registry_sessions", NULL, "registry", NULL, "NetData Registry Sessions", "session", 131000, rrd_update_every, RRDSET_TYPE_LINE);
-
- rrddim_add(sts, "sessions", NULL, 1, 1, RRDDIM_ABSOLUTE);
- }
- else rrdset_next(sts);
-
- rrddim_set(sts, "sessions", registry.usages_count);
- rrdset_done(sts);
-
- // ------------------------------------------------------------------------
-
- if(!stc) stc = rrdset_find("netdata.registry_entries");
- if(!stc) {
- stc = rrdset_create("netdata", "registry_entries", NULL, "registry", NULL, "NetData Registry Entries", "entries", 131100, rrd_update_every, RRDSET_TYPE_LINE);
-
- rrddim_add(stc, "persons", NULL, 1, 1, RRDDIM_ABSOLUTE);
- rrddim_add(stc, "machines", NULL, 1, 1, RRDDIM_ABSOLUTE);
- rrddim_add(stc, "urls", NULL, 1, 1, RRDDIM_ABSOLUTE);
- rrddim_add(stc, "persons_urls", NULL, 1, 1, RRDDIM_ABSOLUTE);
- rrddim_add(stc, "machines_urls", NULL, 1, 1, RRDDIM_ABSOLUTE);
- }
- else rrdset_next(stc);
-
- rrddim_set(stc, "persons", registry.persons_count);
- rrddim_set(stc, "machines", registry.machines_count);
- rrddim_set(stc, "urls", registry.urls_count);
- rrddim_set(stc, "persons_urls", registry.persons_urls_count);
- rrddim_set(stc, "machines_urls", registry.machines_urls_count);
- rrdset_done(stc);
-
- // ------------------------------------------------------------------------
-
- if(!stm) stm = rrdset_find("netdata.registry_mem");
- if(!stm) {
- stm = rrdset_create("netdata", "registry_mem", NULL, "registry", NULL, "NetData Registry Memory", "KB", 131300, rrd_update_every, RRDSET_TYPE_STACKED);
-
- rrddim_add(stm, "persons", NULL, 1, 1024, RRDDIM_ABSOLUTE);
- rrddim_add(stm, "machines", NULL, 1, 1024, RRDDIM_ABSOLUTE);
- rrddim_add(stm, "urls", NULL, 1, 1024, RRDDIM_ABSOLUTE);
- rrddim_add(stm, "persons_urls", NULL, 1, 1024, RRDDIM_ABSOLUTE);
- rrddim_add(stm, "machines_urls", NULL, 1, 1024, RRDDIM_ABSOLUTE);
- }
- else rrdset_next(stm);
-
- rrddim_set(stm, "persons", registry.persons_memory + registry.persons_count * sizeof(NAME_VALUE) + sizeof(DICTIONARY));
- rrddim_set(stm, "machines", registry.machines_memory + registry.machines_count * sizeof(NAME_VALUE) + sizeof(DICTIONARY));
- rrddim_set(stm, "urls", registry.urls_memory + registry.urls_count * sizeof(NAME_VALUE) + sizeof(DICTIONARY));
- rrddim_set(stm, "persons_urls", registry.persons_urls_memory + registry.persons_count * sizeof(DICTIONARY) + registry.persons_urls_count * sizeof(NAME_VALUE));
- rrddim_set(stm, "machines_urls", registry.machines_urls_memory + registry.machines_count * sizeof(DICTIONARY) + registry.machines_urls_count * sizeof(NAME_VALUE));
- rrdset_done(stm);
+ if(!registry.enabled) return;
+
+ static RRDSET *sts = NULL, *stc = NULL, *stm = NULL;
+
+ if(!sts) sts = rrdset_find("netdata.registry_sessions");
+ if(!sts) {
+ sts = rrdset_create("netdata", "registry_sessions", NULL, "registry", NULL, "NetData Registry Sessions", "session", 131000, rrd_update_every, RRDSET_TYPE_LINE);
+
+ rrddim_add(sts, "sessions", NULL, 1, 1, RRDDIM_ABSOLUTE);
+ }
+ else rrdset_next(sts);
+
+ rrddim_set(sts, "sessions", registry.usages_count);
+ rrdset_done(sts);
+
+ // ------------------------------------------------------------------------
+
+ if(!stc) stc = rrdset_find("netdata.registry_entries");
+ if(!stc) {
+ stc = rrdset_create("netdata", "registry_entries", NULL, "registry", NULL, "NetData Registry Entries", "entries", 131100, rrd_update_every, RRDSET_TYPE_LINE);
+
+ rrddim_add(stc, "persons", NULL, 1, 1, RRDDIM_ABSOLUTE);
+ rrddim_add(stc, "machines", NULL, 1, 1, RRDDIM_ABSOLUTE);
+ rrddim_add(stc, "urls", NULL, 1, 1, RRDDIM_ABSOLUTE);
+ rrddim_add(stc, "persons_urls", NULL, 1, 1, RRDDIM_ABSOLUTE);
+ rrddim_add(stc, "machines_urls", NULL, 1, 1, RRDDIM_ABSOLUTE);
+ }
+ else rrdset_next(stc);
+
+ rrddim_set(stc, "persons", registry.persons_count);
+ rrddim_set(stc, "machines", registry.machines_count);
+ rrddim_set(stc, "urls", registry.urls_count);
+ rrddim_set(stc, "persons_urls", registry.persons_urls_count);
+ rrddim_set(stc, "machines_urls", registry.machines_urls_count);
+ rrdset_done(stc);
+
+ // ------------------------------------------------------------------------
+
+ if(!stm) stm = rrdset_find("netdata.registry_mem");
+ if(!stm) {
+ stm = rrdset_create("netdata", "registry_mem", NULL, "registry", NULL, "NetData Registry Memory", "KB", 131300, rrd_update_every, RRDSET_TYPE_STACKED);
+
+ rrddim_add(stm, "persons", NULL, 1, 1024, RRDDIM_ABSOLUTE);
+ rrddim_add(stm, "machines", NULL, 1, 1024, RRDDIM_ABSOLUTE);
+ rrddim_add(stm, "urls", NULL, 1, 1024, RRDDIM_ABSOLUTE);
+ rrddim_add(stm, "persons_urls", NULL, 1, 1024, RRDDIM_ABSOLUTE);
+ rrddim_add(stm, "machines_urls", NULL, 1, 1024, RRDDIM_ABSOLUTE);
+ }
+ else rrdset_next(stm);
+
+ rrddim_set(stm, "persons", registry.persons_memory + registry.persons_count * sizeof(NAME_VALUE) + sizeof(DICTIONARY));
+ rrddim_set(stm, "machines", registry.machines_memory + registry.machines_count * sizeof(NAME_VALUE) + sizeof(DICTIONARY));
+ rrddim_set(stm, "urls", registry.urls_memory + registry.urls_count * sizeof(NAME_VALUE) + sizeof(DICTIONARY));
+ rrddim_set(stm, "persons_urls", registry.persons_urls_memory + registry.persons_count * sizeof(DICTIONARY) + registry.persons_urls_count * sizeof(NAME_VALUE));
+ rrddim_set(stm, "machines_urls", registry.machines_urls_memory + registry.machines_count * sizeof(DICTIONARY) + registry.machines_urls_count * sizeof(NAME_VALUE));
+ rrdset_done(stm);
}
diff --git a/src/registry.h b/src/registry.h
index d95383b5d..c2b57a23d 100644
--- a/src/registry.h
+++ b/src/registry.h
@@ -1,10 +1,12 @@
-#include "web_client.h"
-
#ifndef NETDATA_REGISTRY_H
#define NETDATA_REGISTRY_H 1
#define NETDATA_REGISTRY_COOKIE_NAME "netdata_registry_id"
+extern void registry_set_cookie(struct web_client *w, const char *guid);
+extern const char *registry_to_announce(void);
+extern int registry_verify_cookies_redirects(void);
+
extern int registry_request_access_json(struct web_client *w, char *person_guid, char *machine_guid, char *url, char *name, time_t when);
extern int registry_request_delete_json(struct web_client *w, char *person_guid, char *machine_guid, char *url, char *delete_url, time_t when);
extern int registry_request_search_json(struct web_client *w, char *person_guid, char *machine_guid, char *url, char *request_machine, time_t when);
diff --git a/src/rrd.c b/src/rrd.c
index ee23da0c2..61f99eda8 100644
--- a/src/rrd.c
+++ b/src/rrd.c
@@ -1,24 +1,4 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stddef.h>
-#include <string.h>
-#include <unistd.h>
-#include <time.h>
-#include <sys/time.h>
-#include <sys/mman.h>
-#include <pthread.h>
-#include <errno.h>
-#include <ctype.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <stdlib.h>
-
#include "common.h"
-#include "log.h"
-#include "appconfig.h"
-
-#include "rrd.h"
#define RRD_DEFAULT_GAP_INTERPOLATIONS 1
@@ -32,36 +12,143 @@ int rrd_delete_unupdated_dimensions = 0;
int rrd_update_every = UPDATE_EVERY;
int rrd_default_history_entries = RRD_DEFAULT_HISTORY_ENTRIES;
+int rrd_memory_mode = RRD_MEMORY_MODE_SAVE;
-RRDSET *rrdset_root = NULL;
-pthread_rwlock_t rrdset_root_rwlock = PTHREAD_RWLOCK_INITIALIZER;
+static int rrdset_compare(void* a, void* b);
+static int rrdset_compare_name(void* a, void* b);
+static int rrdfamily_compare(void *a, void *b);
-int rrd_memory_mode = RRD_MEMORY_MODE_SAVE;
+// ----------------------------------------------------------------------------
+// RRDHOST
+
+RRDHOST localhost = {
+ .hostname = "localhost",
+ .rrdset_root = NULL,
+ .rrdset_root_rwlock = PTHREAD_RWLOCK_INITIALIZER,
+ .rrdset_root_index = {
+ { NULL, rrdset_compare },
+ AVL_LOCK_INITIALIZER
+ },
+ .rrdset_root_index_name = {
+ { NULL, rrdset_compare_name },
+ AVL_LOCK_INITIALIZER
+ },
+ .rrdfamily_root_index = {
+ { NULL, rrdfamily_compare },
+ AVL_LOCK_INITIALIZER
+ },
+ .variables_root_index = {
+ { NULL, rrdvar_compare },
+ AVL_LOCK_INITIALIZER
+ },
+ .health_log = {
+ .nextid = 1,
+ .count = 0,
+ .max = 1000,
+ .alarms = NULL,
+ .alarm_log_rwlock = PTHREAD_RWLOCK_INITIALIZER
+ }
+};
+
+void rrdhost_rwlock(RRDHOST *host) {
+ pthread_rwlock_wrlock(&host->rrdset_root_rwlock);
+}
+
+void rrdhost_rdlock(RRDHOST *host) {
+ pthread_rwlock_rdlock(&host->rrdset_root_rwlock);
+}
+
+void rrdhost_unlock(RRDHOST *host) {
+ pthread_rwlock_unlock(&host->rrdset_root_rwlock);
+}
+
+void rrdhost_check_rdlock_int(RRDHOST *host, const char *file, const char *function, const unsigned long line) {
+ int ret = pthread_rwlock_trywrlock(&host->rrdset_root_rwlock);
+
+ if(ret == 0)
+ fatal("RRDHOST '%s' should be read-locked, but it is not, at function %s() at line %lu of file '%s'", host->hostname, function, line, file);
+}
+
+void rrdhost_check_wrlock_int(RRDHOST *host, const char *file, const char *function, const unsigned long line) {
+ int ret = pthread_rwlock_tryrdlock(&host->rrdset_root_rwlock);
+
+ if(ret == 0)
+ fatal("RRDHOST '%s' should be write-locked, but it is not, at function %s() at line %lu of file '%s'", host->hostname, function, line, file);
+}
+
+// ----------------------------------------------------------------------------
+// RRDFAMILY index
+
+static int rrdfamily_compare(void *a, void *b) {
+ if(((RRDFAMILY *)a)->hash_family < ((RRDFAMILY *)b)->hash_family) return -1;
+ else if(((RRDFAMILY *)a)->hash_family > ((RRDFAMILY *)b)->hash_family) return 1;
+ else return strcmp(((RRDFAMILY *)a)->family, ((RRDFAMILY *)b)->family);
+}
+
+#define rrdfamily_index_add(host, rc) (RRDFAMILY *)avl_insert_lock(&((host)->rrdfamily_root_index), (avl *)(rc))
+#define rrdfamily_index_del(host, rc) (RRDFAMILY *)avl_remove_lock(&((host)->rrdfamily_root_index), (avl *)(rc))
+
+static RRDFAMILY *rrdfamily_index_find(RRDHOST *host, const char *id, uint32_t hash) {
+ RRDFAMILY tmp;
+ tmp.family = id;
+ tmp.hash_family = (hash)?hash:simple_hash(tmp.family);
+
+ return (RRDFAMILY *)avl_search_lock(&(host->rrdfamily_root_index), (avl *) &tmp);
+}
+
+RRDFAMILY *rrdfamily_create(const char *id) {
+ RRDFAMILY *rc = rrdfamily_index_find(&localhost, id, 0);
+ if(!rc) {
+ rc = callocz(1, sizeof(RRDFAMILY));
+
+ rc->family = strdupz(id);
+ rc->hash_family = simple_hash(rc->family);
+
+ // initialize the variables index
+ avl_init_lock(&rc->variables_root_index, rrdvar_compare);
+ RRDFAMILY *ret = rrdfamily_index_add(&localhost, rc);
+ if(ret != rc)
+ fatal("INTERNAL ERROR: Expected to INSERT RRDFAMILY '%s' into index, but inserted '%s'.", rc->family, (ret)?ret->family:"NONE");
+ }
+
+ rc->use_count++;
+ return rc;
+}
+
+void rrdfamily_free(RRDFAMILY *rc) {
+ rc->use_count--;
+ if(!rc->use_count) {
+ RRDFAMILY *ret = rrdfamily_index_del(&localhost, rc);
+ if(ret != rc)
+ fatal("INTERNAL ERROR: Expected to DELETE RRDFAMILY '%s' from index, but deleted '%s'.", rc->family, (ret)?ret->family:"NONE");
+
+ if(rc->variables_root_index.avl_tree.root != NULL)
+ fatal("INTERNAL ERROR: Variables index of RRDFAMILY '%s' that is freed, is not empty.", rc->family);
+
+ freez((void *)rc->family);
+ freez(rc);
+ }
+}
// ----------------------------------------------------------------------------
// RRDSET index
static int rrdset_compare(void* a, void* b) {
- if(((RRDSET *)a)->hash < ((RRDSET *)b)->hash) return -1;
- else if(((RRDSET *)a)->hash > ((RRDSET *)b)->hash) return 1;
- else return strcmp(((RRDSET *)a)->id, ((RRDSET *)b)->id);
+ if(((RRDSET *)a)->hash < ((RRDSET *)b)->hash) return -1;
+ else if(((RRDSET *)a)->hash > ((RRDSET *)b)->hash) return 1;
+ else return strcmp(((RRDSET *)a)->id, ((RRDSET *)b)->id);
}
-avl_tree_lock rrdset_root_index = {
- { NULL, rrdset_compare },
- AVL_LOCK_INITIALIZER
-};
-
-#define rrdset_index_add(st) avl_insert_lock(&rrdset_root_index, (avl *)(st))
-#define rrdset_index_del(st) avl_remove_lock(&rrdset_root_index, (avl *)(st))
+#define rrdset_index_add(host, st) (RRDSET *)avl_insert_lock(&((host)->rrdset_root_index), (avl *)(st))
+#define rrdset_index_del(host, st) (RRDSET *)avl_remove_lock(&((host)->rrdset_root_index), (avl *)(st))
-static RRDSET *rrdset_index_find(const char *id, uint32_t hash) {
- RRDSET tmp;
- strncpyz(tmp.id, id, RRD_ID_LENGTH_MAX);
- tmp.hash = (hash)?hash:simple_hash(tmp.id);
+static RRDSET *rrdset_index_find(RRDHOST *host, const char *id, uint32_t hash) {
+ RRDSET tmp;
+ strncpyz(tmp.id, id, RRD_ID_LENGTH_MAX);
+ tmp.hash = (hash)?hash:simple_hash(tmp.id);
- return (RRDSET *)avl_search_lock(&(rrdset_root_index), (avl *) &tmp);
+ return (RRDSET *)avl_search_lock(&(host->rrdset_root_index), (avl *) &tmp);
}
// ----------------------------------------------------------------------------
@@ -70,46 +157,50 @@ static RRDSET *rrdset_index_find(const char *id, uint32_t hash) {
#define rrdset_from_avlname(avlname_ptr) ((RRDSET *)((avlname_ptr) - offsetof(RRDSET, avlname)))
static int rrdset_compare_name(void* a, void* b) {
- RRDSET *A = rrdset_from_avlname(a);
- RRDSET *B = rrdset_from_avlname(b);
+ RRDSET *A = rrdset_from_avlname(a);
+ RRDSET *B = rrdset_from_avlname(b);
- // fprintf(stderr, "COMPARING: %s with %s\n", A->name, B->name);
+ // fprintf(stderr, "COMPARING: %s with %s\n", A->name, B->name);
- if(A->hash_name < B->hash_name) return -1;
- else if(A->hash_name > B->hash_name) return 1;
- else return strcmp(A->name, B->name);
+ if(A->hash_name < B->hash_name) return -1;
+ else if(A->hash_name > B->hash_name) return 1;
+ else return strcmp(A->name, B->name);
}
-avl_tree_lock rrdset_root_index_name = {
- { NULL, rrdset_compare_name },
- AVL_LOCK_INITIALIZER
-};
+RRDSET *rrdset_index_add_name(RRDHOST *host, RRDSET *st) {
+ void *result;
+ // fprintf(stderr, "ADDING: %s (name: %s)\n", st->id, st->name);
+ result = avl_insert_lock(&host->rrdset_root_index_name, (avl *) (&st->avlname));
+ if(result) return rrdset_from_avlname(result);
+ return NULL;
+}
-int rrdset_index_add_name(RRDSET *st) {
- // fprintf(stderr, "ADDING: %s (name: %s)\n", st->id, st->name);
- return avl_insert_lock(&rrdset_root_index_name, (avl *) (&st->avlname));
+RRDSET *rrdset_index_del_name(RRDHOST *host, RRDSET *st) {
+ void *result;
+ // fprintf(stderr, "DELETING: %s (name: %s)\n", st->id, st->name);
+ result = (RRDSET *)avl_remove_lock(&((host)->rrdset_root_index_name), (avl *)(&st->avlname));
+ if(result) return rrdset_from_avlname(result);
+ return NULL;
}
-#define rrdset_index_del_name(st) avl_remove_lock(&rrdset_root_index_name, (avl *)(&st->avlname))
-
-static RRDSET *rrdset_index_find_name(const char *name, uint32_t hash) {
- void *result = NULL;
- RRDSET tmp;
- tmp.name = name;
- tmp.hash_name = (hash)?hash:simple_hash(tmp.name);
-
- // fprintf(stderr, "SEARCHING: %s\n", name);
- result = avl_search_lock(&(rrdset_root_index_name), (avl *) (&(tmp.avlname)));
- if(result) {
- RRDSET *st = rrdset_from_avlname(result);
- if(strcmp(st->magic, RRDSET_MAGIC))
- error("Search for RRDSET %s returned an invalid RRDSET %s (name %s)", name, st->id, st->name);
-
- // fprintf(stderr, "FOUND: %s\n", name);
- return rrdset_from_avlname(result);
- }
- // fprintf(stderr, "NOT FOUND: %s\n", name);
- return NULL;
+static RRDSET *rrdset_index_find_name(RRDHOST *host, const char *name, uint32_t hash) {
+ void *result = NULL;
+ RRDSET tmp;
+ tmp.name = name;
+ tmp.hash_name = (hash)?hash:simple_hash(tmp.name);
+
+ // fprintf(stderr, "SEARCHING: %s\n", name);
+ result = avl_search_lock(&host->rrdset_root_index_name, (avl *) (&(tmp.avlname)));
+ if(result) {
+ RRDSET *st = rrdset_from_avlname(result);
+ if(strcmp(st->magic, RRDSET_MAGIC))
+ error("Search for RRDSET %s returned an invalid RRDSET %s (name %s)", name, st->id, st->name);
+
+ // fprintf(stderr, "FOUND: %s\n", name);
+ return rrdset_from_avlname(result);
+ }
+ // fprintf(stderr, "NOT FOUND: %s\n", name);
+ return NULL;
}
@@ -117,20 +208,20 @@ static RRDSET *rrdset_index_find_name(const char *name, uint32_t hash) {
// RRDDIM index
static int rrddim_compare(void* a, void* b) {
- if(((RRDDIM *)a)->hash < ((RRDDIM *)b)->hash) return -1;
- else if(((RRDDIM *)a)->hash > ((RRDDIM *)b)->hash) return 1;
- else return strcmp(((RRDDIM *)a)->id, ((RRDDIM *)b)->id);
+ if(((RRDDIM *)a)->hash < ((RRDDIM *)b)->hash) return -1;
+ else if(((RRDDIM *)a)->hash > ((RRDDIM *)b)->hash) return 1;
+ else return strcmp(((RRDDIM *)a)->id, ((RRDDIM *)b)->id);
}
#define rrddim_index_add(st, rd) avl_insert_lock(&((st)->dimensions_index), (avl *)(rd))
#define rrddim_index_del(st,rd ) avl_remove_lock(&((st)->dimensions_index), (avl *)(rd))
static RRDDIM *rrddim_index_find(RRDSET *st, const char *id, uint32_t hash) {
- RRDDIM tmp;
- strncpyz(tmp.id, id, RRD_ID_LENGTH_MAX);
- tmp.hash = (hash)?hash:simple_hash(tmp.id);
+ RRDDIM tmp;
+ strncpyz(tmp.id, id, RRD_ID_LENGTH_MAX);
+ tmp.hash = (hash)?hash:simple_hash(tmp.id);
- return (RRDDIM *)avl_search_lock(&(st->dimensions_index), (avl *) &tmp);
+ return (RRDDIM *)avl_search_lock(&(st->dimensions_index), (avl *) &tmp);
}
// ----------------------------------------------------------------------------
@@ -138,29 +229,29 @@ static RRDDIM *rrddim_index_find(RRDSET *st, const char *id, uint32_t hash) {
int rrdset_type_id(const char *name)
{
- if(unlikely(strcmp(name, RRDSET_TYPE_AREA_NAME) == 0)) return RRDSET_TYPE_AREA;
- else if(unlikely(strcmp(name, RRDSET_TYPE_STACKED_NAME) == 0)) return RRDSET_TYPE_STACKED;
- else if(unlikely(strcmp(name, RRDSET_TYPE_LINE_NAME) == 0)) return RRDSET_TYPE_LINE;
- return RRDSET_TYPE_LINE;
+ if(unlikely(strcmp(name, RRDSET_TYPE_AREA_NAME) == 0)) return RRDSET_TYPE_AREA;
+ else if(unlikely(strcmp(name, RRDSET_TYPE_STACKED_NAME) == 0)) return RRDSET_TYPE_STACKED;
+ else if(unlikely(strcmp(name, RRDSET_TYPE_LINE_NAME) == 0)) return RRDSET_TYPE_LINE;
+ return RRDSET_TYPE_LINE;
}
const char *rrdset_type_name(int chart_type)
{
- static char line[] = RRDSET_TYPE_LINE_NAME;
- static char area[] = RRDSET_TYPE_AREA_NAME;
- static char stacked[] = RRDSET_TYPE_STACKED_NAME;
+ static char line[] = RRDSET_TYPE_LINE_NAME;
+ static char area[] = RRDSET_TYPE_AREA_NAME;
+ static char stacked[] = RRDSET_TYPE_STACKED_NAME;
- switch(chart_type) {
- case RRDSET_TYPE_LINE:
- return line;
+ switch(chart_type) {
+ case RRDSET_TYPE_LINE:
+ return line;
- case RRDSET_TYPE_AREA:
- return area;
+ case RRDSET_TYPE_AREA:
+ return area;
- case RRDSET_TYPE_STACKED:
- return stacked;
- }
- return line;
+ case RRDSET_TYPE_STACKED:
+ return stacked;
+ }
+ return line;
}
// ----------------------------------------------------------------------------
@@ -168,33 +259,33 @@ const char *rrdset_type_name(int chart_type)
const char *rrd_memory_mode_name(int id)
{
- static const char ram[] = RRD_MEMORY_MODE_RAM_NAME;
- static const char map[] = RRD_MEMORY_MODE_MAP_NAME;
- static const char save[] = RRD_MEMORY_MODE_SAVE_NAME;
+ static const char ram[] = RRD_MEMORY_MODE_RAM_NAME;
+ static const char map[] = RRD_MEMORY_MODE_MAP_NAME;
+ static const char save[] = RRD_MEMORY_MODE_SAVE_NAME;
- switch(id) {
- case RRD_MEMORY_MODE_RAM:
- return ram;
+ switch(id) {
+ case RRD_MEMORY_MODE_RAM:
+ return ram;
- case RRD_MEMORY_MODE_MAP:
- return map;
+ case RRD_MEMORY_MODE_MAP:
+ return map;
- case RRD_MEMORY_MODE_SAVE:
- default:
- return save;
- }
+ case RRD_MEMORY_MODE_SAVE:
+ default:
+ return save;
+ }
- return save;
+ return save;
}
int rrd_memory_mode_id(const char *name)
{
- if(unlikely(!strcmp(name, RRD_MEMORY_MODE_RAM_NAME)))
- return RRD_MEMORY_MODE_RAM;
- else if(unlikely(!strcmp(name, RRD_MEMORY_MODE_MAP_NAME)))
- return RRD_MEMORY_MODE_MAP;
+ if(unlikely(!strcmp(name, RRD_MEMORY_MODE_RAM_NAME)))
+ return RRD_MEMORY_MODE_RAM;
+ else if(unlikely(!strcmp(name, RRD_MEMORY_MODE_MAP_NAME)))
+ return RRD_MEMORY_MODE_MAP;
- return RRD_MEMORY_MODE_SAVE;
+ return RRD_MEMORY_MODE_SAVE;
}
// ----------------------------------------------------------------------------
@@ -202,67 +293,73 @@ int rrd_memory_mode_id(const char *name)
int rrddim_algorithm_id(const char *name)
{
- if(strcmp(name, RRDDIM_INCREMENTAL_NAME) == 0) return RRDDIM_INCREMENTAL;
- if(strcmp(name, RRDDIM_ABSOLUTE_NAME) == 0) return RRDDIM_ABSOLUTE;
- if(strcmp(name, RRDDIM_PCENT_OVER_ROW_TOTAL_NAME) == 0) return RRDDIM_PCENT_OVER_ROW_TOTAL;
- if(strcmp(name, RRDDIM_PCENT_OVER_DIFF_TOTAL_NAME) == 0) return RRDDIM_PCENT_OVER_DIFF_TOTAL;
- return RRDDIM_ABSOLUTE;
+ if(strcmp(name, RRDDIM_INCREMENTAL_NAME) == 0) return RRDDIM_INCREMENTAL;
+ if(strcmp(name, RRDDIM_ABSOLUTE_NAME) == 0) return RRDDIM_ABSOLUTE;
+ if(strcmp(name, RRDDIM_PCENT_OVER_ROW_TOTAL_NAME) == 0) return RRDDIM_PCENT_OVER_ROW_TOTAL;
+ if(strcmp(name, RRDDIM_PCENT_OVER_DIFF_TOTAL_NAME) == 0) return RRDDIM_PCENT_OVER_DIFF_TOTAL;
+ return RRDDIM_ABSOLUTE;
}
const char *rrddim_algorithm_name(int chart_type)
{
- static char absolute[] = RRDDIM_ABSOLUTE_NAME;
- static char incremental[] = RRDDIM_INCREMENTAL_NAME;
- static char percentage_of_absolute_row[] = RRDDIM_PCENT_OVER_ROW_TOTAL_NAME;
- static char percentage_of_incremental_row[] = RRDDIM_PCENT_OVER_DIFF_TOTAL_NAME;
+ static char absolute[] = RRDDIM_ABSOLUTE_NAME;
+ static char incremental[] = RRDDIM_INCREMENTAL_NAME;
+ static char percentage_of_absolute_row[] = RRDDIM_PCENT_OVER_ROW_TOTAL_NAME;
+ static char percentage_of_incremental_row[] = RRDDIM_PCENT_OVER_DIFF_TOTAL_NAME;
- switch(chart_type) {
- case RRDDIM_ABSOLUTE:
- return absolute;
+ switch(chart_type) {
+ case RRDDIM_ABSOLUTE:
+ return absolute;
- case RRDDIM_INCREMENTAL:
- return incremental;
+ case RRDDIM_INCREMENTAL:
+ return incremental;
- case RRDDIM_PCENT_OVER_ROW_TOTAL:
- return percentage_of_absolute_row;
+ case RRDDIM_PCENT_OVER_ROW_TOTAL:
+ return percentage_of_absolute_row;
- case RRDDIM_PCENT_OVER_DIFF_TOTAL:
- return percentage_of_incremental_row;
- }
- return absolute;
+ case RRDDIM_PCENT_OVER_DIFF_TOTAL:
+ return percentage_of_incremental_row;
+ }
+ return absolute;
}
// ----------------------------------------------------------------------------
// chart names
-char *rrdset_strncpy_name(char *to, const char *from, int length)
+char *rrdset_strncpyz_name(char *to, const char *from, size_t length)
{
- int i;
- for(i = 0; i < length && from[i] ;i++) {
- if(from[i] == '.' || isalpha(from[i]) || isdigit(from[i])) to[i] = from[i];
- else to[i] = '_';
- }
- if(i < length) to[i] = '\0';
- to[length - 1] = '\0';
-
- return to;
+ char c, *p = to;
+
+ while (length-- && (c = *from++)) {
+ if(c != '.' && !isalnum(c))
+ c = '_';
+
+ *p++ = c;
+ }
+
+ *p = '\0';
+
+ return to;
}
void rrdset_set_name(RRDSET *st, const char *name)
{
- debug(D_RRD_CALLS, "rrdset_set_name() old: %s, new: %s", st->name, name);
+ debug(D_RRD_CALLS, "rrdset_set_name() old: %s, new: %s", st->name, name);
- if(st->name) rrdset_index_del_name(st);
+ if(st->name) {
+ rrdset_index_del_name(&localhost, st);
+ rrdsetvar_rename_all(st);
+ }
- char b[CONFIG_MAX_VALUE + 1];
- char n[RRD_ID_LENGTH_MAX + 1];
+ char b[CONFIG_MAX_VALUE + 1];
+ char n[RRD_ID_LENGTH_MAX + 1];
- snprintfz(n, RRD_ID_LENGTH_MAX, "%s.%s", st->type, name);
- rrdset_strncpy_name(b, n, CONFIG_MAX_VALUE);
- st->name = config_get(st->id, "name", b);
- st->hash_name = simple_hash(st->name);
+ snprintfz(n, RRD_ID_LENGTH_MAX, "%s.%s", st->type, name);
+ rrdset_strncpyz_name(b, n, CONFIG_MAX_VALUE);
+ st->name = config_get(st->id, "name", b);
+ st->hash_name = simple_hash(st->name);
- rrdset_index_add_name(st);
+ rrdset_index_add_name(&localhost, st);
}
// ----------------------------------------------------------------------------
@@ -270,25 +367,30 @@ void rrdset_set_name(RRDSET *st, const char *name)
char *rrdset_cache_dir(const char *id)
{
- char *ret = NULL;
-
- static char *cache_dir = NULL;
- if(!cache_dir) cache_dir = config_get("global", "cache directory", CACHE_DIR);
-
- char b[FILENAME_MAX + 1];
- char n[FILENAME_MAX + 1];
- rrdset_strncpy_name(b, id, FILENAME_MAX);
-
- snprintfz(n, FILENAME_MAX, "%s/%s", cache_dir, b);
- ret = config_get(id, "cache directory", n);
-
- if(rrd_memory_mode == RRD_MEMORY_MODE_MAP || rrd_memory_mode == RRD_MEMORY_MODE_SAVE) {
- int r = mkdir(ret, 0775);
- if(r != 0 && errno != EEXIST)
- error("Cannot create directory '%s'", ret);
- }
-
- return ret;
+ char *ret = NULL;
+
+ static char *cache_dir = NULL;
+ if(!cache_dir) {
+ cache_dir = config_get("global", "cache directory", CACHE_DIR);
+ int r = mkdir(cache_dir, 0755);
+ if(r != 0 && errno != EEXIST)
+ error("Cannot create directory '%s'", cache_dir);
+ }
+
+ char b[FILENAME_MAX + 1];
+ char n[FILENAME_MAX + 1];
+ rrdset_strncpyz_name(b, id, FILENAME_MAX);
+
+ snprintfz(n, FILENAME_MAX, "%s/%s", cache_dir, b);
+ ret = config_get(id, "cache directory", n);
+
+ if(rrd_memory_mode == RRD_MEMORY_MODE_MAP || rrd_memory_mode == RRD_MEMORY_MODE_SAVE) {
+ int r = mkdir(ret, 0775);
+ if(r != 0 && errno != EEXIST)
+ error("Cannot create directory '%s'", ret);
+ }
+
+ return ret;
}
// ----------------------------------------------------------------------------
@@ -296,1025 +398,1110 @@ char *rrdset_cache_dir(const char *id)
void rrdset_reset(RRDSET *st)
{
- debug(D_RRD_CALLS, "rrdset_reset() %s", st->name);
-
- st->last_collected_time.tv_sec = 0;
- st->last_collected_time.tv_usec = 0;
- st->last_updated.tv_sec = 0;
- st->last_updated.tv_usec = 0;
- st->current_entry = 0;
- st->counter = 0;
- st->counter_done = 0;
-
- RRDDIM *rd;
- for(rd = st->dimensions; rd ; rd = rd->next) {
- rd->last_collected_time.tv_sec = 0;
- rd->last_collected_time.tv_usec = 0;
- rd->counter = 0;
- bzero(rd->values, rd->entries * sizeof(storage_number));
- }
+ debug(D_RRD_CALLS, "rrdset_reset() %s", st->name);
+
+ st->last_collected_time.tv_sec = 0;
+ st->last_collected_time.tv_usec = 0;
+ st->last_updated.tv_sec = 0;
+ st->last_updated.tv_usec = 0;
+ st->current_entry = 0;
+ st->counter = 0;
+ st->counter_done = 0;
+
+ RRDDIM *rd;
+ for(rd = st->dimensions; rd ; rd = rd->next) {
+ rd->last_collected_time.tv_sec = 0;
+ rd->last_collected_time.tv_usec = 0;
+ rd->counter = 0;
+ bzero(rd->values, rd->entries * sizeof(storage_number));
+ }
}
RRDSET *rrdset_create(const char *type, const char *id, const char *name, const char *family, const char *context, const char *title, const char *units, long priority, int update_every, int chart_type)
{
- if(!type || !type[0]) {
- fatal("Cannot create rrd stats without a type.");
- return NULL;
- }
-
- if(!id || !id[0]) {
- fatal("Cannot create rrd stats without an id.");
- return NULL;
- }
-
- char fullid[RRD_ID_LENGTH_MAX + 1];
- char fullfilename[FILENAME_MAX + 1];
- RRDSET *st = NULL;
-
- snprintfz(fullid, RRD_ID_LENGTH_MAX, "%s.%s", type, id);
-
- st = rrdset_find(fullid);
- if(st) {
- error("Cannot create rrd stats for '%s', it already exists.", fullid);
- return st;
- }
-
- long entries = config_get_number(fullid, "history", rrd_default_history_entries);
- if(entries < 5) entries = config_set_number(fullid, "history", 5);
- if(entries > RRD_HISTORY_ENTRIES_MAX) entries = config_set_number(fullid, "history", RRD_HISTORY_ENTRIES_MAX);
-
- int enabled = config_get_boolean(fullid, "enabled", 1);
- if(!enabled) entries = 5;
-
- unsigned long size = sizeof(RRDSET);
- char *cache_dir = rrdset_cache_dir(fullid);
-
- debug(D_RRD_CALLS, "Creating RRD_STATS for '%s.%s'.", type, id);
-
- snprintfz(fullfilename, FILENAME_MAX, "%s/main.db", cache_dir);
- if(rrd_memory_mode != RRD_MEMORY_MODE_RAM) st = (RRDSET *)mymmap(fullfilename, size, ((rrd_memory_mode == RRD_MEMORY_MODE_MAP)?MAP_SHARED:MAP_PRIVATE), 0);
- if(st) {
- if(strcmp(st->magic, RRDSET_MAGIC) != 0) {
- errno = 0;
- info("Initializing file %s.", fullfilename);
- bzero(st, size);
- }
- else if(strcmp(st->id, fullid) != 0) {
- errno = 0;
- error("File %s contents are not for chart %s. Clearing it.", fullfilename, fullid);
- // munmap(st, size);
- // st = NULL;
- bzero(st, size);
- }
- else if(st->memsize != size || st->entries != entries) {
- errno = 0;
- error("File %s does not have the desired size. Clearing it.", fullfilename);
- bzero(st, size);
- }
- else if(st->update_every != update_every) {
- errno = 0;
- error("File %s does not have the desired update frequency. Clearing it.", fullfilename);
- bzero(st, size);
- }
- else if((time(NULL) - st->last_updated.tv_sec) > update_every * entries) {
- errno = 0;
- error("File %s is too old. Clearing it.", fullfilename);
- bzero(st, size);
- }
- }
-
- if(st) {
- st->name = NULL;
- st->type = NULL;
- st->family = NULL;
- st->context = NULL;
- st->title = NULL;
- st->units = NULL;
- st->dimensions = NULL;
- st->next = NULL;
- st->mapped = rrd_memory_mode;
- }
- else {
- st = calloc(1, size);
- if(!st) {
- fatal("Cannot allocate memory for RRD_STATS %s.%s", type, id);
- return NULL;
- }
- st->mapped = RRD_MEMORY_MODE_RAM;
- }
- st->memsize = size;
- st->entries = entries;
- st->update_every = update_every;
-
- strcpy(st->cache_filename, fullfilename);
- strcpy(st->magic, RRDSET_MAGIC);
-
- strcpy(st->id, fullid);
- st->hash = simple_hash(st->id);
-
- st->cache_dir = cache_dir;
-
- st->chart_type = rrdset_type_id(config_get(st->id, "chart type", rrdset_type_name(chart_type)));
- st->type = config_get(st->id, "type", type);
- st->family = config_get(st->id, "family", family?family:st->type);
- st->context = config_get(st->id, "context", context?context:st->id);
- st->units = config_get(st->id, "units", units?units:"");
-
- st->priority = config_get_number(st->id, "priority", priority);
- st->enabled = enabled;
-
- st->isdetail = 0;
- st->debug = 0;
-
- st->last_collected_time.tv_sec = 0;
- st->last_collected_time.tv_usec = 0;
- st->counter_done = 0;
-
- st->gap_when_lost_iterations_above = (int) (
- config_get_number(st->id, "gap when lost iterations above", RRD_DEFAULT_GAP_INTERPOLATIONS) + 2);
-
- avl_init_lock(&st->dimensions_index, rrddim_compare);
-
- pthread_rwlock_init(&st->rwlock, NULL);
- pthread_rwlock_wrlock(&rrdset_root_rwlock);
-
- if(name && *name) rrdset_set_name(st, name);
- else rrdset_set_name(st, id);
-
- {
- char varvalue[CONFIG_MAX_VALUE + 1];
- snprintfz(varvalue, CONFIG_MAX_VALUE, "%s (%s)", title?title:"", st->name);
- st->title = config_get(st->id, "title", varvalue);
- }
-
- st->next = rrdset_root;
- rrdset_root = st;
-
- rrdset_index_add(st);
-
- pthread_rwlock_unlock(&rrdset_root_rwlock);
-
- return(st);
+ if(!type || !type[0]) {
+ fatal("Cannot create rrd stats without a type.");
+ return NULL;
+ }
+
+ if(!id || !id[0]) {
+ fatal("Cannot create rrd stats without an id.");
+ return NULL;
+ }
+
+ char fullid[RRD_ID_LENGTH_MAX + 1];
+ char fullfilename[FILENAME_MAX + 1];
+ RRDSET *st = NULL;
+
+ snprintfz(fullid, RRD_ID_LENGTH_MAX, "%s.%s", type, id);
+
+ st = rrdset_find(fullid);
+ if(st) {
+ error("Cannot create rrd stats for '%s', it already exists.", fullid);
+ return st;
+ }
+
+ long entries = config_get_number(fullid, "history", rrd_default_history_entries);
+ if(entries < 5) entries = config_set_number(fullid, "history", 5);
+ if(entries > RRD_HISTORY_ENTRIES_MAX) entries = config_set_number(fullid, "history", RRD_HISTORY_ENTRIES_MAX);
+
+ int enabled = config_get_boolean(fullid, "enabled", 1);
+ if(!enabled) entries = 5;
+
+ unsigned long size = sizeof(RRDSET);
+ char *cache_dir = rrdset_cache_dir(fullid);
+
+ debug(D_RRD_CALLS, "Creating RRD_STATS for '%s.%s'.", type, id);
+
+ snprintfz(fullfilename, FILENAME_MAX, "%s/main.db", cache_dir);
+ if(rrd_memory_mode != RRD_MEMORY_MODE_RAM) st = (RRDSET *)mymmap(fullfilename, size, ((rrd_memory_mode == RRD_MEMORY_MODE_MAP)?MAP_SHARED:MAP_PRIVATE), 0);
+ if(st) {
+ if(strcmp(st->magic, RRDSET_MAGIC) != 0) {
+ errno = 0;
+ info("Initializing file %s.", fullfilename);
+ bzero(st, size);
+ }
+ else if(strcmp(st->id, fullid) != 0) {
+ errno = 0;
+ error("File %s contents are not for chart %s. Clearing it.", fullfilename, fullid);
+ // munmap(st, size);
+ // st = NULL;
+ bzero(st, size);
+ }
+ else if(st->memsize != size || st->entries != entries) {
+ errno = 0;
+ error("File %s does not have the desired size. Clearing it.", fullfilename);
+ bzero(st, size);
+ }
+ else if(st->update_every != update_every) {
+ errno = 0;
+ error("File %s does not have the desired update frequency. Clearing it.", fullfilename);
+ bzero(st, size);
+ }
+ else if((time(NULL) - st->last_updated.tv_sec) > update_every * entries) {
+ errno = 0;
+ error("File %s is too old. Clearing it.", fullfilename);
+ bzero(st, size);
+ }
+ }
+
+ if(st) {
+ st->name = NULL;
+ st->type = NULL;
+ st->family = NULL;
+ st->context = NULL;
+ st->title = NULL;
+ st->units = NULL;
+ st->dimensions = NULL;
+ st->next = NULL;
+ st->mapped = rrd_memory_mode;
+ st->variables = NULL;
+ }
+ else {
+ st = callocz(1, size);
+ st->mapped = RRD_MEMORY_MODE_RAM;
+ }
+
+ st->memsize = size;
+ st->entries = entries;
+ st->update_every = update_every;
+
+ if(st->current_entry >= st->entries) st->current_entry = 0;
+
+ strcpy(st->cache_filename, fullfilename);
+ strcpy(st->magic, RRDSET_MAGIC);
+
+ strcpy(st->id, fullid);
+ st->hash = simple_hash(st->id);
+
+ st->cache_dir = cache_dir;
+
+ st->chart_type = rrdset_type_id(config_get(st->id, "chart type", rrdset_type_name(chart_type)));
+ st->type = config_get(st->id, "type", type);
+ st->family = config_get(st->id, "family", family?family:st->type);
+ st->units = config_get(st->id, "units", units?units:"");
+
+ st->context = config_get(st->id, "context", context?context:st->id);
+ st->hash_context = simple_hash(st->context);
+
+ st->priority = config_get_number(st->id, "priority", priority);
+ st->enabled = enabled;
+
+ st->isdetail = 0;
+ st->debug = 0;
+
+ // if(!strcmp(st->id, "disk_util.dm-0")) {
+ // st->debug = 1;
+ // error("enabled debugging for '%s'", st->id);
+ // }
+ // else error("not enabled debugging for '%s'", st->id);
+
+ st->green = NAN;
+ st->red = NAN;
+
+ st->last_collected_time.tv_sec = 0;
+ st->last_collected_time.tv_usec = 0;
+ st->counter_done = 0;
+
+ st->gap_when_lost_iterations_above = (int) (
+ config_get_number(st->id, "gap when lost iterations above", RRD_DEFAULT_GAP_INTERPOLATIONS) + 2);
+
+ avl_init_lock(&st->dimensions_index, rrddim_compare);
+ avl_init_lock(&st->variables_root_index, rrdvar_compare);
+
+ pthread_rwlock_init(&st->rwlock, NULL);
+ rrdhost_rwlock(&localhost);
+
+ if(name && *name) rrdset_set_name(st, name);
+ else rrdset_set_name(st, id);
+
+ {
+ char varvalue[CONFIG_MAX_VALUE + 1];
+ snprintfz(varvalue, CONFIG_MAX_VALUE, "%s (%s)", title?title:"", st->name);
+ st->title = config_get(st->id, "title", varvalue);
+ }
+
+ st->rrdfamily = rrdfamily_create(st->family);
+ st->rrdhost = &localhost;
+
+ st->next = localhost.rrdset_root;
+ localhost.rrdset_root = st;
+
+ if(health_enabled) {
+ rrdsetvar_create(st, "last_collected_t", RRDVAR_TYPE_TIME_T, &st->last_collected_time.tv_sec, 0);
+ rrdsetvar_create(st, "collected_total_raw", RRDVAR_TYPE_TOTAL, &st->last_collected_total, 0);
+ rrdsetvar_create(st, "green", RRDVAR_TYPE_CALCULATED, &st->green, 0);
+ rrdsetvar_create(st, "red", RRDVAR_TYPE_CALCULATED, &st->red, 0);
+ rrdsetvar_create(st, "update_every", RRDVAR_TYPE_INT, &st->update_every, 0);
+ }
+
+ rrdset_index_add(&localhost, st);
+
+ rrdsetcalc_link_matching(st);
+ rrdcalctemplate_link_matching(st);
+
+ rrdhost_unlock(&localhost);
+
+ return(st);
}
RRDDIM *rrddim_add(RRDSET *st, const char *id, const char *name, long multiplier, long divisor, int algorithm)
{
- char filename[FILENAME_MAX + 1];
- char fullfilename[FILENAME_MAX + 1];
-
- char varname[CONFIG_MAX_NAME + 1];
- RRDDIM *rd = NULL;
- unsigned long size = sizeof(RRDDIM) + (st->entries * sizeof(storage_number));
-
- debug(D_RRD_CALLS, "Adding dimension '%s/%s'.", st->id, id);
-
- rrdset_strncpy_name(filename, id, FILENAME_MAX);
- snprintfz(fullfilename, FILENAME_MAX, "%s/%s.db", st->cache_dir, filename);
- if(rrd_memory_mode != RRD_MEMORY_MODE_RAM) rd = (RRDDIM *)mymmap(fullfilename, size, ((rrd_memory_mode == RRD_MEMORY_MODE_MAP)?MAP_SHARED:MAP_PRIVATE), 1);
- if(rd) {
- struct timeval now;
- gettimeofday(&now, NULL);
-
- if(strcmp(rd->magic, RRDDIMENSION_MAGIC) != 0) {
- errno = 0;
- info("Initializing file %s.", fullfilename);
- bzero(rd, size);
- }
- else if(rd->memsize != size) {
- errno = 0;
- error("File %s does not have the desired size. Clearing it.", fullfilename);
- bzero(rd, size);
- }
- else if(rd->multiplier != multiplier) {
- errno = 0;
- error("File %s does not have the same multiplier. Clearing it.", fullfilename);
- bzero(rd, size);
- }
- else if(rd->divisor != divisor) {
- errno = 0;
- error("File %s does not have the same divisor. Clearing it.", fullfilename);
- bzero(rd, size);
- }
- else if(rd->algorithm != algorithm) {
- errno = 0;
- error("File %s does not have the same algorithm. Clearing it.", fullfilename);
- bzero(rd, size);
- }
- else if(rd->update_every != st->update_every) {
- errno = 0;
- error("File %s does not have the same refresh frequency. Clearing it.", fullfilename);
- bzero(rd, size);
- }
- else if(usecdiff(&now, &rd->last_collected_time) > (rd->entries * rd->update_every * 1000000ULL)) {
- errno = 0;
- error("File %s is too old. Clearing it.", fullfilename);
- bzero(rd, size);
- }
- else if(strcmp(rd->id, id) != 0) {
- errno = 0;
- error("File %s contents are not for dimension %s. Clearing it.", fullfilename, id);
- // munmap(rd, size);
- // rd = NULL;
- bzero(rd, size);
- }
- }
-
- if(rd) {
- // we have a file mapped for rd
- rd->mapped = rrd_memory_mode;
- rd->flags = 0x00000000;
- rd->next = NULL;
- rd->name = NULL;
- }
- else {
- // if we didn't manage to get a mmap'd dimension, just create one
-
- rd = calloc(1, size);
- if(!rd) {
- fatal("Cannot allocate RRD_DIMENSION %s/%s.", st->id, id);
- return NULL;
- }
-
- rd->mapped = RRD_MEMORY_MODE_RAM;
- }
- rd->memsize = size;
-
- strcpy(rd->magic, RRDDIMENSION_MAGIC);
- strcpy(rd->cache_filename, fullfilename);
- strncpyz(rd->id, id, RRD_ID_LENGTH_MAX);
- rd->hash = simple_hash(rd->id);
-
- snprintfz(varname, CONFIG_MAX_NAME, "dim %s name", rd->id);
- rd->name = config_get(st->id, varname, (name && *name)?name:rd->id);
-
- snprintfz(varname, CONFIG_MAX_NAME, "dim %s algorithm", rd->id);
- rd->algorithm = rrddim_algorithm_id(config_get(st->id, varname, rrddim_algorithm_name(algorithm)));
-
- snprintfz(varname, CONFIG_MAX_NAME, "dim %s multiplier", rd->id);
- rd->multiplier = config_get_number(st->id, varname, multiplier);
-
- snprintfz(varname, CONFIG_MAX_NAME, "dim %s divisor", rd->id);
- rd->divisor = config_get_number(st->id, varname, divisor);
- if(!rd->divisor) rd->divisor = 1;
-
- rd->entries = st->entries;
- rd->update_every = st->update_every;
-
- // prevent incremental calculation spikes
- rd->counter = 0;
-
- // append this dimension
- pthread_rwlock_wrlock(&st->rwlock);
- if(!st->dimensions)
- st->dimensions = rd;
- else {
- RRDDIM *td = st->dimensions;
- for(; td->next; td = td->next) ;
- td->next = rd;
- }
- pthread_rwlock_unlock(&st->rwlock);
-
- rrddim_index_add(st, rd);
-
- return(rd);
+ char filename[FILENAME_MAX + 1];
+ char fullfilename[FILENAME_MAX + 1];
+
+ char varname[CONFIG_MAX_NAME + 1];
+ RRDDIM *rd = NULL;
+ unsigned long size = sizeof(RRDDIM) + (st->entries * sizeof(storage_number));
+
+ debug(D_RRD_CALLS, "Adding dimension '%s/%s'.", st->id, id);
+
+ rrdset_strncpyz_name(filename, id, FILENAME_MAX);
+ snprintfz(fullfilename, FILENAME_MAX, "%s/%s.db", st->cache_dir, filename);
+ if(rrd_memory_mode != RRD_MEMORY_MODE_RAM) rd = (RRDDIM *)mymmap(fullfilename, size, ((rrd_memory_mode == RRD_MEMORY_MODE_MAP)?MAP_SHARED:MAP_PRIVATE), 1);
+ if(rd) {
+ struct timeval now;
+ gettimeofday(&now, NULL);
+
+ if(strcmp(rd->magic, RRDDIMENSION_MAGIC) != 0) {
+ errno = 0;
+ info("Initializing file %s.", fullfilename);
+ bzero(rd, size);
+ }
+ else if(rd->memsize != size) {
+ errno = 0;
+ error("File %s does not have the desired size. Clearing it.", fullfilename);
+ bzero(rd, size);
+ }
+ else if(rd->multiplier != multiplier) {
+ errno = 0;
+ error("File %s does not have the same multiplier. Clearing it.", fullfilename);
+ bzero(rd, size);
+ }
+ else if(rd->divisor != divisor) {
+ errno = 0;
+ error("File %s does not have the same divisor. Clearing it.", fullfilename);
+ bzero(rd, size);
+ }
+ else if(rd->algorithm != algorithm) {
+ errno = 0;
+ error("File %s does not have the same algorithm. Clearing it.", fullfilename);
+ bzero(rd, size);
+ }
+ else if(rd->update_every != st->update_every) {
+ errno = 0;
+ error("File %s does not have the same refresh frequency. Clearing it.", fullfilename);
+ bzero(rd, size);
+ }
+ else if(usec_dt(&now, &rd->last_collected_time) > (rd->entries * rd->update_every * 1000000ULL)) {
+ errno = 0;
+ error("File %s is too old. Clearing it.", fullfilename);
+ bzero(rd, size);
+ }
+ else if(strcmp(rd->id, id) != 0) {
+ errno = 0;
+ error("File %s contents are not for dimension %s. Clearing it.", fullfilename, id);
+ // munmap(rd, size);
+ // rd = NULL;
+ bzero(rd, size);
+ }
+ }
+
+ if(rd) {
+ // we have a file mapped for rd
+ rd->mapped = rrd_memory_mode;
+ rd->flags = 0x00000000;
+ rd->variables = NULL;
+ rd->next = NULL;
+ rd->name = NULL;
+ }
+ else {
+ // if we didn't manage to get a mmap'd dimension, just create one
+
+ rd = callocz(1, size);
+ rd->mapped = RRD_MEMORY_MODE_RAM;
+ }
+ rd->memsize = size;
+
+ strcpy(rd->magic, RRDDIMENSION_MAGIC);
+ strcpy(rd->cache_filename, fullfilename);
+ strncpyz(rd->id, id, RRD_ID_LENGTH_MAX);
+ rd->hash = simple_hash(rd->id);
+
+ snprintfz(varname, CONFIG_MAX_NAME, "dim %s name", rd->id);
+ rd->name = config_get(st->id, varname, (name && *name)?name:rd->id);
+
+ snprintfz(varname, CONFIG_MAX_NAME, "dim %s algorithm", rd->id);
+ rd->algorithm = rrddim_algorithm_id(config_get(st->id, varname, rrddim_algorithm_name(algorithm)));
+
+ snprintfz(varname, CONFIG_MAX_NAME, "dim %s multiplier", rd->id);
+ rd->multiplier = config_get_number(st->id, varname, multiplier);
+
+ snprintfz(varname, CONFIG_MAX_NAME, "dim %s divisor", rd->id);
+ rd->divisor = config_get_number(st->id, varname, divisor);
+ if(!rd->divisor) rd->divisor = 1;
+
+ rd->entries = st->entries;
+ rd->update_every = st->update_every;
+
+ // prevent incremental calculation spikes
+ rd->counter = 0;
+ rd->updated = 0;
+ rd->calculated_value = 0;
+ rd->last_calculated_value = 0;
+ rd->collected_value = 0;
+ rd->last_collected_value = 0;
+ rd->collected_volume = 0;
+ rd->stored_volume = 0;
+ rd->last_stored_value = 0;
+ rd->values[st->current_entry] = pack_storage_number(0, SN_NOT_EXISTS);
+ rd->last_collected_time.tv_sec = 0;
+ rd->last_collected_time.tv_usec = 0;
+ rd->rrdset = st;
+
+ // append this dimension
+ pthread_rwlock_wrlock(&st->rwlock);
+ if(!st->dimensions)
+ st->dimensions = rd;
+ else {
+ RRDDIM *td = st->dimensions;
+ for(; td->next; td = td->next) ;
+ td->next = rd;
+ }
+
+ if(health_enabled) {
+ rrddimvar_create(rd, RRDVAR_TYPE_CALCULATED, NULL, NULL, &rd->last_stored_value, 0);
+ rrddimvar_create(rd, RRDVAR_TYPE_COLLECTED, NULL, "_raw", &rd->last_collected_value, 0);
+ rrddimvar_create(rd, RRDVAR_TYPE_TIME_T, NULL, "_last_collected_t", &rd->last_collected_time.tv_sec, 0);
+ }
+
+ pthread_rwlock_unlock(&st->rwlock);
+
+ rrddim_index_add(st, rd);
+
+ return(rd);
}
void rrddim_set_name(RRDSET *st, RRDDIM *rd, const char *name)
{
- debug(D_RRD_CALLS, "rrddim_set_name() %s.%s", st->name, rd->name);
+ debug(D_RRD_CALLS, "rrddim_set_name() %s.%s", st->name, rd->name);
+
+ char varname[CONFIG_MAX_NAME + 1];
+ snprintfz(varname, CONFIG_MAX_NAME, "dim %s name", rd->id);
+ config_set_default(st->id, varname, name);
- char varname[CONFIG_MAX_NAME + 1];
- snprintfz(varname, CONFIG_MAX_NAME, "dim %s name", rd->id);
- config_set_default(st->id, varname, name);
+ rrddimvar_rename_all(rd);
}
void rrddim_free(RRDSET *st, RRDDIM *rd)
{
- debug(D_RRD_CALLS, "rrddim_free() %s.%s", st->name, rd->name);
-
- RRDDIM *i, *last = NULL;
- for(i = st->dimensions; i && i != rd ; i = i->next) last = i;
-
- if(!i) {
- error("Request to free dimension %s.%s but it is not linked.", st->id, rd->name);
- return;
- }
-
- if(last) last->next = rd->next;
- else st->dimensions = rd->next;
- rd->next = NULL;
-
- rrddim_index_del(st, rd);
-
- // free(rd->annotations);
- if(rd->mapped == RRD_MEMORY_MODE_SAVE) {
- debug(D_RRD_CALLS, "Saving dimension '%s' to '%s'.", rd->name, rd->cache_filename);
- savememory(rd->cache_filename, rd, rd->memsize);
-
- debug(D_RRD_CALLS, "Unmapping dimension '%s'.", rd->name);
- munmap(rd, rd->memsize);
- }
- else if(rd->mapped == RRD_MEMORY_MODE_MAP) {
- debug(D_RRD_CALLS, "Unmapping dimension '%s'.", rd->name);
- munmap(rd, rd->memsize);
- }
- else {
- debug(D_RRD_CALLS, "Removing dimension '%s'.", rd->name);
- free(rd);
- }
+ debug(D_RRD_CALLS, "rrddim_free() %s.%s", st->name, rd->name);
+
+ if(rd == st->dimensions)
+ st->dimensions = rd->next;
+ else {
+ RRDDIM *i;
+ for (i = st->dimensions; i && i->next != rd; i = i->next) ;
+
+ if (i && i->next == rd)
+ i->next = rd->next;
+ else
+ error("Request to free dimension '%s.%s' but it is not linked.", st->id, rd->name);
+ }
+ rd->next = NULL;
+
+ while(rd->variables)
+ rrddimvar_free(rd->variables);
+
+ rrddim_index_del(st, rd);
+
+ // free(rd->annotations);
+ if(rd->mapped == RRD_MEMORY_MODE_SAVE) {
+ debug(D_RRD_CALLS, "Saving dimension '%s' to '%s'.", rd->name, rd->cache_filename);
+ savememory(rd->cache_filename, rd, rd->memsize);
+
+ debug(D_RRD_CALLS, "Unmapping dimension '%s'.", rd->name);
+ munmap(rd, rd->memsize);
+ }
+ else if(rd->mapped == RRD_MEMORY_MODE_MAP) {
+ debug(D_RRD_CALLS, "Unmapping dimension '%s'.", rd->name);
+ munmap(rd, rd->memsize);
+ }
+ else {
+ debug(D_RRD_CALLS, "Removing dimension '%s'.", rd->name);
+ freez(rd);
+ }
}
void rrdset_free_all(void)
{
- info("Freeing all memory...");
+ info("Freeing all memory...");
- RRDSET *st;
- for(st = rrdset_root; st ;) {
- RRDSET *next = st->next;
+ rrdhost_rwlock(&localhost);
- while(st->dimensions)
- rrddim_free(st, st->dimensions);
+ RRDSET *st;
+ for(st = localhost.rrdset_root; st ;) {
+ RRDSET *next = st->next;
- rrdset_index_del(st);
+ pthread_rwlock_wrlock(&st->rwlock);
- if(st->mapped == RRD_MEMORY_MODE_SAVE) {
- debug(D_RRD_CALLS, "Saving stats '%s' to '%s'.", st->name, st->cache_filename);
- savememory(st->cache_filename, st, st->memsize);
+ while(st->variables)
+ rrdsetvar_free(st->variables);
- debug(D_RRD_CALLS, "Unmapping stats '%s'.", st->name);
- munmap(st, st->memsize);
- }
- else if(st->mapped == RRD_MEMORY_MODE_MAP) {
- debug(D_RRD_CALLS, "Unmapping stats '%s'.", st->name);
- munmap(st, st->memsize);
- }
- else
- free(st);
+ while(st->alarms)
+ rrdsetcalc_unlink(st->alarms);
- st = next;
- }
- rrdset_root = NULL;
+ while(st->dimensions)
+ rrddim_free(st, st->dimensions);
- info("Memory cleanup completed...");
-}
+ rrdset_index_del(&localhost, st);
-void rrdset_save_all(void)
-{
- debug(D_RRD_CALLS, "rrdset_save_all()");
+ st->rrdfamily->use_count--;
+ if(!st->rrdfamily->use_count)
+ rrdfamily_free(st->rrdfamily);
+
+ pthread_rwlock_unlock(&st->rwlock);
+
+ if(st->mapped == RRD_MEMORY_MODE_SAVE) {
+ debug(D_RRD_CALLS, "Saving stats '%s' to '%s'.", st->name, st->cache_filename);
+ savememory(st->cache_filename, st, st->memsize);
+
+ debug(D_RRD_CALLS, "Unmapping stats '%s'.", st->name);
+ munmap(st, st->memsize);
+ }
+ else if(st->mapped == RRD_MEMORY_MODE_MAP) {
+ debug(D_RRD_CALLS, "Unmapping stats '%s'.", st->name);
+ munmap(st, st->memsize);
+ }
+ else
+ freez(st);
+
+ st = next;
+ }
+ localhost.rrdset_root = NULL;
+
+ rrdhost_unlock(&localhost);
+
+ info("Memory cleanup completed...");
+}
- // let it log a few error messages
- error_log_limit_reset();
+void rrdset_save_all(void) {
+ info("Saving database...");
- RRDSET *st;
- RRDDIM *rd;
+ RRDSET *st;
+ RRDDIM *rd;
- pthread_rwlock_wrlock(&rrdset_root_rwlock);
- for(st = rrdset_root; st ; st = st->next) {
- pthread_rwlock_wrlock(&st->rwlock);
+ rrdhost_rwlock(&localhost);
+ for(st = localhost.rrdset_root; st ; st = st->next) {
+ pthread_rwlock_wrlock(&st->rwlock);
- if(st->mapped == RRD_MEMORY_MODE_SAVE) {
- debug(D_RRD_CALLS, "Saving stats '%s' to '%s'.", st->name, st->cache_filename);
- savememory(st->cache_filename, st, st->memsize);
- }
+ if(st->mapped == RRD_MEMORY_MODE_SAVE) {
+ debug(D_RRD_CALLS, "Saving stats '%s' to '%s'.", st->name, st->cache_filename);
+ savememory(st->cache_filename, st, st->memsize);
+ }
- for(rd = st->dimensions; rd ; rd = rd->next) {
- if(likely(rd->mapped == RRD_MEMORY_MODE_SAVE)) {
- debug(D_RRD_CALLS, "Saving dimension '%s' to '%s'.", rd->name, rd->cache_filename);
- savememory(rd->cache_filename, rd, rd->memsize);
- }
- }
+ for(rd = st->dimensions; rd ; rd = rd->next) {
+ if(likely(rd->mapped == RRD_MEMORY_MODE_SAVE)) {
+ debug(D_RRD_CALLS, "Saving dimension '%s' to '%s'.", rd->name, rd->cache_filename);
+ savememory(rd->cache_filename, rd, rd->memsize);
+ }
+ }
- pthread_rwlock_unlock(&st->rwlock);
- }
- pthread_rwlock_unlock(&rrdset_root_rwlock);
+ pthread_rwlock_unlock(&st->rwlock);
+ }
+ rrdhost_unlock(&localhost);
}
RRDSET *rrdset_find(const char *id)
{
- debug(D_RRD_CALLS, "rrdset_find() for chart %s", id);
+ debug(D_RRD_CALLS, "rrdset_find() for chart %s", id);
- RRDSET *st = rrdset_index_find(id, 0);
- return(st);
+ RRDSET *st = rrdset_index_find(&localhost, id, 0);
+ return(st);
}
RRDSET *rrdset_find_bytype(const char *type, const char *id)
{
- debug(D_RRD_CALLS, "rrdset_find_bytype() for chart %s.%s", type, id);
+ debug(D_RRD_CALLS, "rrdset_find_bytype() for chart %s.%s", type, id);
- char buf[RRD_ID_LENGTH_MAX + 1];
+ char buf[RRD_ID_LENGTH_MAX + 1];
- strncpyz(buf, type, RRD_ID_LENGTH_MAX - 1);
- strcat(buf, ".");
- int len = (int) strlen(buf);
- strncpyz(&buf[len], id, (size_t) (RRD_ID_LENGTH_MAX - len));
+ strncpyz(buf, type, RRD_ID_LENGTH_MAX - 1);
+ strcat(buf, ".");
+ int len = (int) strlen(buf);
+ strncpyz(&buf[len], id, (size_t) (RRD_ID_LENGTH_MAX - len));
- return(rrdset_find(buf));
+ return(rrdset_find(buf));
}
RRDSET *rrdset_find_byname(const char *name)
{
- debug(D_RRD_CALLS, "rrdset_find_byname() for chart %s", name);
+ debug(D_RRD_CALLS, "rrdset_find_byname() for chart %s", name);
- RRDSET *st = rrdset_index_find_name(name, 0);
- return(st);
+ RRDSET *st = rrdset_index_find_name(&localhost, name, 0);
+ return(st);
}
RRDDIM *rrddim_find(RRDSET *st, const char *id)
{
- debug(D_RRD_CALLS, "rrddim_find() for chart %s, dimension %s", st->name, id);
+ debug(D_RRD_CALLS, "rrddim_find() for chart %s, dimension %s", st->name, id);
- return rrddim_index_find(st, id, 0);
+ return rrddim_index_find(st, id, 0);
}
int rrddim_hide(RRDSET *st, const char *id)
{
- debug(D_RRD_CALLS, "rrddim_hide() for chart %s, dimension %s", st->name, id);
+ debug(D_RRD_CALLS, "rrddim_hide() for chart %s, dimension %s", st->name, id);
- RRDDIM *rd = rrddim_find(st, id);
- if(unlikely(!rd)) {
- error("Cannot find dimension with id '%s' on stats '%s' (%s).", id, st->name, st->id);
- return 1;
- }
+ RRDDIM *rd = rrddim_find(st, id);
+ if(unlikely(!rd)) {
+ error("Cannot find dimension with id '%s' on stats '%s' (%s).", id, st->name, st->id);
+ return 1;
+ }
- rd->flags |= RRDDIM_FLAG_HIDDEN;
- return 0;
+ rd->flags |= RRDDIM_FLAG_HIDDEN;
+ return 0;
}
int rrddim_unhide(RRDSET *st, const char *id)
{
- debug(D_RRD_CALLS, "rrddim_unhide() for chart %s, dimension %s", st->name, id);
+ debug(D_RRD_CALLS, "rrddim_unhide() for chart %s, dimension %s", st->name, id);
- RRDDIM *rd = rrddim_find(st, id);
- if(unlikely(!rd)) {
- error("Cannot find dimension with id '%s' on stats '%s' (%s).", id, st->name, st->id);
- return 1;
- }
+ RRDDIM *rd = rrddim_find(st, id);
+ if(unlikely(!rd)) {
+ error("Cannot find dimension with id '%s' on stats '%s' (%s).", id, st->name, st->id);
+ return 1;
+ }
- if(rd->flags & RRDDIM_FLAG_HIDDEN) rd->flags ^= RRDDIM_FLAG_HIDDEN;
- return 0;
+ if(rd->flags & RRDDIM_FLAG_HIDDEN) rd->flags ^= RRDDIM_FLAG_HIDDEN;
+ return 0;
}
collected_number rrddim_set_by_pointer(RRDSET *st, RRDDIM *rd, collected_number value)
{
- debug(D_RRD_CALLS, "rrddim_set_by_pointer() for chart %s, dimension %s, value " COLLECTED_NUMBER_FORMAT, st->name, rd->name, value);
+ debug(D_RRD_CALLS, "rrddim_set_by_pointer() for chart %s, dimension %s, value " COLLECTED_NUMBER_FORMAT, st->name, rd->name, value);
- gettimeofday(&rd->last_collected_time, NULL);
- rd->collected_value = value;
- rd->updated = 1;
- rd->counter++;
+ gettimeofday(&rd->last_collected_time, NULL);
+ rd->collected_value = value;
+ rd->updated = 1;
+ rd->counter++;
- return rd->last_collected_value;
+ return rd->last_collected_value;
}
collected_number rrddim_set(RRDSET *st, const char *id, collected_number value)
{
- RRDDIM *rd = rrddim_find(st, id);
- if(unlikely(!rd)) {
- error("Cannot find dimension with id '%s' on stats '%s' (%s).", id, st->name, st->id);
- return 0;
- }
+ RRDDIM *rd = rrddim_find(st, id);
+ if(unlikely(!rd)) {
+ error("Cannot find dimension with id '%s' on stats '%s' (%s).", id, st->name, st->id);
+ return 0;
+ }
- return rrddim_set_by_pointer(st, rd, value);
+ return rrddim_set_by_pointer(st, rd, value);
}
void rrdset_next_usec(RRDSET *st, unsigned long long microseconds)
{
- if(!microseconds) rrdset_next(st);
- else {
- debug(D_RRD_CALLS, "rrdset_next_usec() for chart %s with microseconds %llu", st->name, microseconds);
+ if(!microseconds) rrdset_next(st);
+ else {
+ debug(D_RRD_CALLS, "rrdset_next_usec() for chart %s with microseconds %llu", st->name, microseconds);
- if(unlikely(st->debug)) debug(D_RRD_STATS, "%s: NEXT: %llu microseconds", st->name, microseconds);
- st->usec_since_last_update = microseconds;
- }
+ if(unlikely(st->debug)) debug(D_RRD_STATS, "%s: NEXT: %llu microseconds", st->name, microseconds);
+ st->usec_since_last_update = microseconds;
+ }
}
void rrdset_next(RRDSET *st)
{
- unsigned long long microseconds = 0;
+ unsigned long long microseconds = 0;
- if(likely(st->last_collected_time.tv_sec)) {
- struct timeval now;
- gettimeofday(&now, NULL);
- microseconds = usecdiff(&now, &st->last_collected_time);
- }
- // prevent infinite loop
- else microseconds = st->update_every * 1000000ULL;
+ if(likely(st->last_collected_time.tv_sec)) {
+ struct timeval now;
+ gettimeofday(&now, NULL);
+ microseconds = usec_dt(&now, &st->last_collected_time);
+ }
+ // prevent infinite loop
+ else microseconds = st->update_every * 1000000ULL;
- rrdset_next_usec(st, microseconds);
+ rrdset_next_usec(st, microseconds);
}
void rrdset_next_plugins(RRDSET *st)
{
- rrdset_next(st);
+ rrdset_next(st);
}
unsigned long long rrdset_done(RRDSET *st)
{
- debug(D_RRD_CALLS, "rrdset_done() for chart %s", st->name);
-
- RRDDIM *rd, *last;
- int oldstate, store_this_entry = 1, first_entry = 0;
- unsigned long long last_ut, now_ut, next_ut, stored_entries = 0;
-
- if(unlikely(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate) != 0))
- error("Cannot set pthread cancel state to DISABLE.");
-
- // a read lock is OK here
- pthread_rwlock_rdlock(&st->rwlock);
-
- // enable the chart, if it was disabled
- if(unlikely(rrd_delete_unupdated_dimensions) && !st->enabled)
- st->enabled = 1;
-
- // check if the chart has a long time to be updated
- if(unlikely(st->usec_since_last_update > st->entries * st->update_every * 1000000ULL)) {
- info("%s: took too long to be updated (%0.3Lf secs). Reseting it.", st->name, (long double)(st->usec_since_last_update / 1000000.0));
- rrdset_reset(st);
- st->usec_since_last_update = st->update_every * 1000000ULL;
- first_entry = 1;
- }
- if(unlikely(st->debug)) debug(D_RRD_STATS, "%s: microseconds since last update: %llu", st->name, st->usec_since_last_update);
-
- // set last_collected_time
- if(unlikely(!st->last_collected_time.tv_sec)) {
- // it is the first entry
- // set the last_collected_time to now
- gettimeofday(&st->last_collected_time, NULL);
-
- // the first entry should not be stored
- store_this_entry = 0;
- first_entry = 1;
-
- if(unlikely(st->debug)) debug(D_RRD_STATS, "%s: has not set last_collected_time. Setting it now. Will not store the next entry.", st->name);
- }
- else {
- // it is not the first entry
- // calculate the proper last_collected_time, using usec_since_last_update
- unsigned long long ut = st->last_collected_time.tv_sec * 1000000ULL + st->last_collected_time.tv_usec + st->usec_since_last_update;
- st->last_collected_time.tv_sec = (time_t) (ut / 1000000ULL);
- st->last_collected_time.tv_usec = (useconds_t) (ut % 1000000ULL);
- }
-
- // if this set has not been updated in the past
- // we fake the last_update time to be = now - usec_since_last_update
- if(unlikely(!st->last_updated.tv_sec)) {
- // it has never been updated before
- // set a fake last_updated, in the past using usec_since_last_update
- unsigned long long ut = st->last_collected_time.tv_sec * 1000000ULL + st->last_collected_time.tv_usec - st->usec_since_last_update;
- st->last_updated.tv_sec = (time_t) (ut / 1000000ULL);
- st->last_updated.tv_usec = (useconds_t) (ut % 1000000ULL);
-
- // the first entry should not be stored
- store_this_entry = 0;
- first_entry = 1;
-
- if(unlikely(st->debug)) debug(D_RRD_STATS, "%s: initializing last_updated to now - %llu microseconds (%0.3Lf). Will not store the next entry.", st->name, st->usec_since_last_update, (long double)ut/1000000.0);
- }
-
- // check if we will re-write the entire data set
- if(unlikely(usecdiff(&st->last_collected_time, &st->last_updated) > st->update_every * st->entries * 1000000ULL)) {
- info("%s: too old data (last updated at %u.%u, last collected at %u.%u). Reseting it. Will not store the next entry.", st->name, st->last_updated.tv_sec, st->last_updated.tv_usec, st->last_collected_time.tv_sec, st->last_collected_time.tv_usec);
- rrdset_reset(st);
-
- st->usec_since_last_update = st->update_every * 1000000ULL;
-
- gettimeofday(&st->last_collected_time, NULL);
-
- unsigned long long ut = st->last_collected_time.tv_sec * 1000000ULL + st->last_collected_time.tv_usec - st->usec_since_last_update;
- st->last_updated.tv_sec = (time_t) (ut / 1000000ULL);
- st->last_updated.tv_usec = (useconds_t) (ut % 1000000ULL);
-
- // the first entry should not be stored
- store_this_entry = 0;
- first_entry = 1;
- }
-
- // these are the 3 variables that will help us in interpolation
- // last_ut = the last time we added a value to the storage
- // now_ut = the time the current value is taken at
- // next_ut = the time of the next interpolation point
- last_ut = st->last_updated.tv_sec * 1000000ULL + st->last_updated.tv_usec;
- now_ut = st->last_collected_time.tv_sec * 1000000ULL + st->last_collected_time.tv_usec;
- next_ut = (st->last_updated.tv_sec + st->update_every) * 1000000ULL;
-
- if(unlikely(!first_entry && now_ut < next_ut)) {
- if(unlikely(st->debug)) debug(D_RRD_STATS, "%s: THIS IS IN THE SAME INTERPOLATION POINT", st->name);
- }
-
- if(unlikely(st->debug)) {
- debug(D_RRD_STATS, "%s: last ut = %0.3Lf (last updated time)", st->name, (long double)last_ut/1000000.0);
- debug(D_RRD_STATS, "%s: now ut = %0.3Lf (current update time)", st->name, (long double)now_ut/1000000.0);
- debug(D_RRD_STATS, "%s: next ut = %0.3Lf (next interpolation point)", st->name, (long double)next_ut/1000000.0);
- }
-
- if(unlikely(!st->counter_done)) {
- store_this_entry = 0;
- if(unlikely(st->debug)) debug(D_RRD_STATS, "%s: Will not store the next entry.", st->name);
- }
- st->counter_done++;
-
- // calculate totals and count the dimensions
- int dimensions;
- st->collected_total = 0;
- for( rd = st->dimensions, dimensions = 0 ; likely(rd) ; rd = rd->next, dimensions++ )
- st->collected_total += rd->collected_value;
-
- uint32_t storage_flags = SN_EXISTS;
-
- // process all dimensions to calculate their values
- // based on the collected figures only
- // at this stage we do not interpolate anything
- for( rd = st->dimensions ; likely(rd) ; rd = rd->next ) {
-
- if(unlikely(st->debug)) debug(D_RRD_STATS, "%s/%s: START "
- " last_collected_value = " COLLECTED_NUMBER_FORMAT
- " collected_value = " COLLECTED_NUMBER_FORMAT
- " last_calculated_value = " CALCULATED_NUMBER_FORMAT
- " calculated_value = " CALCULATED_NUMBER_FORMAT
- , st->id, rd->name
- , rd->last_collected_value
- , rd->collected_value
- , rd->last_calculated_value
- , rd->calculated_value
- );
-
- switch(rd->algorithm) {
- case RRDDIM_ABSOLUTE:
- rd->calculated_value = (calculated_number)rd->collected_value
- * (calculated_number)rd->multiplier
- / (calculated_number)rd->divisor;
-
- if(unlikely(st->debug))
- debug(D_RRD_STATS, "%s/%s: CALC ABS/ABS-NO-IN "
- CALCULATED_NUMBER_FORMAT " = "
- COLLECTED_NUMBER_FORMAT
- " * " CALCULATED_NUMBER_FORMAT
- " / " CALCULATED_NUMBER_FORMAT
- , st->id, rd->name
- , rd->calculated_value
- , rd->collected_value
- , (calculated_number)rd->multiplier
- , (calculated_number)rd->divisor
- );
- break;
-
- case RRDDIM_PCENT_OVER_ROW_TOTAL:
- if(unlikely(!st->collected_total)) rd->calculated_value = 0;
- else
- // the percentage of the current value
- // over the total of all dimensions
- rd->calculated_value =
- (calculated_number)100
- * (calculated_number)rd->collected_value
- / (calculated_number)st->collected_total;
-
- if(unlikely(st->debug))
- debug(D_RRD_STATS, "%s/%s: CALC PCENT-ROW "
- CALCULATED_NUMBER_FORMAT " = 100"
- " * " COLLECTED_NUMBER_FORMAT
- " / " COLLECTED_NUMBER_FORMAT
- , st->id, rd->name
- , rd->calculated_value
- , rd->collected_value
- , st->collected_total
- );
- break;
-
- case RRDDIM_INCREMENTAL:
- if(unlikely(!rd->updated || rd->counter <= 1)) {
- rd->calculated_value = 0;
- continue;
- }
-
- // if the new is smaller than the old (an overflow, or reset), set the old equal to the new
- // to reset the calculation (it will give zero as the calculation for this second)
- if(unlikely(rd->last_collected_value > rd->collected_value)) {
- debug(D_RRD_STATS, "%s.%s: RESET or OVERFLOW. Last collected value = " COLLECTED_NUMBER_FORMAT ", current = " COLLECTED_NUMBER_FORMAT
- , st->name, rd->name
- , rd->last_collected_value
- , rd->collected_value);
- if(!(rd->flags & RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS)) storage_flags = SN_EXISTS_RESET;
- rd->last_collected_value = rd->collected_value;
- }
-
- rd->calculated_value = (calculated_number)(rd->collected_value - rd->last_collected_value)
- * (calculated_number)rd->multiplier
- / (calculated_number)rd->divisor;
-
- if(unlikely(st->debug))
- debug(D_RRD_STATS, "%s/%s: CALC INC PRE "
- CALCULATED_NUMBER_FORMAT " = ("
- COLLECTED_NUMBER_FORMAT " - " COLLECTED_NUMBER_FORMAT
- ")"
- " * " CALCULATED_NUMBER_FORMAT
- " / " CALCULATED_NUMBER_FORMAT
- , st->id, rd->name
- , rd->calculated_value
- , rd->collected_value, rd->last_collected_value
- , (calculated_number)rd->multiplier
- , (calculated_number)rd->divisor
- );
- break;
-
- case RRDDIM_PCENT_OVER_DIFF_TOTAL:
- if(unlikely(!rd->updated || rd->counter <= 1)) {
- rd->calculated_value = 0;
- continue;
- }
-
- // the percentage of the current increment
- // over the increment of all dimensions together
- if(unlikely(st->collected_total == st->last_collected_total)) rd->calculated_value = rd->last_calculated_value;
- else rd->calculated_value =
- (calculated_number)100
- * (calculated_number)(rd->collected_value - rd->last_collected_value)
- / (calculated_number)(st->collected_total - st->last_collected_total);
-
- if(unlikely(st->debug))
- debug(D_RRD_STATS, "%s/%s: CALC PCENT-DIFF "
- CALCULATED_NUMBER_FORMAT " = 100"
- " * (" COLLECTED_NUMBER_FORMAT " - " COLLECTED_NUMBER_FORMAT ")"
- " / (" COLLECTED_NUMBER_FORMAT " - " COLLECTED_NUMBER_FORMAT ")"
- , st->id, rd->name
- , rd->calculated_value
- , rd->collected_value, rd->last_collected_value
- , st->collected_total, st->last_collected_total
- );
- break;
-
- default:
- // make the default zero, to make sure
- // it gets noticed when we add new types
- rd->calculated_value = 0;
-
- if(unlikely(st->debug))
- debug(D_RRD_STATS, "%s/%s: CALC "
- CALCULATED_NUMBER_FORMAT " = 0"
- , st->id, rd->name
- , rd->calculated_value
- );
- break;
- }
-
- if(unlikely(st->debug)) debug(D_RRD_STATS, "%s/%s: PHASE2 "
- " last_collected_value = " COLLECTED_NUMBER_FORMAT
- " collected_value = " COLLECTED_NUMBER_FORMAT
- " last_calculated_value = " CALCULATED_NUMBER_FORMAT
- " calculated_value = " CALCULATED_NUMBER_FORMAT
- , st->id, rd->name
- , rd->last_collected_value
- , rd->collected_value
- , rd->last_calculated_value
- , rd->calculated_value
- );
-
- }
-
- // at this point we have all the calculated values ready
- // it is now time to interpolate values on a second boundary
-
- unsigned long long first_ut = last_ut;
- long long iterations = (now_ut - last_ut) / (st->update_every * 1000000ULL);
- if((now_ut % (st->update_every * 1000000ULL)) == 0) iterations++;
-
- for( ; likely(next_ut <= now_ut) ; next_ut += st->update_every * 1000000ULL, iterations-- ) {
+ if(unlikely(netdata_exit)) return 0;
+
+ debug(D_RRD_CALLS, "rrdset_done() for chart %s", st->name);
+
+ RRDDIM *rd, *last;
+ int oldstate, store_this_entry = 1, first_entry = 0;
+ unsigned long long last_ut, now_ut, next_ut, stored_entries = 0;
+
+ if(unlikely(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate) != 0))
+ error("Cannot set pthread cancel state to DISABLE.");
+
+ // a read lock is OK here
+ pthread_rwlock_rdlock(&st->rwlock);
+
+ // enable the chart, if it was disabled
+ if(unlikely(rrd_delete_unupdated_dimensions) && !st->enabled)
+ st->enabled = 1;
+
+ // check if the chart has a long time to be updated
+ if(unlikely(st->usec_since_last_update > st->entries * st->update_every * 1000000ULL)) {
+ info("%s: took too long to be updated (%0.3Lf secs). Reseting it.", st->name, (long double)(st->usec_since_last_update / 1000000.0));
+ rrdset_reset(st);
+ st->usec_since_last_update = st->update_every * 1000000ULL;
+ first_entry = 1;
+ }
+ if(unlikely(st->debug)) debug(D_RRD_STATS, "%s: microseconds since last update: %llu", st->name, st->usec_since_last_update);
+
+ // set last_collected_time
+ if(unlikely(!st->last_collected_time.tv_sec)) {
+ // it is the first entry
+ // set the last_collected_time to now
+ gettimeofday(&st->last_collected_time, NULL);
+
+ // the first entry should not be stored
+ store_this_entry = 0;
+ first_entry = 1;
+
+ if(unlikely(st->debug)) debug(D_RRD_STATS, "%s: has not set last_collected_time. Setting it now. Will not store the next entry.", st->name);
+ }
+ else {
+ // it is not the first entry
+ // calculate the proper last_collected_time, using usec_since_last_update
+ unsigned long long ut = st->last_collected_time.tv_sec * 1000000ULL + st->last_collected_time.tv_usec + st->usec_since_last_update;
+ st->last_collected_time.tv_sec = (time_t) (ut / 1000000ULL);
+ st->last_collected_time.tv_usec = (suseconds_t) (ut % 1000000ULL);
+ }
+
+ // if this set has not been updated in the past
+ // we fake the last_update time to be = now - usec_since_last_update
+ if(unlikely(!st->last_updated.tv_sec)) {
+ // it has never been updated before
+ // set a fake last_updated, in the past using usec_since_last_update
+ unsigned long long ut = st->last_collected_time.tv_sec * 1000000ULL + st->last_collected_time.tv_usec - st->usec_since_last_update;
+ st->last_updated.tv_sec = (time_t) (ut / 1000000ULL);
+ st->last_updated.tv_usec = (suseconds_t) (ut % 1000000ULL);
+
+ // the first entry should not be stored
+ store_this_entry = 0;
+ first_entry = 1;
+
+ if(unlikely(st->debug)) debug(D_RRD_STATS, "%s: initializing last_updated to now - %llu microseconds (%0.3Lf). Will not store the next entry.", st->name, st->usec_since_last_update, (long double)ut/1000000.0);
+ }
+
+ // check if we will re-write the entire data set
+ if(unlikely(usec_dt(&st->last_collected_time, &st->last_updated) > st->update_every * st->entries * 1000000ULL)) {
+ info("%s: too old data (last updated at %ld.%ld, last collected at %ld.%ld). Reseting it. Will not store the next entry.", st->name, st->last_updated.tv_sec, st->last_updated.tv_usec, st->last_collected_time.tv_sec, st->last_collected_time.tv_usec);
+ rrdset_reset(st);
+
+ st->usec_since_last_update = st->update_every * 1000000ULL;
+
+ gettimeofday(&st->last_collected_time, NULL);
+
+ unsigned long long ut = st->last_collected_time.tv_sec * 1000000ULL + st->last_collected_time.tv_usec - st->usec_since_last_update;
+ st->last_updated.tv_sec = (time_t) (ut / 1000000ULL);
+ st->last_updated.tv_usec = (suseconds_t) (ut % 1000000ULL);
+
+ // the first entry should not be stored
+ store_this_entry = 0;
+ first_entry = 1;
+ }
+
+ // these are the 3 variables that will help us in interpolation
+ // last_ut = the last time we added a value to the storage
+ // now_ut = the time the current value is taken at
+ // next_ut = the time of the next interpolation point
+ last_ut = st->last_updated.tv_sec * 1000000ULL + st->last_updated.tv_usec;
+ now_ut = st->last_collected_time.tv_sec * 1000000ULL + st->last_collected_time.tv_usec;
+ next_ut = (st->last_updated.tv_sec + st->update_every) * 1000000ULL;
+
+ if(unlikely(!first_entry && now_ut < next_ut)) {
+ if(unlikely(st->debug)) debug(D_RRD_STATS, "%s: THIS IS IN THE SAME INTERPOLATION POINT", st->name);
+ }
+
+ if(unlikely(st->debug)) {
+ debug(D_RRD_STATS, "%s: last ut = %0.3Lf (last updated time)", st->name, (long double)last_ut/1000000.0);
+ debug(D_RRD_STATS, "%s: now ut = %0.3Lf (current update time)", st->name, (long double)now_ut/1000000.0);
+ debug(D_RRD_STATS, "%s: next ut = %0.3Lf (next interpolation point)", st->name, (long double)next_ut/1000000.0);
+ }
+
+ if(unlikely(!st->counter_done)) {
+ store_this_entry = 0;
+ if(unlikely(st->debug)) debug(D_RRD_STATS, "%s: Will not store the next entry.", st->name);
+ }
+ st->counter_done++;
+
+ // calculate totals and count the dimensions
+ int dimensions;
+ st->collected_total = 0;
+ for( rd = st->dimensions, dimensions = 0 ; likely(rd) ; rd = rd->next, dimensions++ )
+ if(likely(rd->updated)) st->collected_total += rd->collected_value;
+
+ uint32_t storage_flags = SN_EXISTS;
+
+ // process all dimensions to calculate their values
+ // based on the collected figures only
+ // at this stage we do not interpolate anything
+ for( rd = st->dimensions ; likely(rd) ; rd = rd->next ) {
+
+ if(unlikely(!rd->updated)) {
+ rd->calculated_value = 0;
+ continue;
+ }
+
+ if(unlikely(st->debug)) debug(D_RRD_STATS, "%s/%s: START "
+ " last_collected_value = " COLLECTED_NUMBER_FORMAT
+ " collected_value = " COLLECTED_NUMBER_FORMAT
+ " last_calculated_value = " CALCULATED_NUMBER_FORMAT
+ " calculated_value = " CALCULATED_NUMBER_FORMAT
+ , st->id, rd->name
+ , rd->last_collected_value
+ , rd->collected_value
+ , rd->last_calculated_value
+ , rd->calculated_value
+ );
+
+ switch(rd->algorithm) {
+ case RRDDIM_ABSOLUTE:
+ rd->calculated_value = (calculated_number)rd->collected_value
+ * (calculated_number)rd->multiplier
+ / (calculated_number)rd->divisor;
+
+ if(unlikely(st->debug))
+ debug(D_RRD_STATS, "%s/%s: CALC ABS/ABS-NO-IN "
+ CALCULATED_NUMBER_FORMAT " = "
+ COLLECTED_NUMBER_FORMAT
+ " * " CALCULATED_NUMBER_FORMAT
+ " / " CALCULATED_NUMBER_FORMAT
+ , st->id, rd->name
+ , rd->calculated_value
+ , rd->collected_value
+ , (calculated_number)rd->multiplier
+ , (calculated_number)rd->divisor
+ );
+ break;
+
+ case RRDDIM_PCENT_OVER_ROW_TOTAL:
+ if(unlikely(!st->collected_total))
+ rd->calculated_value = 0;
+ else
+ // the percentage of the current value
+ // over the total of all dimensions
+ rd->calculated_value =
+ (calculated_number)100
+ * (calculated_number)rd->collected_value
+ / (calculated_number)st->collected_total;
+
+ if(unlikely(st->debug))
+ debug(D_RRD_STATS, "%s/%s: CALC PCENT-ROW "
+ CALCULATED_NUMBER_FORMAT " = 100"
+ " * " COLLECTED_NUMBER_FORMAT
+ " / " COLLECTED_NUMBER_FORMAT
+ , st->id, rd->name
+ , rd->calculated_value
+ , rd->collected_value
+ , st->collected_total
+ );
+ break;
+
+ case RRDDIM_INCREMENTAL:
+ if(unlikely(rd->counter <= 1)) {
+ rd->calculated_value = 0;
+ continue;
+ }
+
+ // if the new is smaller than the old (an overflow, or reset), set the old equal to the new
+ // to reset the calculation (it will give zero as the calculation for this second)
+ if(unlikely(rd->last_collected_value > rd->collected_value)) {
+ debug(D_RRD_STATS, "%s.%s: RESET or OVERFLOW. Last collected value = " COLLECTED_NUMBER_FORMAT ", current = " COLLECTED_NUMBER_FORMAT
+ , st->name, rd->name
+ , rd->last_collected_value
+ , rd->collected_value);
+ if(!(rd->flags & RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS)) storage_flags = SN_EXISTS_RESET;
+ rd->last_collected_value = rd->collected_value;
+ }
+
+ rd->calculated_value =
+ (calculated_number)(rd->collected_value - rd->last_collected_value)
+ * (calculated_number)rd->multiplier
+ / (calculated_number)rd->divisor;
+
+ if(unlikely(st->debug))
+ debug(D_RRD_STATS, "%s/%s: CALC INC PRE "
+ CALCULATED_NUMBER_FORMAT " = ("
+ COLLECTED_NUMBER_FORMAT " - " COLLECTED_NUMBER_FORMAT
+ ")"
+ " * " CALCULATED_NUMBER_FORMAT
+ " / " CALCULATED_NUMBER_FORMAT
+ , st->id, rd->name
+ , rd->calculated_value
+ , rd->collected_value, rd->last_collected_value
+ , (calculated_number)rd->multiplier
+ , (calculated_number)rd->divisor
+ );
+ break;
+
+ case RRDDIM_PCENT_OVER_DIFF_TOTAL:
+ if(unlikely(rd->counter <= 1)) {
+ rd->calculated_value = 0;
+ continue;
+ }
+
+ // if the new is smaller than the old (an overflow, or reset), set the old equal to the new
+ // to reset the calculation (it will give zero as the calculation for this second)
+ if(unlikely(rd->last_collected_value > rd->collected_value)) {
+ debug(D_RRD_STATS, "%s.%s: RESET or OVERFLOW. Last collected value = " COLLECTED_NUMBER_FORMAT ", current = " COLLECTED_NUMBER_FORMAT
+ , st->name, rd->name
+ , rd->last_collected_value
+ , rd->collected_value);
+ if(!(rd->flags & RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS)) storage_flags = SN_EXISTS_RESET;
+ rd->last_collected_value = rd->collected_value;
+ }
+
+ // the percentage of the current increment
+ // over the increment of all dimensions together
+ if(unlikely(st->collected_total == st->last_collected_total))
+ rd->calculated_value = 0;
+ else
+ rd->calculated_value =
+ (calculated_number)100
+ * (calculated_number)(rd->collected_value - rd->last_collected_value)
+ / (calculated_number)(st->collected_total - st->last_collected_total);
+
+ if(unlikely(st->debug))
+ debug(D_RRD_STATS, "%s/%s: CALC PCENT-DIFF "
+ CALCULATED_NUMBER_FORMAT " = 100"
+ " * (" COLLECTED_NUMBER_FORMAT " - " COLLECTED_NUMBER_FORMAT ")"
+ " / (" COLLECTED_NUMBER_FORMAT " - " COLLECTED_NUMBER_FORMAT ")"
+ , st->id, rd->name
+ , rd->calculated_value
+ , rd->collected_value, rd->last_collected_value
+ , st->collected_total, st->last_collected_total
+ );
+ break;
+
+ default:
+ // make the default zero, to make sure
+ // it gets noticed when we add new types
+ rd->calculated_value = 0;
+
+ if(unlikely(st->debug))
+ debug(D_RRD_STATS, "%s/%s: CALC "
+ CALCULATED_NUMBER_FORMAT " = 0"
+ , st->id, rd->name
+ , rd->calculated_value
+ );
+ break;
+ }
+
+ if(unlikely(st->debug)) debug(D_RRD_STATS, "%s/%s: PHASE2 "
+ " last_collected_value = " COLLECTED_NUMBER_FORMAT
+ " collected_value = " COLLECTED_NUMBER_FORMAT
+ " last_calculated_value = " CALCULATED_NUMBER_FORMAT
+ " calculated_value = " CALCULATED_NUMBER_FORMAT
+ , st->id, rd->name
+ , rd->last_collected_value
+ , rd->collected_value
+ , rd->last_calculated_value
+ , rd->calculated_value
+ );
+
+ }
+
+ // at this point we have all the calculated values ready
+ // it is now time to interpolate values on a second boundary
+
+ unsigned long long first_ut = last_ut;
+ long long iterations = (now_ut - last_ut) / (st->update_every * 1000000ULL);
+ if((now_ut % (st->update_every * 1000000ULL)) == 0) iterations++;
+
+ for( ; likely(next_ut <= now_ut) ; next_ut += st->update_every * 1000000ULL, iterations-- ) {
#ifdef NETDATA_INTERNAL_CHECKS
- if(iterations < 0) { error("%s: iterations calculation wrapped! first_ut = %llu, last_ut = %llu, next_ut = %llu, now_ut = %llu", st->name, first_ut, last_ut, next_ut, now_ut); }
+ if(iterations < 0) { error("%s: iterations calculation wrapped! first_ut = %llu, last_ut = %llu, next_ut = %llu, now_ut = %llu", st->name, first_ut, last_ut, next_ut, now_ut); }
#endif
- if(unlikely(st->debug)) {
- debug(D_RRD_STATS, "%s: last ut = %0.3Lf (last updated time)", st->name, (long double)last_ut/1000000.0);
- debug(D_RRD_STATS, "%s: next ut = %0.3Lf (next interpolation point)", st->name, (long double)next_ut/1000000.0);
- }
-
- st->last_updated.tv_sec = (time_t) (next_ut / 1000000ULL);
- st->last_updated.tv_usec = 0;
-
- for( rd = st->dimensions ; likely(rd) ; rd = rd->next ) {
- calculated_number new_value;
-
- switch(rd->algorithm) {
- case RRDDIM_INCREMENTAL:
- new_value = (calculated_number)
- ( rd->calculated_value
- * (calculated_number)(next_ut - last_ut)
- / (calculated_number)(now_ut - last_ut)
- );
-
- if(unlikely(st->debug))
- debug(D_RRD_STATS, "%s/%s: CALC2 INC "
- CALCULATED_NUMBER_FORMAT " = "
- CALCULATED_NUMBER_FORMAT
- " * %llu"
- " / %llu"
- , st->id, rd->name
- , new_value
- , rd->calculated_value
- , (next_ut - last_ut)
- , (now_ut - last_ut)
- );
-
- rd->calculated_value -= new_value;
- new_value += rd->last_calculated_value;
- rd->last_calculated_value = 0;
- new_value /= (calculated_number)st->update_every;
- break;
-
- case RRDDIM_ABSOLUTE:
- case RRDDIM_PCENT_OVER_ROW_TOTAL:
- case RRDDIM_PCENT_OVER_DIFF_TOTAL:
- default:
- if(iterations == 1) {
- // this is the last iteration
- // do not interpolate
- // just show the calculated value
-
- new_value = rd->calculated_value;
- }
- else {
- // we have missed an update
- // interpolate in the middle values
-
- new_value = (calculated_number)
- ( ( (rd->calculated_value - rd->last_calculated_value)
- * (calculated_number)(next_ut - first_ut)
- / (calculated_number)(now_ut - first_ut)
- )
- + rd->last_calculated_value
- );
-
- if(unlikely(st->debug))
- debug(D_RRD_STATS, "%s/%s: CALC2 DEF "
- CALCULATED_NUMBER_FORMAT " = ((("
- "(" CALCULATED_NUMBER_FORMAT " - " CALCULATED_NUMBER_FORMAT ")"
- " * %llu"
- " / %llu) + " CALCULATED_NUMBER_FORMAT
- , st->id, rd->name
- , new_value
- , rd->calculated_value, rd->last_calculated_value
- , (next_ut - first_ut)
- , (now_ut - first_ut), rd->last_calculated_value
- );
-
- // this is wrong
- // it fades the value towards the target
- // while we know the calculated value is different
- // if(likely(next_ut + st->update_every * 1000000ULL > now_ut)) rd->calculated_value = new_value;
- }
- break;
- }
-
- if(unlikely(!store_this_entry)) {
- store_this_entry = 1;
- continue;
- }
-
- if(likely(rd->updated && rd->counter > 1 && iterations < st->gap_when_lost_iterations_above)) {
- rd->values[st->current_entry] = pack_storage_number(new_value, storage_flags );
-
- if(unlikely(st->debug))
- debug(D_RRD_STATS, "%s/%s: STORE[%ld] "
- CALCULATED_NUMBER_FORMAT " = " CALCULATED_NUMBER_FORMAT
- , st->id, rd->name
- , st->current_entry
- , unpack_storage_number(rd->values[st->current_entry]), new_value
- );
- }
- else {
- if(unlikely(st->debug)) debug(D_RRD_STATS, "%s/%s: STORE[%ld] = NON EXISTING "
- , st->id, rd->name
- , st->current_entry
- );
- rd->values[st->current_entry] = pack_storage_number(0, SN_NOT_EXISTS);
- }
-
- stored_entries++;
-
- if(unlikely(st->debug)) {
- calculated_number t1 = new_value * (calculated_number)rd->multiplier / (calculated_number)rd->divisor;
- calculated_number t2 = unpack_storage_number(rd->values[st->current_entry]);
- calculated_number accuracy = accuracy_loss(t1, t2);
- debug(D_RRD_STATS, "%s/%s: UNPACK[%ld] = " CALCULATED_NUMBER_FORMAT " FLAGS=0x%08x (original = " CALCULATED_NUMBER_FORMAT ", accuracy loss = " CALCULATED_NUMBER_FORMAT "%%%s)"
- , st->id, rd->name
- , st->current_entry
- , t2
- , get_storage_number_flags(rd->values[st->current_entry])
- , t1
- , accuracy
- , (accuracy > ACCURACY_LOSS) ? " **TOO BIG** " : ""
- );
-
- rd->collected_volume += t1;
- rd->stored_volume += t2;
- accuracy = accuracy_loss(rd->collected_volume, rd->stored_volume);
- debug(D_RRD_STATS, "%s/%s: VOLUME[%ld] = " CALCULATED_NUMBER_FORMAT ", calculated = " CALCULATED_NUMBER_FORMAT ", accuracy loss = " CALCULATED_NUMBER_FORMAT "%%%s"
- , st->id, rd->name
- , st->current_entry
- , rd->stored_volume
- , rd->collected_volume
- , accuracy
- , (accuracy > ACCURACY_LOSS) ? " **TOO BIG** " : ""
- );
-
- }
- }
- // reset the storage flags for the next point, if any;
- storage_flags = SN_EXISTS;
-
- st->counter++;
- st->current_entry = ((st->current_entry + 1) >= st->entries) ? 0 : st->current_entry + 1;
- last_ut = next_ut;
- }
-
- // align next interpolation to last collection point
- if(likely(stored_entries || !store_this_entry)) {
- st->last_updated.tv_sec = st->last_collected_time.tv_sec;
- st->last_updated.tv_usec = st->last_collected_time.tv_usec;
- }
-
- for( rd = st->dimensions; likely(rd) ; rd = rd->next ) {
- if(unlikely(!rd->updated)) continue;
-
- if(likely(stored_entries || !store_this_entry)) {
- if(unlikely(st->debug)) debug(D_RRD_STATS, "%s/%s: setting last_collected_value (old: " COLLECTED_NUMBER_FORMAT ") to last_collected_value (new: " COLLECTED_NUMBER_FORMAT ")", st->id, rd->name, rd->last_collected_value, rd->collected_value);
- rd->last_collected_value = rd->collected_value;
-
- if(unlikely(st->debug)) debug(D_RRD_STATS, "%s/%s: setting last_calculated_value (old: " CALCULATED_NUMBER_FORMAT ") to last_calculated_value (new: " CALCULATED_NUMBER_FORMAT ")", st->id, rd->name, rd->last_calculated_value, rd->calculated_value);
- rd->last_calculated_value = rd->calculated_value;
- }
-
- rd->calculated_value = 0;
- rd->collected_value = 0;
- rd->updated = 0;
-
- if(unlikely(st->debug)) debug(D_RRD_STATS, "%s/%s: END "
- " last_collected_value = " COLLECTED_NUMBER_FORMAT
- " collected_value = " COLLECTED_NUMBER_FORMAT
- " last_calculated_value = " CALCULATED_NUMBER_FORMAT
- " calculated_value = " CALCULATED_NUMBER_FORMAT
- , st->id, rd->name
- , rd->last_collected_value
- , rd->collected_value
- , rd->last_calculated_value
- , rd->calculated_value
- );
- }
- st->last_collected_total = st->collected_total;
-
- // ALL DONE ABOUT THE DATA UPDATE
- // --------------------------------------------------------------------
-
- // find if there are any obsolete dimensions (not updated recently)
- if(unlikely(rrd_delete_unupdated_dimensions)) {
-
- for( rd = st->dimensions; likely(rd) ; rd = rd->next )
- if((rd->last_collected_time.tv_sec + (rrd_delete_unupdated_dimensions * st->update_every)) < st->last_collected_time.tv_sec)
- break;
-
- if(unlikely(rd)) {
- // there is dimension to free
- // upgrade our read lock to a write lock
- pthread_rwlock_unlock(&st->rwlock);
- pthread_rwlock_wrlock(&st->rwlock);
-
- for( rd = st->dimensions, last = NULL ; likely(rd) ; ) {
- // remove it only it is not updated in rrd_delete_unupdated_dimensions seconds
-
- if(unlikely((rd->last_collected_time.tv_sec + (rrd_delete_unupdated_dimensions * st->update_every)) < st->last_collected_time.tv_sec)) {
- info("Removing obsolete dimension '%s' (%s) of '%s' (%s).", rd->name, rd->id, st->name, st->id);
-
- if(unlikely(!last)) {
- st->dimensions = rd->next;
- rd->next = NULL;
- rrddim_free(st, rd);
- rd = st->dimensions;
- continue;
- }
- else {
- last->next = rd->next;
- rd->next = NULL;
- rrddim_free(st, rd);
- rd = last->next;
- continue;
- }
- }
-
- last = rd;
- rd = rd->next;
- }
-
- if(unlikely(!st->dimensions)) {
- info("Disabling chart %s (%s) since it does not have any dimensions", st->name, st->id);
- st->enabled = 0;
- }
- }
- }
-
- pthread_rwlock_unlock(&st->rwlock);
-
- if(unlikely(pthread_setcancelstate(oldstate, NULL) != 0))
- error("Cannot set pthread cancel state to RESTORE (%d).", oldstate);
-
- return(st->usec_since_last_update);
+ if(unlikely(st->debug)) {
+ debug(D_RRD_STATS, "%s: last ut = %0.3Lf (last updated time)", st->name, (long double)last_ut/1000000.0);
+ debug(D_RRD_STATS, "%s: next ut = %0.3Lf (next interpolation point)", st->name, (long double)next_ut/1000000.0);
+ }
+
+ st->last_updated.tv_sec = (time_t) (next_ut / 1000000ULL);
+ st->last_updated.tv_usec = 0;
+
+ for( rd = st->dimensions ; likely(rd) ; rd = rd->next ) {
+ calculated_number new_value;
+
+ switch(rd->algorithm) {
+ case RRDDIM_INCREMENTAL:
+ new_value = (calculated_number)
+ ( rd->calculated_value
+ * (calculated_number)(next_ut - last_ut)
+ / (calculated_number)(now_ut - last_ut)
+ );
+
+ if(unlikely(st->debug))
+ debug(D_RRD_STATS, "%s/%s: CALC2 INC "
+ CALCULATED_NUMBER_FORMAT " = "
+ CALCULATED_NUMBER_FORMAT
+ " * %llu"
+ " / %llu"
+ , st->id, rd->name
+ , new_value
+ , rd->calculated_value
+ , (next_ut - last_ut)
+ , (now_ut - last_ut)
+ );
+
+ rd->calculated_value -= new_value;
+ new_value += rd->last_calculated_value;
+ rd->last_calculated_value = 0;
+ new_value /= (calculated_number)st->update_every;
+ break;
+
+ case RRDDIM_ABSOLUTE:
+ case RRDDIM_PCENT_OVER_ROW_TOTAL:
+ case RRDDIM_PCENT_OVER_DIFF_TOTAL:
+ default:
+ if(iterations == 1) {
+ // this is the last iteration
+ // do not interpolate
+ // just show the calculated value
+
+ new_value = rd->calculated_value;
+ }
+ else {
+ // we have missed an update
+ // interpolate in the middle values
+
+ new_value = (calculated_number)
+ ( ( (rd->calculated_value - rd->last_calculated_value)
+ * (calculated_number)(next_ut - first_ut)
+ / (calculated_number)(now_ut - first_ut)
+ )
+ + rd->last_calculated_value
+ );
+
+ if(unlikely(st->debug))
+ debug(D_RRD_STATS, "%s/%s: CALC2 DEF "
+ CALCULATED_NUMBER_FORMAT " = ((("
+ "(" CALCULATED_NUMBER_FORMAT " - " CALCULATED_NUMBER_FORMAT ")"
+ " * %llu"
+ " / %llu) + " CALCULATED_NUMBER_FORMAT
+ , st->id, rd->name
+ , new_value
+ , rd->calculated_value, rd->last_calculated_value
+ , (next_ut - first_ut)
+ , (now_ut - first_ut), rd->last_calculated_value
+ );
+
+ // this is wrong
+ // it fades the value towards the target
+ // while we know the calculated value is different
+ // if(likely(next_ut + st->update_every * 1000000ULL > now_ut)) rd->calculated_value = new_value;
+ }
+ break;
+ }
+
+ if(unlikely(!store_this_entry)) {
+ // store_this_entry = 1;
+ continue;
+ }
+
+ if(likely(rd->updated && rd->counter > 1 && iterations < st->gap_when_lost_iterations_above)) {
+ rd->values[st->current_entry] = pack_storage_number(new_value, storage_flags );
+ rd->last_stored_value = new_value;
+
+ if(unlikely(st->debug))
+ debug(D_RRD_STATS, "%s/%s: STORE[%ld] "
+ CALCULATED_NUMBER_FORMAT " = " CALCULATED_NUMBER_FORMAT
+ , st->id, rd->name
+ , st->current_entry
+ , unpack_storage_number(rd->values[st->current_entry]), new_value
+ );
+ }
+ else {
+ if(unlikely(st->debug)) debug(D_RRD_STATS, "%s/%s: STORE[%ld] = NON EXISTING "
+ , st->id, rd->name
+ , st->current_entry
+ );
+ rd->values[st->current_entry] = pack_storage_number(0, SN_NOT_EXISTS);
+ rd->last_stored_value = NAN;
+ }
+
+ stored_entries++;
+
+ if(unlikely(st->debug)) {
+ calculated_number t1 = new_value * (calculated_number)rd->multiplier / (calculated_number)rd->divisor;
+ calculated_number t2 = unpack_storage_number(rd->values[st->current_entry]);
+ calculated_number accuracy = accuracy_loss(t1, t2);
+ debug(D_RRD_STATS, "%s/%s: UNPACK[%ld] = " CALCULATED_NUMBER_FORMAT " FLAGS=0x%08x (original = " CALCULATED_NUMBER_FORMAT ", accuracy loss = " CALCULATED_NUMBER_FORMAT "%%%s)"
+ , st->id, rd->name
+ , st->current_entry
+ , t2
+ , get_storage_number_flags(rd->values[st->current_entry])
+ , t1
+ , accuracy
+ , (accuracy > ACCURACY_LOSS) ? " **TOO BIG** " : ""
+ );
+
+ rd->collected_volume += t1;
+ rd->stored_volume += t2;
+ accuracy = accuracy_loss(rd->collected_volume, rd->stored_volume);
+ debug(D_RRD_STATS, "%s/%s: VOLUME[%ld] = " CALCULATED_NUMBER_FORMAT ", calculated = " CALCULATED_NUMBER_FORMAT ", accuracy loss = " CALCULATED_NUMBER_FORMAT "%%%s"
+ , st->id, rd->name
+ , st->current_entry
+ , rd->stored_volume
+ , rd->collected_volume
+ , accuracy
+ , (accuracy > ACCURACY_LOSS) ? " **TOO BIG** " : ""
+ );
+
+ }
+ }
+ // reset the storage flags for the next point, if any;
+ storage_flags = SN_EXISTS;
+
+ st->counter++;
+ st->current_entry = ((st->current_entry + 1) >= st->entries) ? 0 : st->current_entry + 1;
+ last_ut = next_ut;
+ }
+
+ // align next interpolation to last collection point
+ if(likely(stored_entries || !store_this_entry)) {
+ st->last_updated.tv_sec = st->last_collected_time.tv_sec;
+ st->last_updated.tv_usec = st->last_collected_time.tv_usec;
+ st->last_collected_total = st->collected_total;
+ }
+
+ for( rd = st->dimensions; likely(rd) ; rd = rd->next ) {
+ if(unlikely(!rd->updated)) continue;
+
+ if(likely(stored_entries || !store_this_entry)) {
+ if(unlikely(st->debug)) debug(D_RRD_STATS, "%s/%s: setting last_collected_value (old: " COLLECTED_NUMBER_FORMAT ") to last_collected_value (new: " COLLECTED_NUMBER_FORMAT ")", st->id, rd->name, rd->last_collected_value, rd->collected_value);
+ rd->last_collected_value = rd->collected_value;
+
+ if(unlikely(st->debug)) debug(D_RRD_STATS, "%s/%s: setting last_calculated_value (old: " CALCULATED_NUMBER_FORMAT ") to last_calculated_value (new: " CALCULATED_NUMBER_FORMAT ")", st->id, rd->name, rd->last_calculated_value, rd->calculated_value);
+ rd->last_calculated_value = rd->calculated_value;
+ }
+
+ rd->calculated_value = 0;
+ rd->collected_value = 0;
+ rd->updated = 0;
+
+ if(unlikely(st->debug)) debug(D_RRD_STATS, "%s/%s: END "
+ " last_collected_value = " COLLECTED_NUMBER_FORMAT
+ " collected_value = " COLLECTED_NUMBER_FORMAT
+ " last_calculated_value = " CALCULATED_NUMBER_FORMAT
+ " calculated_value = " CALCULATED_NUMBER_FORMAT
+ , st->id, rd->name
+ , rd->last_collected_value
+ , rd->collected_value
+ , rd->last_calculated_value
+ , rd->calculated_value
+ );
+ }
+
+ // ALL DONE ABOUT THE DATA UPDATE
+ // --------------------------------------------------------------------
+
+ // find if there are any obsolete dimensions (not updated recently)
+ if(unlikely(rrd_delete_unupdated_dimensions)) {
+
+ for( rd = st->dimensions; likely(rd) ; rd = rd->next )
+ if((rd->last_collected_time.tv_sec + (rrd_delete_unupdated_dimensions * st->update_every)) < st->last_collected_time.tv_sec)
+ break;
+
+ if(unlikely(rd)) {
+ // there is dimension to free
+ // upgrade our read lock to a write lock
+ pthread_rwlock_unlock(&st->rwlock);
+ pthread_rwlock_wrlock(&st->rwlock);
+
+ for( rd = st->dimensions, last = NULL ; likely(rd) ; ) {
+ // remove it only it is not updated in rrd_delete_unupdated_dimensions seconds
+
+ if(unlikely((rd->last_collected_time.tv_sec + (rrd_delete_unupdated_dimensions * st->update_every)) < st->last_collected_time.tv_sec)) {
+ info("Removing obsolete dimension '%s' (%s) of '%s' (%s).", rd->name, rd->id, st->name, st->id);
+
+ if(unlikely(!last)) {
+ st->dimensions = rd->next;
+ rd->next = NULL;
+ rrddim_free(st, rd);
+ rd = st->dimensions;
+ continue;
+ }
+ else {
+ last->next = rd->next;
+ rd->next = NULL;
+ rrddim_free(st, rd);
+ rd = last->next;
+ continue;
+ }
+ }
+
+ last = rd;
+ rd = rd->next;
+ }
+
+ if(unlikely(!st->dimensions)) {
+ info("Disabling chart %s (%s) since it does not have any dimensions", st->name, st->id);
+ st->enabled = 0;
+ }
+ }
+ }
+
+ pthread_rwlock_unlock(&st->rwlock);
+
+ if(unlikely(pthread_setcancelstate(oldstate, NULL) != 0))
+ error("Cannot set pthread cancel state to RESTORE (%d).", oldstate);
+
+ return(st->usec_since_last_update);
}
diff --git a/src/rrd.h b/src/rrd.h
index 60eeeb04f..37eb121e7 100644
--- a/src/rrd.h
+++ b/src/rrd.h
@@ -1,10 +1,3 @@
-#include <sys/time.h>
-#include <pthread.h>
-#include <stdint.h>
-
-#include "avl.h"
-#include "storage_number.h"
-
#ifndef NETDATA_RRD_H
#define NETDATA_RRD_H 1
@@ -20,10 +13,10 @@ extern int rrd_default_history_entries;
// set to zero to disable this feature
extern int rrd_delete_unupdated_dimensions;
-#define RRD_ID_LENGTH_MAX 1024
+#define RRD_ID_LENGTH_MAX 400
-#define RRDSET_MAGIC "NETDATA RRD SET FILE V017"
-#define RRDDIMENSION_MAGIC "NETDATA RRD DIMENSION FILE V017"
+#define RRDSET_MAGIC "NETDATA RRD SET FILE V018"
+#define RRDDIMENSION_MAGIC "NETDATA RRD DIMENSION FILE V018"
typedef long long total_number;
#define TOTAL_NUMBER_FORMAT "%lld"
@@ -35,8 +28,8 @@ typedef long long total_number;
#define RRDSET_TYPE_AREA_NAME "area"
#define RRDSET_TYPE_STACKED_NAME "stacked"
-#define RRDSET_TYPE_LINE 0
-#define RRDSET_TYPE_AREA 1
+#define RRDSET_TYPE_LINE 0
+#define RRDSET_TYPE_AREA 1
#define RRDSET_TYPE_STACKED 2
int rrdset_type_id(const char *name);
@@ -63,15 +56,15 @@ extern int rrd_memory_mode_id(const char *name);
// ----------------------------------------------------------------------------
// algorithms types
-#define RRDDIM_ABSOLUTE_NAME "absolute"
-#define RRDDIM_INCREMENTAL_NAME "incremental"
-#define RRDDIM_PCENT_OVER_DIFF_TOTAL_NAME "percentage-of-incremental-row"
-#define RRDDIM_PCENT_OVER_ROW_TOTAL_NAME "percentage-of-absolute-row"
+#define RRDDIM_ABSOLUTE_NAME "absolute"
+#define RRDDIM_INCREMENTAL_NAME "incremental"
+#define RRDDIM_PCENT_OVER_DIFF_TOTAL_NAME "percentage-of-incremental-row"
+#define RRDDIM_PCENT_OVER_ROW_TOTAL_NAME "percentage-of-absolute-row"
-#define RRDDIM_ABSOLUTE 0
-#define RRDDIM_INCREMENTAL 1
-#define RRDDIM_PCENT_OVER_DIFF_TOTAL 2
-#define RRDDIM_PCENT_OVER_ROW_TOTAL 3
+#define RRDDIM_ABSOLUTE 0
+#define RRDDIM_INCREMENTAL 1
+#define RRDDIM_PCENT_OVER_DIFF_TOTAL 2
+#define RRDDIM_PCENT_OVER_ROW_TOTAL 3
extern int rrddim_algorithm_id(const char *name);
extern const char *rrddim_algorithm_name(int chart_type);
@@ -83,80 +76,103 @@ extern const char *rrddim_algorithm_name(int chart_type);
#define RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS 0x00000002 // do not offer RESET or OVERFLOW info to callers
// ----------------------------------------------------------------------------
+// RRD CONTEXT
+
+struct rrdfamily {
+ avl avl;
+
+ const char *family;
+ uint32_t hash_family;
+
+ size_t use_count;
+
+ avl_tree_lock variables_root_index;
+};
+typedef struct rrdfamily RRDFAMILY;
+
+// ----------------------------------------------------------------------------
// RRD DIMENSION
struct rrddim {
- // ------------------------------------------------------------------------
- // binary indexing structures
+ // ------------------------------------------------------------------------
+ // binary indexing structures
+
+ avl avl; // the binary index - this has to be first member!
+
+ // ------------------------------------------------------------------------
+ // the dimension definition
+
+ char id[RRD_ID_LENGTH_MAX + 1]; // the id of this dimension (for internal identification)
- avl avl; // the binary index - this has to be first member!
+ const char *name; // the name of this dimension (as presented to user)
+ // this is a pointer to the config structure
+ // since the config always has a higher priority
+ // (the user overwrites the name of the charts)
- // ------------------------------------------------------------------------
- // the dimension definition
+ int algorithm; // the algorithm that is applied to add new collected values
+ long multiplier; // the multiplier of the collected values
+ long divisor; // the divider of the collected values
- char id[RRD_ID_LENGTH_MAX + 1]; // the id of this dimension (for internal identification)
+ int mapped; // if set to non zero, this dimension is mapped to a file
- const char *name; // the name of this dimension (as presented to user)
- // this is a pointer to the config structure
- // since the config always has a higher priority
- // (the user overwrites the name of the charts)
+ // ------------------------------------------------------------------------
+ // members for temporary data we need for calculations
- int algorithm; // the algorithm that is applied to add new collected values
- long multiplier; // the multiplier of the collected values
- long divisor; // the divider of the collected values
+ uint32_t hash; // a simple hash of the id, to speed up searching / indexing
+ // instead of strcmp() every item in the binary index
+ // we first compare the hashes
- int mapped; // if set to non zero, this dimension is mapped to a file
+ // FIXME
+ // we need the hash_name too!
- // ------------------------------------------------------------------------
- // members for temporary data we need for calculations
+ uint32_t flags;
- uint32_t hash; // a simple hash of the id, to speed up searching / indexing
- // instead of strcmp() every item in the binary index
- // we first compare the hashes
+ char cache_filename[FILENAME_MAX+1]; // the filename we load/save from/to this set
- uint32_t flags;
+ unsigned long counter; // the number of times we added values to this rrdim
- char cache_filename[FILENAME_MAX+1]; // the filename we load/save from/to this set
+ int updated; // set to 0 after each calculation, to 1 after each collected value
+ // we use this to detect that a dimension is not updated
- unsigned long counter; // the number of times we added values to this rrdim
+ struct timeval last_collected_time; // when was this dimension last updated
+ // this is actual date time we updated the last_collected_value
+ // THIS IS DIFFERENT FROM THE SAME MEMBER OF RRDSET
- int updated; // set to 0 after each calculation, to 1 after each collected value
- // we use this to detect that a dimension is not updated
+ calculated_number calculated_value; // the current calculated value, after applying the algorithm - resets to zero after being used
+ calculated_number last_calculated_value; // the last calculated value processed
- struct timeval last_collected_time; // when was this dimension last updated
- // this is actual date time we updated the last_collected_value
- // THIS IS DIFFERENT FROM THE SAME MEMBER OF RRDSET
+ calculated_number last_stored_value; // the last value as stored in the database (after interpolation)
- calculated_number calculated_value; // the current calculated value, after applying the algorithm
- calculated_number last_calculated_value; // the last calculated value
+ collected_number collected_value; // the current value, as collected - resets to 0 after being used
+ collected_number last_collected_value; // the last value that was collected, after being processed
- collected_number collected_value; // the current value, as collected
- collected_number last_collected_value; // the last value that was collected
+ // the *_volume members are used to calculate the accuracy of the rounding done by the
+ // storage number - they are printed to debug.log when debug is enabled for a set.
+ calculated_number collected_volume; // the sum of all collected values so far
+ calculated_number stored_volume; // the sum of all stored values so far
- // the *_volume members are used to calculate the accuracy of the rounding done by the
- // storage number - they are printed to debug.log when debug is enabled for a set.
- calculated_number collected_volume; // the sum of all collected values so far
- calculated_number stored_volume; // the sum of all stored values so far
+ struct rrddim *next; // linking of dimensions within the same data set
+ struct rrdset *rrdset;
- struct rrddim *next; // linking of dimensions within the same data set
+ // ------------------------------------------------------------------------
+ // members for checking the data when loading from disk
- // ------------------------------------------------------------------------
- // members for checking the data when loading from disk
+ long entries; // how many entries this dimension has in ram
+ // this is the same to the entries of the data set
+ // we set it here, to check the data when we load it from disk.
- long entries; // how many entries this dimension has in ram
- // this is the same to the entries of the data set
- // we set it here, to check the data when we load it from disk.
+ int update_every; // every how many seconds is this updated
- int update_every; // every how many seconds is this updated
+ unsigned long memsize; // the memory allocated for this dimension
- unsigned long memsize; // the memory allocated for this dimension
+ char magic[sizeof(RRDDIMENSION_MAGIC) + 1]; // a string to be saved, used to identify our data file
- char magic[sizeof(RRDDIMENSION_MAGIC) + 1]; // a string to be saved, used to identify our data file
+ struct rrddimvar *variables;
- // ------------------------------------------------------------------------
- // the values stored in this dimension, using our floating point numbers
+ // ------------------------------------------------------------------------
+ // the values stored in this dimension, using our floating point numbers
- storage_number values[]; // the array of values - THIS HAS TO BE THE LAST MEMBER
+ storage_number values[]; // the array of values - THIS HAS TO BE THE LAST MEMBER
};
typedef struct rrddim RRDDIM;
@@ -165,99 +181,155 @@ typedef struct rrddim RRDDIM;
// RRDSET
struct rrdset {
- // ------------------------------------------------------------------------
- // binary indexing structures
+ // ------------------------------------------------------------------------
+ // binary indexing structures
+
+ avl avl; // the index, with key the id - this has to be first!
+ avl avlname; // the index, with key the name
+
+ // ------------------------------------------------------------------------
+ // the set configuration
+
+ char id[RRD_ID_LENGTH_MAX + 1]; // id of the data set
- avl avl; // the index, with key the id - this has to be first!
- avl avlname; // the index, with key the name
+ const char *name; // the name of this dimension (as presented to user)
+ // this is a pointer to the config structure
+ // since the config always has a higher priority
+ // (the user overwrites the name of the charts)
- // ------------------------------------------------------------------------
- // the set configuration
+ char *type; // the type of graph RRD_TYPE_* (a category, for determining graphing options)
+ char *family; // grouping sets under the same family
+ char *title; // title shown to user
+ char *units; // units of measurement
- char id[RRD_ID_LENGTH_MAX + 1]; // id of the data set
+ char *context; // the template of this data set
+ uint32_t hash_context;
- const char *name; // the name of this dimension (as presented to user)
- // this is a pointer to the config structure
- // since the config always has a higher priority
- // (the user overwrites the name of the charts)
+ int chart_type;
- char *type; // the type of graph RRD_TYPE_* (a category, for determining graphing options)
- char *family; // grouping sets under the same family
- char *context; // the template of this data set
- char *title; // title shown to user
- char *units; // units of measurement
+ int update_every; // every how many seconds is this updated?
- int chart_type;
+ long entries; // total number of entries in the data set
- int update_every; // every how many seconds is this updated?
+ long current_entry; // the entry that is currently being updated
+ // it goes around in a round-robin fashion
- long entries; // total number of entries in the data set
+ int enabled;
- long current_entry; // the entry that is currently being updated
- // it goes around in a round-robin fashion
+ int gap_when_lost_iterations_above; // after how many lost iterations a gap should be stored
+ // netdata will interpolate values for gaps lower than this
- int enabled;
+ long priority;
- int gap_when_lost_iterations_above; // after how many lost iterations a gap should be stored
- // netdata will interpolate values for gaps lower than this
+ int isdetail; // if set, the data set should be considered as a detail of another
+ // (the master data set should be the one that has the same family and is not detail)
- long priority;
+ // ------------------------------------------------------------------------
+ // members for temporary data we need for calculations
- int isdetail; // if set, the data set should be considered as a detail of another
- // (the master data set should be the one that has the same family and is not detail)
+ int mapped; // if set to 1, this is memory mapped
- // ------------------------------------------------------------------------
- // members for temporary data we need for calculations
+ int debug;
- int mapped; // if set to 1, this is memory mapped
+ char *cache_dir; // the directory to store dimensions
+ char cache_filename[FILENAME_MAX+1]; // the filename to store this set
- int debug;
+ pthread_rwlock_t rwlock;
- char *cache_dir; // the directory to store dimensions
- char cache_filename[FILENAME_MAX+1]; // the filename to store this set
+ unsigned long counter; // the number of times we added values to this rrd
+ unsigned long counter_done; // the number of times we added values to this rrd
- pthread_rwlock_t rwlock;
+ uint32_t hash; // a simple hash on the id, to speed up searching
+ // we first compare hashes, and only if the hashes are equal we do string comparisons
- unsigned long counter; // the number of times we added values to this rrd
- unsigned long counter_done; // the number of times we added values to this rrd
+ uint32_t hash_name; // a simple hash on the name
- uint32_t hash; // a simple hash on the id, to speed up searching
- // we first compare hashes, and only if the hashes are equal we do string comparisons
+ unsigned long long usec_since_last_update; // the time in microseconds since the last collection of data
- uint32_t hash_name; // a simple hash on the name
+ struct timeval last_updated; // when this data set was last updated (updated every time the rrd_stats_done() function)
+ struct timeval last_collected_time; // when did this data set last collected values
- unsigned long long usec_since_last_update; // the time in microseconds since the last collection of data
+ total_number collected_total; // used internally to calculate percentages
+ total_number last_collected_total; // used internally to calculate percentages
- struct timeval last_updated; // when this data set was last updated (updated every time the rrd_stats_done() function)
- struct timeval last_collected_time; // when did this data set last collected values
+ RRDFAMILY *rrdfamily;
+ struct rrdhost *rrdhost;
- total_number collected_total; // used internally to calculate percentages
- total_number last_collected_total; // used internally to calculate percentages
+ struct rrdset *next; // linking of rrdsets
- struct rrdset *next; // linking of rrdsets
+ // ------------------------------------------------------------------------
+ // local variables
- // ------------------------------------------------------------------------
- // members for checking the data when loading from disk
+ calculated_number green;
+ calculated_number red;
- unsigned long memsize; // how much mem we have allocated for this (without dimensions)
+ avl_tree_lock variables_root_index;
+ RRDSETVAR *variables;
+ RRDCALC *alarms;
- char magic[sizeof(RRDSET_MAGIC) + 1]; // our magic
+ // ------------------------------------------------------------------------
+ // members for checking the data when loading from disk
- // ------------------------------------------------------------------------
- // the dimensions
+ unsigned long memsize; // how much mem we have allocated for this (without dimensions)
+
+ char magic[sizeof(RRDSET_MAGIC) + 1]; // our magic
+
+ // ------------------------------------------------------------------------
+ // the dimensions
+
+ avl_tree_lock dimensions_index; // the root of the dimensions index
+ RRDDIM *dimensions; // the actual data for every dimension
- avl_tree_lock dimensions_index; // the root of the dimensions index
- RRDDIM *dimensions; // the actual data for every dimension
};
typedef struct rrdset RRDSET;
-extern RRDSET *rrdset_root;
-extern pthread_rwlock_t rrdset_root_rwlock;
+// ----------------------------------------------------------------------------
+// RRD HOST
+
+struct rrdhost {
+ avl avl;
+
+ char *hostname;
+
+ RRDSET *rrdset_root;
+ pthread_rwlock_t rrdset_root_rwlock;
+
+ avl_tree_lock rrdset_root_index;
+ avl_tree_lock rrdset_root_index_name;
+
+ avl_tree_lock rrdfamily_root_index;
+ avl_tree_lock variables_root_index;
+
+ // all RRDCALCs are primarily allocated and linked here
+ // RRDCALCs may be linked to charts at any point
+ // (charts may or may not exist when these are loaded)
+ RRDCALC *alarms;
+ ALARM_LOG health_log;
+
+ RRDCALCTEMPLATE *templates;
+};
+typedef struct rrdhost RRDHOST;
+extern RRDHOST localhost;
+
+#ifdef NETDATA_INTERNAL_CHECKS
+#define rrdhost_check_wrlock(host) rrdhost_check_wrlock_int(host, __FILE__, __FUNCTION__, __LINE__)
+#define rrdhost_check_rdlock(host) rrdhost_check_rdlock_int(host, __FILE__, __FUNCTION__, __LINE__)
+#else
+#define rrdhost_check_rdlock(host) (void)0
+#define rrdhost_check_wrlock(host) (void)0
+#endif
+
+extern void rrdhost_check_wrlock_int(RRDHOST *host, const char *file, const char *function, const unsigned long line);
+extern void rrdhost_check_rdlock_int(RRDHOST *host, const char *file, const char *function, const unsigned long line);
+
+extern void rrdhost_rwlock(RRDHOST *host);
+extern void rrdhost_rdlock(RRDHOST *host);
+extern void rrdhost_unlock(RRDHOST *host);
// ----------------------------------------------------------------------------
// RRD SET functions
-extern char *rrdset_strncpy_name(char *to, const char *from, int length);
+extern char *rrdset_strncpyz_name(char *to, const char *from, size_t length);
extern void rrdset_set_name(RRDSET *st, const char *name);
extern char *rrdset_cache_dir(const char *id);
@@ -265,15 +337,15 @@ extern char *rrdset_cache_dir(const char *id);
extern void rrdset_reset(RRDSET *st);
extern RRDSET *rrdset_create(const char *type
- , const char *id
- , const char *name
- , const char *family
- , const char *context
- , const char *title
- , const char *units
- , long priority
- , int update_every
- , int chart_type);
+ , const char *id
+ , const char *name
+ , const char *family
+ , const char *context
+ , const char *title
+ , const char *units
+ , long priority
+ , int update_every
+ , int chart_type);
extern void rrdset_free_all(void);
extern void rrdset_save_all(void);
@@ -306,20 +378,20 @@ extern unsigned long long rrdset_done(RRDSET *st);
// get the slot of the round robin database, for the given timestamp (t)
// it always returns a valid slot, although may not be for the time requested if the time is outside the round robin database
#define rrdset_time2slot(st, t) ( \
- ( (time_t)(t) >= rrdset_last_entry_t(st)) ? ( rrdset_last_slot(st) ) : \
- ( ((time_t)(t) <= rrdset_first_entry_t(st)) ? rrdset_first_slot(st) : \
- ( (rrdset_last_slot(st) >= (unsigned long)((rrdset_last_entry_t(st) - (time_t)(t)) / (unsigned long)((st)->update_every)) ) ? \
- (rrdset_last_slot(st) - (unsigned long)((rrdset_last_entry_t(st) - (time_t)(t)) / (unsigned long)((st)->update_every)) ) : \
- (rrdset_last_slot(st) - (unsigned long)((rrdset_last_entry_t(st) - (time_t)(t)) / (unsigned long)((st)->update_every)) + (unsigned long)(st)->entries ) \
- )))
+ ( (time_t)(t) >= rrdset_last_entry_t(st)) ? ( rrdset_last_slot(st) ) : \
+ ( ((time_t)(t) <= rrdset_first_entry_t(st)) ? rrdset_first_slot(st) : \
+ ( (rrdset_last_slot(st) >= (unsigned long)((rrdset_last_entry_t(st) - (time_t)(t)) / (unsigned long)((st)->update_every)) ) ? \
+ (rrdset_last_slot(st) - (unsigned long)((rrdset_last_entry_t(st) - (time_t)(t)) / (unsigned long)((st)->update_every)) ) : \
+ (rrdset_last_slot(st) - (unsigned long)((rrdset_last_entry_t(st) - (time_t)(t)) / (unsigned long)((st)->update_every)) + (unsigned long)(st)->entries ) \
+ )))
// get the timestamp of a specific slot in the round robin database
#define rrdset_slot2time(st, slot) ( rrdset_last_entry_t(st) - \
- ((unsigned long)(st)->update_every * ( \
- ( (unsigned long)(slot) > rrdset_last_slot(st)) ? \
- ( (rrdset_last_slot(st) - (unsigned long)(slot) + (unsigned long)(st)->entries) ) : \
- ( (rrdset_last_slot(st) - (unsigned long)(slot)) )) \
- ))
+ ((unsigned long)(st)->update_every * ( \
+ ( (unsigned long)(slot) > rrdset_last_slot(st)) ? \
+ ( (rrdset_last_slot(st) - (unsigned long)(slot) + (unsigned long)(st)->entries) ) : \
+ ( (rrdset_last_slot(st) - (unsigned long)(slot)) )) \
+ ))
// ----------------------------------------------------------------------------
// RRD DIMENSION functions
diff --git a/src/rrd2json.c b/src/rrd2json.c
index e0bd06670..9009a8b1d 100644
--- a/src/rrd2json.c
+++ b/src/rrd2json.c
@@ -1,222 +1,215 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <pthread.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdint.h>
-#include <math.h>
-
-#include "log.h"
#include "common.h"
-#include "rrd2json.h"
#define HOSTNAME_MAX 1024
char *hostname = "unknown";
void rrd_stats_api_v1_chart(RRDSET *st, BUFFER *wb)
{
- pthread_rwlock_rdlock(&st->rwlock);
-
- buffer_sprintf(wb,
- "\t\t{\n"
- "\t\t\t\"id\": \"%s\",\n"
- "\t\t\t\"name\": \"%s\",\n"
- "\t\t\t\"type\": \"%s\",\n"
- "\t\t\t\"family\": \"%s\",\n"
- "\t\t\t\"context\": \"%s\",\n"
- "\t\t\t\"title\": \"%s\",\n"
- "\t\t\t\"priority\": %ld,\n"
- "\t\t\t\"enabled\": %s,\n"
- "\t\t\t\"units\": \"%s\",\n"
- "\t\t\t\"data_url\": \"/api/v1/data?chart=%s\",\n"
- "\t\t\t\"chart_type\": \"%s\",\n"
- "\t\t\t\"duration\": %ld,\n"
- "\t\t\t\"first_entry\": %lu,\n"
- "\t\t\t\"last_entry\": %lu,\n"
- "\t\t\t\"update_every\": %d,\n"
- "\t\t\t\"dimensions\": {\n"
- , st->id
- , st->name
- , st->type
- , st->family
- , st->context
- , st->title
- , st->priority
- , st->enabled?"true":"false"
- , st->units
- , st->name
- , rrdset_type_name(st->chart_type)
- , st->entries * st->update_every
- , rrdset_first_entry_t(st)
- , rrdset_last_entry_t(st)
- , st->update_every
- );
-
- unsigned long memory = st->memsize;
-
- int c = 0;
- RRDDIM *rd;
- for(rd = st->dimensions; rd ; rd = rd->next) {
- if(rd->flags & RRDDIM_FLAG_HIDDEN) continue;
-
- memory += rd->memsize;
-
- buffer_sprintf(wb,
- "%s"
- "\t\t\t\t\"%s\": { \"name\": \"%s\" }"
- , c?",\n":""
- , rd->id
- , rd->name
- );
-
- c++;
- }
-
- buffer_sprintf(wb,
- "\n\t\t\t}\n"
- "\t\t}"
- );
-
- pthread_rwlock_unlock(&st->rwlock);
+ pthread_rwlock_rdlock(&st->rwlock);
+
+ buffer_sprintf(wb,
+ "\t\t{\n"
+ "\t\t\t\"id\": \"%s\",\n"
+ "\t\t\t\"name\": \"%s\",\n"
+ "\t\t\t\"type\": \"%s\",\n"
+ "\t\t\t\"family\": \"%s\",\n"
+ "\t\t\t\"context\": \"%s\",\n"
+ "\t\t\t\"title\": \"%s\",\n"
+ "\t\t\t\"priority\": %ld,\n"
+ "\t\t\t\"enabled\": %s,\n"
+ "\t\t\t\"units\": \"%s\",\n"
+ "\t\t\t\"data_url\": \"/api/v1/data?chart=%s\",\n"
+ "\t\t\t\"chart_type\": \"%s\",\n"
+ "\t\t\t\"duration\": %ld,\n"
+ "\t\t\t\"first_entry\": %ld,\n"
+ "\t\t\t\"last_entry\": %ld,\n"
+ "\t\t\t\"update_every\": %d,\n"
+ "\t\t\t\"dimensions\": {\n"
+ , st->id
+ , st->name
+ , st->type
+ , st->family
+ , st->context
+ , st->title
+ , st->priority
+ , st->enabled?"true":"false"
+ , st->units
+ , st->name
+ , rrdset_type_name(st->chart_type)
+ , st->entries * st->update_every
+ , rrdset_first_entry_t(st)
+ , rrdset_last_entry_t(st)
+ , st->update_every
+ );
+
+ unsigned long memory = st->memsize;
+
+ int c = 0;
+ RRDDIM *rd;
+ for(rd = st->dimensions; rd ; rd = rd->next) {
+ if(rd->flags & RRDDIM_FLAG_HIDDEN) continue;
+
+ memory += rd->memsize;
+
+ buffer_sprintf(wb,
+ "%s"
+ "\t\t\t\t\"%s\": { \"name\": \"%s\" }"
+ , c?",\n":""
+ , rd->id
+ , rd->name
+ );
+
+ c++;
+ }
+
+ buffer_strcat(wb, "\n\t\t\t},\n\t\t\t\"green\": ");
+ buffer_rrd_value(wb, st->green);
+ buffer_strcat(wb, ",\n\t\t\t\"red\": ");
+ buffer_rrd_value(wb, st->red);
+
+ buffer_sprintf(wb,
+ "\n\t\t}"
+ );
+
+ pthread_rwlock_unlock(&st->rwlock);
}
void rrd_stats_api_v1_charts(BUFFER *wb)
{
- long c;
- RRDSET *st;
-
- buffer_sprintf(wb, "{\n"
- "\t\"hostname\": \"%s\""
- ",\n\t\"update_every\": %d"
- ",\n\t\"history\": %d"
- ",\n\t\"charts\": {"
- , hostname
- , rrd_update_every
- , rrd_default_history_entries
- );
-
- pthread_rwlock_rdlock(&rrdset_root_rwlock);
- for(st = rrdset_root, c = 0; st ; st = st->next) {
- if(st->enabled) {
- if(c) buffer_strcat(wb, ",");
- buffer_strcat(wb, "\n\t\t\"");
- buffer_strcat(wb, st->id);
- buffer_strcat(wb, "\": ");
- rrd_stats_api_v1_chart(st, wb);
- c++;
- }
- }
- pthread_rwlock_unlock(&rrdset_root_rwlock);
-
- buffer_strcat(wb, "\n\t}\n}\n");
+ long c;
+ RRDSET *st;
+
+ buffer_sprintf(wb, "{\n"
+ "\t\"hostname\": \"%s\""
+ ",\n\t\"update_every\": %d"
+ ",\n\t\"history\": %d"
+ ",\n\t\"charts\": {"
+ , hostname
+ , rrd_update_every
+ , rrd_default_history_entries
+ );
+
+ pthread_rwlock_rdlock(&localhost.rrdset_root_rwlock);
+ for(st = localhost.rrdset_root, c = 0; st ; st = st->next) {
+ if(st->enabled && st->dimensions) {
+ if(c) buffer_strcat(wb, ",");
+ buffer_strcat(wb, "\n\t\t\"");
+ buffer_strcat(wb, st->id);
+ buffer_strcat(wb, "\": ");
+ rrd_stats_api_v1_chart(st, wb);
+ c++;
+ }
+ }
+ pthread_rwlock_unlock(&localhost.rrdset_root_rwlock);
+
+ buffer_strcat(wb, "\n\t}\n}\n");
}
unsigned long rrd_stats_one_json(RRDSET *st, char *options, BUFFER *wb)
{
- time_t now = time(NULL);
-
- pthread_rwlock_rdlock(&st->rwlock);
-
- buffer_sprintf(wb,
- "\t\t{\n"
- "\t\t\t\"id\": \"%s\",\n"
- "\t\t\t\"name\": \"%s\",\n"
- "\t\t\t\"type\": \"%s\",\n"
- "\t\t\t\"family\": \"%s\",\n"
- "\t\t\t\"context\": \"%s\",\n"
- "\t\t\t\"title\": \"%s\",\n"
- "\t\t\t\"priority\": %ld,\n"
- "\t\t\t\"enabled\": %d,\n"
- "\t\t\t\"units\": \"%s\",\n"
- "\t\t\t\"url\": \"/data/%s/%s\",\n"
- "\t\t\t\"chart_type\": \"%s\",\n"
- "\t\t\t\"counter\": %ld,\n"
- "\t\t\t\"entries\": %ld,\n"
- "\t\t\t\"first_entry_t\": %lu,\n"
- "\t\t\t\"last_entry\": %ld,\n"
- "\t\t\t\"last_entry_t\": %lu,\n"
- "\t\t\t\"last_entry_secs_ago\": %lu,\n"
- "\t\t\t\"update_every\": %d,\n"
- "\t\t\t\"isdetail\": %d,\n"
- "\t\t\t\"usec_since_last_update\": %llu,\n"
- "\t\t\t\"collected_total\": " TOTAL_NUMBER_FORMAT ",\n"
- "\t\t\t\"last_collected_total\": " TOTAL_NUMBER_FORMAT ",\n"
- "\t\t\t\"dimensions\": [\n"
- , st->id
- , st->name
- , st->type
- , st->family
- , st->context
- , st->title
- , st->priority
- , st->enabled
- , st->units
- , st->name, options?options:""
- , rrdset_type_name(st->chart_type)
- , st->counter
- , st->entries
- , rrdset_first_entry_t(st)
- , rrdset_last_slot(st)
- , rrdset_last_entry_t(st)
- , (now < rrdset_last_entry_t(st)) ? (time_t)0 : now - rrdset_last_entry_t(st)
- , st->update_every
- , st->isdetail
- , st->usec_since_last_update
- , st->collected_total
- , st->last_collected_total
- );
-
- unsigned long memory = st->memsize;
-
- RRDDIM *rd;
- for(rd = st->dimensions; rd ; rd = rd->next) {
-
- memory += rd->memsize;
-
- buffer_sprintf(wb,
- "\t\t\t\t{\n"
- "\t\t\t\t\t\"id\": \"%s\",\n"
- "\t\t\t\t\t\"name\": \"%s\",\n"
- "\t\t\t\t\t\"entries\": %ld,\n"
- "\t\t\t\t\t\"isHidden\": %d,\n"
- "\t\t\t\t\t\"algorithm\": \"%s\",\n"
- "\t\t\t\t\t\"multiplier\": %ld,\n"
- "\t\t\t\t\t\"divisor\": %ld,\n"
- "\t\t\t\t\t\"last_entry_t\": %lu,\n"
- "\t\t\t\t\t\"collected_value\": " COLLECTED_NUMBER_FORMAT ",\n"
- "\t\t\t\t\t\"calculated_value\": " CALCULATED_NUMBER_FORMAT ",\n"
- "\t\t\t\t\t\"last_collected_value\": " COLLECTED_NUMBER_FORMAT ",\n"
- "\t\t\t\t\t\"last_calculated_value\": " CALCULATED_NUMBER_FORMAT ",\n"
- "\t\t\t\t\t\"memory\": %lu\n"
- "\t\t\t\t}%s\n"
- , rd->id
- , rd->name
- , rd->entries
- , (rd->flags & RRDDIM_FLAG_HIDDEN)?1:0
- , rrddim_algorithm_name(rd->algorithm)
- , rd->multiplier
- , rd->divisor
- , rd->last_collected_time.tv_sec
- , rd->collected_value
- , rd->calculated_value
- , rd->last_collected_value
- , rd->last_calculated_value
- , rd->memsize
- , rd->next?",":""
- );
- }
-
- buffer_sprintf(wb,
- "\t\t\t],\n"
- "\t\t\t\"memory\" : %lu\n"
- "\t\t}"
- , memory
- );
-
- pthread_rwlock_unlock(&st->rwlock);
- return memory;
+ time_t now = time(NULL);
+
+ pthread_rwlock_rdlock(&st->rwlock);
+
+ buffer_sprintf(wb,
+ "\t\t{\n"
+ "\t\t\t\"id\": \"%s\",\n"
+ "\t\t\t\"name\": \"%s\",\n"
+ "\t\t\t\"type\": \"%s\",\n"
+ "\t\t\t\"family\": \"%s\",\n"
+ "\t\t\t\"context\": \"%s\",\n"
+ "\t\t\t\"title\": \"%s\",\n"
+ "\t\t\t\"priority\": %ld,\n"
+ "\t\t\t\"enabled\": %d,\n"
+ "\t\t\t\"units\": \"%s\",\n"
+ "\t\t\t\"url\": \"/data/%s/%s\",\n"
+ "\t\t\t\"chart_type\": \"%s\",\n"
+ "\t\t\t\"counter\": %lu,\n"
+ "\t\t\t\"entries\": %ld,\n"
+ "\t\t\t\"first_entry_t\": %ld,\n"
+ "\t\t\t\"last_entry\": %lu,\n"
+ "\t\t\t\"last_entry_t\": %ld,\n"
+ "\t\t\t\"last_entry_secs_ago\": %ld,\n"
+ "\t\t\t\"update_every\": %d,\n"
+ "\t\t\t\"isdetail\": %d,\n"
+ "\t\t\t\"usec_since_last_update\": %llu,\n"
+ "\t\t\t\"collected_total\": " TOTAL_NUMBER_FORMAT ",\n"
+ "\t\t\t\"last_collected_total\": " TOTAL_NUMBER_FORMAT ",\n"
+ "\t\t\t\"dimensions\": [\n"
+ , st->id
+ , st->name
+ , st->type
+ , st->family
+ , st->context
+ , st->title
+ , st->priority
+ , st->enabled
+ , st->units
+ , st->name, options?options:""
+ , rrdset_type_name(st->chart_type)
+ , st->counter
+ , st->entries
+ , rrdset_first_entry_t(st)
+ , rrdset_last_slot(st)
+ , rrdset_last_entry_t(st)
+ , (now < rrdset_last_entry_t(st)) ? (time_t)0 : now - rrdset_last_entry_t(st)
+ , st->update_every
+ , st->isdetail
+ , st->usec_since_last_update
+ , st->collected_total
+ , st->last_collected_total
+ );
+
+ unsigned long memory = st->memsize;
+
+ RRDDIM *rd;
+ for(rd = st->dimensions; rd ; rd = rd->next) {
+
+ memory += rd->memsize;
+
+ buffer_sprintf(wb,
+ "\t\t\t\t{\n"
+ "\t\t\t\t\t\"id\": \"%s\",\n"
+ "\t\t\t\t\t\"name\": \"%s\",\n"
+ "\t\t\t\t\t\"entries\": %ld,\n"
+ "\t\t\t\t\t\"isHidden\": %d,\n"
+ "\t\t\t\t\t\"algorithm\": \"%s\",\n"
+ "\t\t\t\t\t\"multiplier\": %ld,\n"
+ "\t\t\t\t\t\"divisor\": %ld,\n"
+ "\t\t\t\t\t\"last_entry_t\": %ld,\n"
+ "\t\t\t\t\t\"collected_value\": " COLLECTED_NUMBER_FORMAT ",\n"
+ "\t\t\t\t\t\"calculated_value\": " CALCULATED_NUMBER_FORMAT ",\n"
+ "\t\t\t\t\t\"last_collected_value\": " COLLECTED_NUMBER_FORMAT ",\n"
+ "\t\t\t\t\t\"last_calculated_value\": " CALCULATED_NUMBER_FORMAT ",\n"
+ "\t\t\t\t\t\"memory\": %lu\n"
+ "\t\t\t\t}%s\n"
+ , rd->id
+ , rd->name
+ , rd->entries
+ , (rd->flags & RRDDIM_FLAG_HIDDEN)?1:0
+ , rrddim_algorithm_name(rd->algorithm)
+ , rd->multiplier
+ , rd->divisor
+ , rd->last_collected_time.tv_sec
+ , rd->collected_value
+ , rd->calculated_value
+ , rd->last_collected_value
+ , rd->last_calculated_value
+ , rd->memsize
+ , rd->next?",":""
+ );
+ }
+
+ buffer_sprintf(wb,
+ "\t\t\t],\n"
+ "\t\t\t\"memory\" : %lu\n"
+ "\t\t}"
+ , memory
+ );
+
+ pthread_rwlock_unlock(&st->rwlock);
+ return memory;
}
#define RRD_GRAPH_JSON_HEADER "{\n\t\"charts\": [\n"
@@ -224,40 +217,40 @@ unsigned long rrd_stats_one_json(RRDSET *st, char *options, BUFFER *wb)
void rrd_stats_graph_json(RRDSET *st, char *options, BUFFER *wb)
{
- buffer_strcat(wb, RRD_GRAPH_JSON_HEADER);
- rrd_stats_one_json(st, options, wb);
- buffer_strcat(wb, RRD_GRAPH_JSON_FOOTER);
+ buffer_strcat(wb, RRD_GRAPH_JSON_HEADER);
+ rrd_stats_one_json(st, options, wb);
+ buffer_strcat(wb, RRD_GRAPH_JSON_FOOTER);
}
void rrd_stats_all_json(BUFFER *wb)
{
- unsigned long memory = 0;
- long c;
- RRDSET *st;
-
- buffer_strcat(wb, RRD_GRAPH_JSON_HEADER);
-
- pthread_rwlock_rdlock(&rrdset_root_rwlock);
- for(st = rrdset_root, c = 0; st ; st = st->next) {
- if(st->enabled) {
- if(c) buffer_strcat(wb, ",\n");
- memory += rrd_stats_one_json(st, NULL, wb);
- c++;
- }
- }
- pthread_rwlock_unlock(&rrdset_root_rwlock);
-
- buffer_sprintf(wb, "\n\t],\n"
- "\t\"hostname\": \"%s\",\n"
- "\t\"update_every\": %d,\n"
- "\t\"history\": %d,\n"
- "\t\"memory\": %lu\n"
- "}\n"
- , hostname
- , rrd_update_every
- , rrd_default_history_entries
- , memory
- );
+ unsigned long memory = 0;
+ long c;
+ RRDSET *st;
+
+ buffer_strcat(wb, RRD_GRAPH_JSON_HEADER);
+
+ pthread_rwlock_rdlock(&localhost.rrdset_root_rwlock);
+ for(st = localhost.rrdset_root, c = 0; st ; st = st->next) {
+ if(st->enabled && st->dimensions) {
+ if(c) buffer_strcat(wb, ",\n");
+ memory += rrd_stats_one_json(st, NULL, wb);
+ c++;
+ }
+ }
+ pthread_rwlock_unlock(&localhost.rrdset_root_rwlock);
+
+ buffer_sprintf(wb, "\n\t],\n"
+ "\t\"hostname\": \"%s\",\n"
+ "\t\"update_every\": %d,\n"
+ "\t\"history\": %d,\n"
+ "\t\"memory\": %lu\n"
+ "}\n"
+ , hostname
+ , rrd_update_every
+ , rrd_default_history_entries
+ , memory
+ );
}
@@ -265,42 +258,42 @@ void rrd_stats_all_json(BUFFER *wb)
// ----------------------------------------------------------------------------
// RRDR dimension options
-#define RRDR_EMPTY 0x01 // the dimension contains / the value is empty (null)
-#define RRDR_RESET 0x02 // the dimension contains / the value is reset
-#define RRDR_HIDDEN 0x04 // the dimension contains / the value is hidden
-#define RRDR_NONZERO 0x08 // the dimension contains / the value is non-zero
+#define RRDR_EMPTY 0x01 // the dimension contains / the value is empty (null)
+#define RRDR_RESET 0x02 // the dimension contains / the value is reset
+#define RRDR_HIDDEN 0x04 // the dimension contains / the value is hidden
+#define RRDR_NONZERO 0x08 // the dimension contains / the value is non-zero
// RRDR result options
#define RRDR_RESULT_OPTION_ABSOLUTE 0x00000001
#define RRDR_RESULT_OPTION_RELATIVE 0x00000002
typedef struct rrdresult {
- RRDSET *st; // the chart this result refers to
+ RRDSET *st; // the chart this result refers to
- uint32_t result_options; // RRDR_RESULT_OPTION_*
+ uint32_t result_options; // RRDR_RESULT_OPTION_*
- int d; // the number of dimensions
- long n; // the number of values in the arrays
- long rows; // the number of rows used
+ int d; // the number of dimensions
+ long n; // the number of values in the arrays
+ long rows; // the number of rows used
- uint8_t *od; // the options for the dimensions
+ uint8_t *od; // the options for the dimensions
- time_t *t; // array of n timestamps
- calculated_number *v; // array n x d values
- uint8_t *o; // array n x d options
+ time_t *t; // array of n timestamps
+ calculated_number *v; // array n x d values
+ uint8_t *o; // array n x d options
- long c; // current line ( -1 ~ n ), ( -1 = none, use rrdr_rows() to get number of rows )
+ long c; // current line ( -1 ~ n ), ( -1 = none, use rrdr_rows() to get number of rows )
- long group; // how many collected values were grouped for each row
- long update_every; // what is the suggested update frequency in seconds
+ long group; // how many collected values were grouped for each row
+ int update_every; // what is the suggested update frequency in seconds
- calculated_number min;
- calculated_number max;
+ calculated_number min;
+ calculated_number max;
- time_t before;
- time_t after;
+ time_t before;
+ time_t after;
- int has_st_lock; // if st is read locked by us
+ int has_st_lock; // if st is read locked by us
} RRDR;
#define rrdr_rows(r) ((r)->rows)
@@ -308,346 +301,347 @@ typedef struct rrdresult {
/*
static void rrdr_dump(RRDR *r)
{
- long c, i;
- RRDDIM *d;
-
- fprintf(stderr, "\nCHART %s (%s)\n", r->st->id, r->st->name);
-
- for(c = 0, d = r->st->dimensions; d ;c++, d = d->next) {
- fprintf(stderr, "DIMENSION %s (%s), %s%s%s%s\n"
- , d->id
- , d->name
- , (r->od[c] & RRDR_EMPTY)?"EMPTY ":""
- , (r->od[c] & RRDR_RESET)?"RESET ":""
- , (r->od[c] & RRDR_HIDDEN)?"HIDDEN ":""
- , (r->od[c] & RRDR_NONZERO)?"NONZERO ":""
- );
- }
-
- if(r->rows <= 0) {
- fprintf(stderr, "RRDR does not have any values in it.\n");
- return;
- }
-
- fprintf(stderr, "RRDR includes %d values in it:\n", r->rows);
-
- // for each line in the array
- for(i = 0; i < r->rows ;i++) {
- calculated_number *cn = &r->v[ i * r->d ];
- uint8_t *co = &r->o[ i * r->d ];
-
- // print the id and the timestamp of the line
- fprintf(stderr, "%ld %ld ", i + 1, r->t[i]);
-
- // for each dimension
- for(c = 0, d = r->st->dimensions; d ;c++, d = d->next) {
- if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
- if(unlikely(!(r->od[c] & RRDR_NONZERO))) continue;
-
- if(co[c] & RRDR_EMPTY)
- fprintf(stderr, "null ");
- else
- fprintf(stderr, CALCULATED_NUMBER_FORMAT " %s%s%s%s "
- , cn[c]
- , (co[c] & RRDR_EMPTY)?"E":" "
- , (co[c] & RRDR_RESET)?"R":" "
- , (co[c] & RRDR_HIDDEN)?"H":" "
- , (co[c] & RRDR_NONZERO)?"N":" "
- );
- }
-
- fprintf(stderr, "\n");
- }
+ long c, i;
+ RRDDIM *d;
+
+ fprintf(stderr, "\nCHART %s (%s)\n", r->st->id, r->st->name);
+
+ for(c = 0, d = r->st->dimensions; d ;c++, d = d->next) {
+ fprintf(stderr, "DIMENSION %s (%s), %s%s%s%s\n"
+ , d->id
+ , d->name
+ , (r->od[c] & RRDR_EMPTY)?"EMPTY ":""
+ , (r->od[c] & RRDR_RESET)?"RESET ":""
+ , (r->od[c] & RRDR_HIDDEN)?"HIDDEN ":""
+ , (r->od[c] & RRDR_NONZERO)?"NONZERO ":""
+ );
+ }
+
+ if(r->rows <= 0) {
+ fprintf(stderr, "RRDR does not have any values in it.\n");
+ return;
+ }
+
+ fprintf(stderr, "RRDR includes %d values in it:\n", r->rows);
+
+ // for each line in the array
+ for(i = 0; i < r->rows ;i++) {
+ calculated_number *cn = &r->v[ i * r->d ];
+ uint8_t *co = &r->o[ i * r->d ];
+
+ // print the id and the timestamp of the line
+ fprintf(stderr, "%ld %ld ", i + 1, r->t[i]);
+
+ // for each dimension
+ for(c = 0, d = r->st->dimensions; d ;c++, d = d->next) {
+ if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
+ if(unlikely(!(r->od[c] & RRDR_NONZERO))) continue;
+
+ if(co[c] & RRDR_EMPTY)
+ fprintf(stderr, "null ");
+ else
+ fprintf(stderr, CALCULATED_NUMBER_FORMAT " %s%s%s%s "
+ , cn[c]
+ , (co[c] & RRDR_EMPTY)?"E":" "
+ , (co[c] & RRDR_RESET)?"R":" "
+ , (co[c] & RRDR_HIDDEN)?"H":" "
+ , (co[c] & RRDR_NONZERO)?"N":" "
+ );
+ }
+
+ fprintf(stderr, "\n");
+ }
}
*/
void rrdr_disable_not_selected_dimensions(RRDR *r, const char *dims)
{
- char b[strlen(dims) + 1];
- char *o = b, *tok;
- strcpy(o, dims);
-
- long c;
- RRDDIM *d;
-
- // disable all of them
- for(c = 0, d = r->st->dimensions; d ;c++, d = d->next)
- r->od[c] |= RRDR_HIDDEN;
-
- while(o && *o && (tok = mystrsep(&o, ", |"))) {
- if(!*tok) continue;
-
- // find it and enable it
- for(c = 0, d = r->st->dimensions; d ;c++, d = d->next) {
- if(!strcmp(d->name, tok)) {
- r->od[c] &= ~RRDR_HIDDEN;
-
- // since the user needs this dimension
- // make it appear as NONZERO, to return it
- // even if the dimension has only zeros
- r->od[c] |= RRDR_NONZERO;
- }
- }
- }
+ char b[strlen(dims) + 1];
+ char *o = b, *tok;
+ strcpy(o, dims);
+
+ long c;
+ RRDDIM *d;
+
+ // disable all of them
+ for(c = 0, d = r->st->dimensions; d ;c++, d = d->next)
+ r->od[c] |= RRDR_HIDDEN;
+
+ while(o && *o && (tok = mystrsep(&o, ",|"))) {
+ if(!*tok) continue;
+
+ uint32_t hash = simple_hash(tok);
+
+ // find it and enable it
+ for(c = 0, d = r->st->dimensions; d ;c++, d = d->next) {
+ if(unlikely((hash == d->hash && !strcmp(d->id, tok)) || !strcmp(d->name, tok))) {
+ r->od[c] &= ~RRDR_HIDDEN;
+
+ // since the user needs this dimension
+ // make it appear as NONZERO, to return it
+ // even if the dimension has only zeros
+ r->od[c] |= RRDR_NONZERO;
+ }
+ }
+ }
}
void rrdr_buffer_print_format(BUFFER *wb, uint32_t format)
{
- switch(format) {
- case DATASOURCE_JSON:
- buffer_strcat(wb, DATASOURCE_FORMAT_JSON);
- break;
-
- case DATASOURCE_DATATABLE_JSON:
- buffer_strcat(wb, DATASOURCE_FORMAT_DATATABLE_JSON);
- break;
-
- case DATASOURCE_DATATABLE_JSONP:
- buffer_strcat(wb, DATASOURCE_FORMAT_DATATABLE_JSONP);
- break;
-
- case DATASOURCE_JSONP:
- buffer_strcat(wb, DATASOURCE_FORMAT_JSONP);
- break;
-
- case DATASOURCE_SSV:
- buffer_strcat(wb, DATASOURCE_FORMAT_SSV);
- break;
-
- case DATASOURCE_CSV:
- buffer_strcat(wb, DATASOURCE_FORMAT_CSV);
- break;
-
- case DATASOURCE_TSV:
- buffer_strcat(wb, DATASOURCE_FORMAT_TSV);
- break;
-
- case DATASOURCE_HTML:
- buffer_strcat(wb, DATASOURCE_FORMAT_HTML);
- break;
-
- case DATASOURCE_JS_ARRAY:
- buffer_strcat(wb, DATASOURCE_FORMAT_JS_ARRAY);
- break;
-
- case DATASOURCE_SSV_COMMA:
- buffer_strcat(wb, DATASOURCE_FORMAT_SSV_COMMA);
- break;
-
- default:
- buffer_strcat(wb, "unknown");
- break;
- }
+ switch(format) {
+ case DATASOURCE_JSON:
+ buffer_strcat(wb, DATASOURCE_FORMAT_JSON);
+ break;
+
+ case DATASOURCE_DATATABLE_JSON:
+ buffer_strcat(wb, DATASOURCE_FORMAT_DATATABLE_JSON);
+ break;
+
+ case DATASOURCE_DATATABLE_JSONP:
+ buffer_strcat(wb, DATASOURCE_FORMAT_DATATABLE_JSONP);
+ break;
+
+ case DATASOURCE_JSONP:
+ buffer_strcat(wb, DATASOURCE_FORMAT_JSONP);
+ break;
+
+ case DATASOURCE_SSV:
+ buffer_strcat(wb, DATASOURCE_FORMAT_SSV);
+ break;
+
+ case DATASOURCE_CSV:
+ buffer_strcat(wb, DATASOURCE_FORMAT_CSV);
+ break;
+
+ case DATASOURCE_TSV:
+ buffer_strcat(wb, DATASOURCE_FORMAT_TSV);
+ break;
+
+ case DATASOURCE_HTML:
+ buffer_strcat(wb, DATASOURCE_FORMAT_HTML);
+ break;
+
+ case DATASOURCE_JS_ARRAY:
+ buffer_strcat(wb, DATASOURCE_FORMAT_JS_ARRAY);
+ break;
+
+ case DATASOURCE_SSV_COMMA:
+ buffer_strcat(wb, DATASOURCE_FORMAT_SSV_COMMA);
+ break;
+
+ default:
+ buffer_strcat(wb, "unknown");
+ break;
+ }
}
uint32_t rrdr_check_options(RRDR *r, uint32_t options, const char *dims)
{
- if(options & RRDR_OPTION_NONZERO) {
- long i;
-
- if(dims && *dims) {
- // the caller wants specific dimensions
- // disable NONZERO option
- // to make sure we don't accidentally prevent
- // the specific dimensions from being returned
- i = 0;
- }
- else {
- // find how many dimensions are not zero
- long c;
- RRDDIM *rd;
- for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ; c++, rd = rd->next) {
- if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
- if(unlikely(!(r->od[c] & RRDR_NONZERO))) continue;
- i++;
- }
- }
-
- // if with nonzero we get i = 0 (no dimensions will be returned)
- // disable nonzero to show all dimensions
- if(!i) options &= ~RRDR_OPTION_NONZERO;
- }
-
- return options;
+ if(options & RRDR_OPTION_NONZERO) {
+ long i;
+
+ if(dims && *dims) {
+ // the caller wants specific dimensions
+ // disable NONZERO option
+ // to make sure we don't accidentally prevent
+ // the specific dimensions from being returned
+ i = 0;
+ }
+ else {
+ // find how many dimensions are not zero
+ long c;
+ RRDDIM *rd;
+ for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ; c++, rd = rd->next) {
+ if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
+ if(unlikely(!(r->od[c] & RRDR_NONZERO))) continue;
+ i++;
+ }
+ }
+
+ // if with nonzero we get i = 0 (no dimensions will be returned)
+ // disable nonzero to show all dimensions
+ if(!i) options &= ~RRDR_OPTION_NONZERO;
+ }
+
+ return options;
}
void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, uint32_t options, int string_value)
{
- long rows = rrdr_rows(r);
- long c, i;
- RRDDIM *rd;
-
- //info("JSONWRAPPER(): %s: BEGIN", r->st->id);
- char kq[2] = "", // key quote
- sq[2] = ""; // string quote
-
- if( options & RRDR_OPTION_GOOGLE_JSON ) {
- kq[0] = '\0';
- sq[0] = '\'';
- }
- else {
- kq[0] = '"';
- sq[0] = '"';
- }
-
- buffer_sprintf(wb, "{\n"
- " %sapi%s: 1,\n"
- " %sid%s: %s%s%s,\n"
- " %sname%s: %s%s%s,\n"
- " %sview_update_every%s: %d,\n"
- " %supdate_every%s: %d,\n"
- " %sfirst_entry%s: %u,\n"
- " %slast_entry%s: %u,\n"
- " %sbefore%s: %u,\n"
- " %safter%s: %u,\n"
- " %sdimension_names%s: ["
- , kq, kq
- , kq, kq, sq, r->st->id, sq
- , kq, kq, sq, r->st->name, sq
- , kq, kq, r->update_every
- , kq, kq, r->st->update_every
- , kq, kq, rrdset_first_entry_t(r->st)
- , kq, kq, rrdset_last_entry_t(r->st)
- , kq, kq, r->before
- , kq, kq, r->after
- , kq, kq);
-
- for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
- if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
- if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
-
- if(i) buffer_strcat(wb, ", ");
- buffer_strcat(wb, sq);
- buffer_strcat(wb, rd->name);
- buffer_strcat(wb, sq);
- i++;
- }
- if(!i) {
+ long rows = rrdr_rows(r);
+ long c, i;
+ RRDDIM *rd;
+
+ //info("JSONWRAPPER(): %s: BEGIN", r->st->id);
+ char kq[2] = "", // key quote
+ sq[2] = ""; // string quote
+
+ if( options & RRDR_OPTION_GOOGLE_JSON ) {
+ kq[0] = '\0';
+ sq[0] = '\'';
+ }
+ else {
+ kq[0] = '"';
+ sq[0] = '"';
+ }
+
+ buffer_sprintf(wb, "{\n"
+ " %sapi%s: 1,\n"
+ " %sid%s: %s%s%s,\n"
+ " %sname%s: %s%s%s,\n"
+ " %sview_update_every%s: %d,\n"
+ " %supdate_every%s: %d,\n"
+ " %sfirst_entry%s: %u,\n"
+ " %slast_entry%s: %u,\n"
+ " %sbefore%s: %u,\n"
+ " %safter%s: %u,\n"
+ " %sdimension_names%s: ["
+ , kq, kq
+ , kq, kq, sq, r->st->id, sq
+ , kq, kq, sq, r->st->name, sq
+ , kq, kq, r->update_every
+ , kq, kq, r->st->update_every
+ , kq, kq, (uint32_t)rrdset_first_entry_t(r->st)
+ , kq, kq, (uint32_t)rrdset_last_entry_t(r->st)
+ , kq, kq, (uint32_t)r->before
+ , kq, kq, (uint32_t)r->after
+ , kq, kq);
+
+ for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
+ if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
+ if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
+
+ if(i) buffer_strcat(wb, ", ");
+ buffer_strcat(wb, sq);
+ buffer_strcat(wb, rd->name);
+ buffer_strcat(wb, sq);
+ i++;
+ }
+ if(!i) {
#ifdef NETDATA_INTERNAL_CHECKS
- error("RRDR is empty for %s (RRDR has %d dimensions, options is 0x%08x)", r->st->id, r->d, options);
+ error("RRDR is empty for %s (RRDR has %d dimensions, options is 0x%08x)", r->st->id, r->d, options);
#endif
- rows = 0;
- buffer_strcat(wb, sq);
- buffer_strcat(wb, "no data");
- buffer_strcat(wb, sq);
- }
-
- buffer_sprintf(wb, "],\n"
- " %sdimension_ids%s: ["
- , kq, kq);
-
- for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
- if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
- if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
-
- if(i) buffer_strcat(wb, ", ");
- buffer_strcat(wb, sq);
- buffer_strcat(wb, rd->id);
- buffer_strcat(wb, sq);
- i++;
- }
- if(!i) {
- rows = 0;
- buffer_strcat(wb, sq);
- buffer_strcat(wb, "no data");
- buffer_strcat(wb, sq);
- }
-
- buffer_sprintf(wb, "],\n"
- " %slatest_values%s: ["
- , kq, kq);
-
- for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
- if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
- if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
-
- if(i) buffer_strcat(wb, ", ");
- i++;
-
- storage_number n = rd->values[rrdset_last_slot(r->st)];
-
- if(!does_storage_number_exist(n))
- buffer_strcat(wb, "null");
- else
- buffer_rrd_value(wb, unpack_storage_number(n));
- }
- if(!i) {
- rows = 0;
- buffer_strcat(wb, "null");
- }
-
- buffer_sprintf(wb, "],\n"
- " %sview_latest_values%s: ["
- , kq, kq);
-
- i = 0;
- if(rows) {
- for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
- if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
- if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
-
- if(i) buffer_strcat(wb, ", ");
- i++;
-
- calculated_number *cn = &r->v[ (0) * r->d ];
- uint8_t *co = &r->o[ (0) * r->d ];
-
- if(co[c] & RRDR_EMPTY)
- buffer_strcat(wb, "null");
- else
- buffer_rrd_value(wb, cn[c]);
- }
- }
- if(!i) {
- rows = 0;
- buffer_strcat(wb, "null");
- }
-
- buffer_sprintf(wb, "],\n"
- " %sdimensions%s: %d,\n"
- " %spoints%s: %d,\n"
- " %sformat%s: %s"
- , kq, kq, i
- , kq, kq, rows
- , kq, kq, sq
- );
-
- rrdr_buffer_print_format(wb, format);
-
- buffer_sprintf(wb, "%s,\n"
- " %sresult%s: "
- , sq
- , kq, kq
- );
-
- if(string_value) buffer_strcat(wb, sq);
- //info("JSONWRAPPER(): %s: END", r->st->id);
+ rows = 0;
+ buffer_strcat(wb, sq);
+ buffer_strcat(wb, "no data");
+ buffer_strcat(wb, sq);
+ }
+
+ buffer_sprintf(wb, "],\n"
+ " %sdimension_ids%s: ["
+ , kq, kq);
+
+ for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
+ if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
+ if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
+
+ if(i) buffer_strcat(wb, ", ");
+ buffer_strcat(wb, sq);
+ buffer_strcat(wb, rd->id);
+ buffer_strcat(wb, sq);
+ i++;
+ }
+ if(!i) {
+ rows = 0;
+ buffer_strcat(wb, sq);
+ buffer_strcat(wb, "no data");
+ buffer_strcat(wb, sq);
+ }
+
+ buffer_sprintf(wb, "],\n"
+ " %slatest_values%s: ["
+ , kq, kq);
+
+ for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
+ if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
+ if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
+
+ if(i) buffer_strcat(wb, ", ");
+ i++;
+
+ storage_number n = rd->values[rrdset_last_slot(r->st)];
+
+ if(!does_storage_number_exist(n))
+ buffer_strcat(wb, "null");
+ else
+ buffer_rrd_value(wb, unpack_storage_number(n));
+ }
+ if(!i) {
+ rows = 0;
+ buffer_strcat(wb, "null");
+ }
+
+ buffer_sprintf(wb, "],\n"
+ " %sview_latest_values%s: ["
+ , kq, kq);
+
+ i = 0;
+ if(rows) {
+ for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
+ if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
+ if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
+
+ if(i) buffer_strcat(wb, ", ");
+ i++;
+
+ calculated_number *cn = &r->v[ (0) * r->d ];
+ uint8_t *co = &r->o[ (0) * r->d ];
+
+ if(co[c] & RRDR_EMPTY)
+ buffer_strcat(wb, "null");
+ else
+ buffer_rrd_value(wb, cn[c]);
+ }
+ }
+ if(!i) {
+ rows = 0;
+ buffer_strcat(wb, "null");
+ }
+
+ buffer_sprintf(wb, "],\n"
+ " %sdimensions%s: %ld,\n"
+ " %spoints%s: %ld,\n"
+ " %sformat%s: %s"
+ , kq, kq, i
+ , kq, kq, rows
+ , kq, kq, sq
+ );
+
+ rrdr_buffer_print_format(wb, format);
+
+ buffer_sprintf(wb, "%s,\n"
+ " %sresult%s: "
+ , sq
+ , kq, kq
+ );
+
+ if(string_value) buffer_strcat(wb, sq);
+ //info("JSONWRAPPER(): %s: END", r->st->id);
}
void rrdr_json_wrapper_end(RRDR *r, BUFFER *wb, uint32_t format, uint32_t options, int string_value)
{
- if(r) {;}
- if(format) {;}
-
- char kq[2] = "", // key quote
- sq[2] = ""; // string quote
-
- if( options & RRDR_OPTION_GOOGLE_JSON ) {
- kq[0] = '\0';
- sq[0] = '\'';
- }
- else {
- kq[0] = '"';
- sq[0] = '"';
- }
-
- if(string_value) buffer_strcat(wb, sq);
-
- buffer_sprintf(wb, ",\n %smin%s: ", kq, kq);
- buffer_rrd_value(wb, r->min);
- buffer_sprintf(wb, ",\n %smax%s: ", kq, kq);
- buffer_rrd_value(wb, r->max);
- buffer_strcat(wb, "\n}\n");
+ (void)format;
+
+ char kq[2] = "", // key quote
+ sq[2] = ""; // string quote
+
+ if( options & RRDR_OPTION_GOOGLE_JSON ) {
+ kq[0] = '\0';
+ sq[0] = '\'';
+ }
+ else {
+ kq[0] = '"';
+ sq[0] = '"';
+ }
+
+ if(string_value) buffer_strcat(wb, sq);
+
+ buffer_sprintf(wb, ",\n %smin%s: ", kq, kq);
+ buffer_rrd_value(wb, r->min);
+ buffer_sprintf(wb, ",\n %smax%s: ", kq, kq);
+ buffer_rrd_value(wb, r->max);
+ buffer_strcat(wb, "\n}\n");
}
#define JSON_DATES_JS 1
@@ -655,1356 +649,1435 @@ void rrdr_json_wrapper_end(RRDR *r, BUFFER *wb, uint32_t format, uint32_t option
static void rrdr2json(RRDR *r, BUFFER *wb, uint32_t options, int datatable)
{
- //info("RRD2JSON(): %s: BEGIN", r->st->id);
- int row_annotations = 0, dates, dates_with_new = 0;
- char kq[2] = "", // key quote
- sq[2] = "", // string quote
- pre_label[101] = "", // before each label
- post_label[101] = "", // after each label
- pre_date[101] = "", // the beginning of line, to the date
- post_date[101] = "", // closing the date
- pre_value[101] = "", // before each value
- post_value[101] = "", // after each value
- post_line[101] = "", // at the end of each row
- normal_annotation[201] = "", // default row annotation
- overflow_annotation[201] = "", // overflow row annotation
- data_begin[101] = "", // between labels and values
- finish[101] = ""; // at the end of everything
-
- if(datatable) {
- dates = JSON_DATES_JS;
- if( options & RRDR_OPTION_GOOGLE_JSON ) {
- kq[0] = '\0';
- sq[0] = '\'';
- }
- else {
- kq[0] = '"';
- sq[0] = '"';
- }
- row_annotations = 1;
- snprintfz(pre_date, 100, " {%sc%s:[{%sv%s:%s", kq, kq, kq, kq, sq);
- snprintfz(post_date, 100, "%s}", sq);
- snprintfz(pre_label, 100, ",\n {%sid%s:%s%s,%slabel%s:%s", kq, kq, sq, sq, kq, kq, sq);
- snprintfz(post_label, 100, "%s,%spattern%s:%s%s,%stype%s:%snumber%s}", sq, kq, kq, sq, sq, kq, kq, sq, sq);
- snprintfz(pre_value, 100, ",{%sv%s:", kq, kq);
- strcpy(post_value, "}");
- strcpy(post_line, "]}");
- snprintfz(data_begin, 100, "\n ],\n %srows%s:\n [\n", kq, kq);
- strcpy(finish, "\n ]\n}");
-
- snprintfz(overflow_annotation, 200, ",{%sv%s:%sRESET OR OVERFLOW%s},{%sv%s:%sThe counters have been wrapped.%s}", kq, kq, sq, sq, kq, kq, sq, sq);
- snprintfz(normal_annotation, 200, ",{%sv%s:null},{%sv%s:null}", kq, kq, kq, kq);
-
- buffer_sprintf(wb, "{\n %scols%s:\n [\n", kq, kq, kq, kq);
- buffer_sprintf(wb, " {%sid%s:%s%s,%slabel%s:%stime%s,%spattern%s:%s%s,%stype%s:%sdatetime%s},\n", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq);
- buffer_sprintf(wb, " {%sid%s:%s%s,%slabel%s:%s%s,%spattern%s:%s%s,%stype%s:%sstring%s,%sp%s:{%srole%s:%sannotation%s}},\n", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, kq, kq, sq, sq);
- buffer_sprintf(wb, " {%sid%s:%s%s,%slabel%s:%s%s,%spattern%s:%s%s,%stype%s:%sstring%s,%sp%s:{%srole%s:%sannotationText%s}}", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, kq, kq, sq, sq);
-
- // remove the valueobjects flag
- // google wants its own keys
- if(options & RRDR_OPTION_OBJECTSROWS)
- options &= ~RRDR_OPTION_OBJECTSROWS;
- }
- else {
- kq[0] = '"';
- sq[0] = '"';
- if((options & RRDR_OPTION_SECONDS) || (options & RRDR_OPTION_MILLISECONDS)) {
- dates = JSON_DATES_TIMESTAMP;
- dates_with_new = 0;
- }
- else {
- dates = JSON_DATES_JS;
- dates_with_new = 1;
- }
- if( options & RRDR_OPTION_OBJECTSROWS )
- strcpy(pre_date, " { ");
- else
- strcpy(pre_date, " [ ");
- strcpy(pre_label, ", \"");
- strcpy(post_label, "\"");
- strcpy(pre_value, ", ");
- if( options & RRDR_OPTION_OBJECTSROWS )
- strcpy(post_line, "}");
- else
- strcpy(post_line, "]");
- snprintfz(data_begin, 100, "],\n %sdata%s:\n [\n", kq, kq);
- strcpy(finish, "\n ]\n}");
-
- buffer_sprintf(wb, "{\n %slabels%s: [", kq, kq);
- buffer_sprintf(wb, "%stime%s", sq, sq);
- }
-
- // -------------------------------------------------------------------------
- // print the JSON header
-
- long c, i;
- RRDDIM *rd;
-
- // print the header lines
- for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
- if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
- if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
-
- buffer_strcat(wb, pre_label);
- buffer_strcat(wb, rd->name);
- buffer_strcat(wb, post_label);
- i++;
- }
- if(!i) {
- buffer_strcat(wb, pre_label);
- buffer_strcat(wb, "no data");
- buffer_strcat(wb, post_label);
- }
-
- // print the begin of row data
- buffer_strcat(wb, data_begin);
-
- // if all dimensions are hidden, print a null
- if(!i) {
- buffer_strcat(wb, finish);
- return;
- }
-
- long start = 0, end = rrdr_rows(r), step = 1;
- if((options & RRDR_OPTION_REVERSED)) {
- start = rrdr_rows(r) - 1;
- end = -1;
- step = -1;
- }
-
- // for each line in the array
- calculated_number total = 1;
- for(i = start; i != end ;i += step) {
- calculated_number *cn = &r->v[ i * r->d ];
- uint8_t *co = &r->o[ i * r->d ];
-
- time_t now = r->t[i];
-
- if(dates == JSON_DATES_JS) {
- // generate the local date time
- struct tm tmbuf, *tm = localtime_r(&now, &tmbuf);
- if(!tm) { error("localtime_r() failed."); continue; }
-
- if(likely(i != start)) buffer_strcat(wb, ",\n");
- buffer_strcat(wb, pre_date);
-
- if( options & RRDR_OPTION_OBJECTSROWS )
- buffer_sprintf(wb, "%stime%s: ", kq, kq);
-
- if(dates_with_new)
- buffer_strcat(wb, "new ");
-
- buffer_jsdate(wb, tm->tm_year + 1900, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
-
- buffer_strcat(wb, post_date);
-
- if(row_annotations) {
- // google supports one annotation per row
- int annotation_found = 0;
- for(c = 0, rd = r->st->dimensions; rd ;c++, rd = rd->next) {
- if(co[c] & RRDR_RESET) {
- buffer_strcat(wb, overflow_annotation);
- annotation_found = 1;
- break;
- }
- }
- if(!annotation_found)
- buffer_strcat(wb, normal_annotation);
- }
- }
- else {
- // print the timestamp of the line
- if(likely(i != start)) buffer_strcat(wb, ",\n");
- buffer_strcat(wb, pre_date);
-
- if( options & RRDR_OPTION_OBJECTSROWS )
- buffer_sprintf(wb, "%stime%s: ", kq, kq);
-
- buffer_rrd_value(wb, (calculated_number)r->t[i]);
- // in ms
- if(options & RRDR_OPTION_MILLISECONDS) buffer_strcat(wb, "000");
-
- buffer_strcat(wb, post_date);
- }
-
- if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
- total = 0;
- for(c = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
- calculated_number n = cn[c];
-
- if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
- n = -n;
-
- total += n;
- }
- // prevent a division by zero
- if(total == 0) total = 1;
- }
-
- // for each dimension
- for(c = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
- if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
- if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
-
- calculated_number n = cn[c];
-
- buffer_strcat(wb, pre_value);
-
- if( options & RRDR_OPTION_OBJECTSROWS )
- buffer_sprintf(wb, "%s%s%s: ", kq, rd->name, kq);
-
- if(co[c] & RRDR_EMPTY) {
- if(options & RRDR_OPTION_NULL2ZERO)
- buffer_strcat(wb, "0");
- else
- buffer_strcat(wb, "null");
- }
- else {
- if(unlikely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
- n = -n;
-
- if(unlikely(options & RRDR_OPTION_PERCENTAGE))
- n = n * 100 / total;
-
- buffer_rrd_value(wb, n);
- }
-
- buffer_strcat(wb, post_value);
- }
-
- buffer_strcat(wb, post_line);
- }
-
- buffer_strcat(wb, finish);
- //info("RRD2JSON(): %s: END", r->st->id);
+ //info("RRD2JSON(): %s: BEGIN", r->st->id);
+ int row_annotations = 0, dates, dates_with_new = 0;
+ char kq[2] = "", // key quote
+ sq[2] = "", // string quote
+ pre_label[101] = "", // before each label
+ post_label[101] = "", // after each label
+ pre_date[101] = "", // the beginning of line, to the date
+ post_date[101] = "", // closing the date
+ pre_value[101] = "", // before each value
+ post_value[101] = "", // after each value
+ post_line[101] = "", // at the end of each row
+ normal_annotation[201] = "", // default row annotation
+ overflow_annotation[201] = "", // overflow row annotation
+ data_begin[101] = "", // between labels and values
+ finish[101] = ""; // at the end of everything
+
+ if(datatable) {
+ dates = JSON_DATES_JS;
+ if( options & RRDR_OPTION_GOOGLE_JSON ) {
+ kq[0] = '\0';
+ sq[0] = '\'';
+ }
+ else {
+ kq[0] = '"';
+ sq[0] = '"';
+ }
+ row_annotations = 1;
+ snprintfz(pre_date, 100, " {%sc%s:[{%sv%s:%s", kq, kq, kq, kq, sq);
+ snprintfz(post_date, 100, "%s}", sq);
+ snprintfz(pre_label, 100, ",\n {%sid%s:%s%s,%slabel%s:%s", kq, kq, sq, sq, kq, kq, sq);
+ snprintfz(post_label, 100, "%s,%spattern%s:%s%s,%stype%s:%snumber%s}", sq, kq, kq, sq, sq, kq, kq, sq, sq);
+ snprintfz(pre_value, 100, ",{%sv%s:", kq, kq);
+ strcpy(post_value, "}");
+ strcpy(post_line, "]}");
+ snprintfz(data_begin, 100, "\n ],\n %srows%s:\n [\n", kq, kq);
+ strcpy(finish, "\n ]\n}");
+
+ snprintfz(overflow_annotation, 200, ",{%sv%s:%sRESET OR OVERFLOW%s},{%sv%s:%sThe counters have been wrapped.%s}", kq, kq, sq, sq, kq, kq, sq, sq);
+ snprintfz(normal_annotation, 200, ",{%sv%s:null},{%sv%s:null}", kq, kq, kq, kq);
+
+ buffer_sprintf(wb, "{\n %scols%s:\n [\n", kq, kq);
+ buffer_sprintf(wb, " {%sid%s:%s%s,%slabel%s:%stime%s,%spattern%s:%s%s,%stype%s:%sdatetime%s},\n", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq);
+ buffer_sprintf(wb, " {%sid%s:%s%s,%slabel%s:%s%s,%spattern%s:%s%s,%stype%s:%sstring%s,%sp%s:{%srole%s:%sannotation%s}},\n", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, kq, kq, sq, sq);
+ buffer_sprintf(wb, " {%sid%s:%s%s,%slabel%s:%s%s,%spattern%s:%s%s,%stype%s:%sstring%s,%sp%s:{%srole%s:%sannotationText%s}}", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, kq, kq, sq, sq);
+
+ // remove the valueobjects flag
+ // google wants its own keys
+ if(options & RRDR_OPTION_OBJECTSROWS)
+ options &= ~RRDR_OPTION_OBJECTSROWS;
+ }
+ else {
+ kq[0] = '"';
+ sq[0] = '"';
+ if((options & RRDR_OPTION_SECONDS) || (options & RRDR_OPTION_MILLISECONDS)) {
+ dates = JSON_DATES_TIMESTAMP;
+ dates_with_new = 0;
+ }
+ else {
+ dates = JSON_DATES_JS;
+ dates_with_new = 1;
+ }
+ if( options & RRDR_OPTION_OBJECTSROWS )
+ strcpy(pre_date, " { ");
+ else
+ strcpy(pre_date, " [ ");
+ strcpy(pre_label, ", \"");
+ strcpy(post_label, "\"");
+ strcpy(pre_value, ", ");
+ if( options & RRDR_OPTION_OBJECTSROWS )
+ strcpy(post_line, "}");
+ else
+ strcpy(post_line, "]");
+ snprintfz(data_begin, 100, "],\n %sdata%s:\n [\n", kq, kq);
+ strcpy(finish, "\n ]\n}");
+
+ buffer_sprintf(wb, "{\n %slabels%s: [", kq, kq);
+ buffer_sprintf(wb, "%stime%s", sq, sq);
+ }
+
+ // -------------------------------------------------------------------------
+ // print the JSON header
+
+ long c, i;
+ RRDDIM *rd;
+
+ // print the header lines
+ for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
+ if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
+ if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
+
+ buffer_strcat(wb, pre_label);
+ buffer_strcat(wb, rd->name);
+ buffer_strcat(wb, post_label);
+ i++;
+ }
+ if(!i) {
+ buffer_strcat(wb, pre_label);
+ buffer_strcat(wb, "no data");
+ buffer_strcat(wb, post_label);
+ }
+
+ // print the begin of row data
+ buffer_strcat(wb, data_begin);
+
+ // if all dimensions are hidden, print a null
+ if(!i) {
+ buffer_strcat(wb, finish);
+ return;
+ }
+
+ long start = 0, end = rrdr_rows(r), step = 1;
+ if((options & RRDR_OPTION_REVERSED)) {
+ start = rrdr_rows(r) - 1;
+ end = -1;
+ step = -1;
+ }
+
+ // for each line in the array
+ calculated_number total = 1;
+ for(i = start; i != end ;i += step) {
+ calculated_number *cn = &r->v[ i * r->d ];
+ uint8_t *co = &r->o[ i * r->d ];
+
+ time_t now = r->t[i];
+
+ if(dates == JSON_DATES_JS) {
+ // generate the local date time
+ struct tm tmbuf, *tm = localtime_r(&now, &tmbuf);
+ if(!tm) { error("localtime_r() failed."); continue; }
+
+ if(likely(i != start)) buffer_strcat(wb, ",\n");
+ buffer_strcat(wb, pre_date);
+
+ if( options & RRDR_OPTION_OBJECTSROWS )
+ buffer_sprintf(wb, "%stime%s: ", kq, kq);
+
+ if(dates_with_new)
+ buffer_strcat(wb, "new ");
+
+ buffer_jsdate(wb, tm->tm_year + 1900, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+ buffer_strcat(wb, post_date);
+
+ if(row_annotations) {
+ // google supports one annotation per row
+ int annotation_found = 0;
+ for(c = 0, rd = r->st->dimensions; rd ;c++, rd = rd->next) {
+ if(co[c] & RRDR_RESET) {
+ buffer_strcat(wb, overflow_annotation);
+ annotation_found = 1;
+ break;
+ }
+ }
+ if(!annotation_found)
+ buffer_strcat(wb, normal_annotation);
+ }
+ }
+ else {
+ // print the timestamp of the line
+ if(likely(i != start)) buffer_strcat(wb, ",\n");
+ buffer_strcat(wb, pre_date);
+
+ if( options & RRDR_OPTION_OBJECTSROWS )
+ buffer_sprintf(wb, "%stime%s: ", kq, kq);
+
+ buffer_rrd_value(wb, (calculated_number)r->t[i]);
+ // in ms
+ if(options & RRDR_OPTION_MILLISECONDS) buffer_strcat(wb, "000");
+
+ buffer_strcat(wb, post_date);
+ }
+
+ if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
+ total = 0;
+ for(c = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
+ calculated_number n = cn[c];
+
+ if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
+ n = -n;
+
+ total += n;
+ }
+ // prevent a division by zero
+ if(total == 0) total = 1;
+ }
+
+ // for each dimension
+ for(c = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
+ if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
+ if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
+
+ calculated_number n = cn[c];
+
+ buffer_strcat(wb, pre_value);
+
+ if( options & RRDR_OPTION_OBJECTSROWS )
+ buffer_sprintf(wb, "%s%s%s: ", kq, rd->name, kq);
+
+ if(co[c] & RRDR_EMPTY) {
+ if(options & RRDR_OPTION_NULL2ZERO)
+ buffer_strcat(wb, "0");
+ else
+ buffer_strcat(wb, "null");
+ }
+ else {
+ if(unlikely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
+ n = -n;
+
+ if(unlikely(options & RRDR_OPTION_PERCENTAGE))
+ n = n * 100 / total;
+
+ buffer_rrd_value(wb, n);
+ }
+
+ buffer_strcat(wb, post_value);
+ }
+
+ buffer_strcat(wb, post_line);
+ }
+
+ buffer_strcat(wb, finish);
+ //info("RRD2JSON(): %s: END", r->st->id);
}
static void rrdr2csv(RRDR *r, BUFFER *wb, uint32_t options, const char *startline, const char *separator, const char *endline, const char *betweenlines)
{
- //info("RRD2CSV(): %s: BEGIN", r->st->id);
- long c, i;
- RRDDIM *d;
-
- // print the csv header
- for(c = 0, i = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
- if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
- if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
-
- if(!i) {
- buffer_strcat(wb, startline);
- if(options & RRDR_OPTION_LABEL_QUOTES) buffer_strcat(wb, "\"");
- buffer_strcat(wb, "time");
- if(options & RRDR_OPTION_LABEL_QUOTES) buffer_strcat(wb, "\"");
- }
- buffer_strcat(wb, separator);
- if(options & RRDR_OPTION_LABEL_QUOTES) buffer_strcat(wb, "\"");
- buffer_strcat(wb, d->name);
- if(options & RRDR_OPTION_LABEL_QUOTES) buffer_strcat(wb, "\"");
- i++;
- }
- buffer_strcat(wb, endline);
-
- if(!i) {
- // no dimensions present
- return;
- }
-
- long start = 0, end = rrdr_rows(r), step = 1;
- if((options & RRDR_OPTION_REVERSED)) {
- start = rrdr_rows(r) - 1;
- end = -1;
- step = -1;
- }
-
- // for each line in the array
- calculated_number total = 1;
- for(i = start; i != end ;i += step) {
- calculated_number *cn = &r->v[ i * r->d ];
- uint8_t *co = &r->o[ i * r->d ];
-
- buffer_strcat(wb, betweenlines);
- buffer_strcat(wb, startline);
-
- time_t now = r->t[i];
-
- if((options & RRDR_OPTION_SECONDS) || (options & RRDR_OPTION_MILLISECONDS)) {
- // print the timestamp of the line
- buffer_rrd_value(wb, (calculated_number)now);
- // in ms
- if(options & RRDR_OPTION_MILLISECONDS) buffer_strcat(wb, "000");
- }
- else {
- // generate the local date time
- struct tm tmbuf, *tm = localtime_r(&now, &tmbuf);
- if(!tm) { error("localtime() failed."); continue; }
- buffer_date(wb, tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
- }
-
- if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
- total = 0;
- for(c = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
- calculated_number n = cn[c];
-
- if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
- n = -n;
-
- total += n;
- }
- // prevent a division by zero
- if(total == 0) total = 1;
- }
-
- // for each dimension
- for(c = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
- if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
- if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
-
- buffer_strcat(wb, separator);
-
- calculated_number n = cn[c];
-
- if(co[c] & RRDR_EMPTY) {
- if(options & RRDR_OPTION_NULL2ZERO)
- buffer_strcat(wb, "0");
- else
- buffer_strcat(wb, "null");
- }
- else {
- if(unlikely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
- n = -n;
-
- if(unlikely(options & RRDR_OPTION_PERCENTAGE))
- n = n * 100 / total;
-
- buffer_rrd_value(wb, n);
- }
- }
-
- buffer_strcat(wb, endline);
- }
- //info("RRD2CSV(): %s: END", r->st->id);
+ //info("RRD2CSV(): %s: BEGIN", r->st->id);
+ long c, i;
+ RRDDIM *d;
+
+ // print the csv header
+ for(c = 0, i = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
+ if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
+ if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
+
+ if(!i) {
+ buffer_strcat(wb, startline);
+ if(options & RRDR_OPTION_LABEL_QUOTES) buffer_strcat(wb, "\"");
+ buffer_strcat(wb, "time");
+ if(options & RRDR_OPTION_LABEL_QUOTES) buffer_strcat(wb, "\"");
+ }
+ buffer_strcat(wb, separator);
+ if(options & RRDR_OPTION_LABEL_QUOTES) buffer_strcat(wb, "\"");
+ buffer_strcat(wb, d->name);
+ if(options & RRDR_OPTION_LABEL_QUOTES) buffer_strcat(wb, "\"");
+ i++;
+ }
+ buffer_strcat(wb, endline);
+
+ if(!i) {
+ // no dimensions present
+ return;
+ }
+
+ long start = 0, end = rrdr_rows(r), step = 1;
+ if((options & RRDR_OPTION_REVERSED)) {
+ start = rrdr_rows(r) - 1;
+ end = -1;
+ step = -1;
+ }
+
+ // for each line in the array
+ calculated_number total = 1;
+ for(i = start; i != end ;i += step) {
+ calculated_number *cn = &r->v[ i * r->d ];
+ uint8_t *co = &r->o[ i * r->d ];
+
+ buffer_strcat(wb, betweenlines);
+ buffer_strcat(wb, startline);
+
+ time_t now = r->t[i];
+
+ if((options & RRDR_OPTION_SECONDS) || (options & RRDR_OPTION_MILLISECONDS)) {
+ // print the timestamp of the line
+ buffer_rrd_value(wb, (calculated_number)now);
+ // in ms
+ if(options & RRDR_OPTION_MILLISECONDS) buffer_strcat(wb, "000");
+ }
+ else {
+ // generate the local date time
+ struct tm tmbuf, *tm = localtime_r(&now, &tmbuf);
+ if(!tm) { error("localtime() failed."); continue; }
+ buffer_date(wb, tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
+ }
+
+ if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
+ total = 0;
+ for(c = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
+ calculated_number n = cn[c];
+
+ if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
+ n = -n;
+
+ total += n;
+ }
+ // prevent a division by zero
+ if(total == 0) total = 1;
+ }
+
+ // for each dimension
+ for(c = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
+ if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
+ if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
+
+ buffer_strcat(wb, separator);
+
+ calculated_number n = cn[c];
+
+ if(co[c] & RRDR_EMPTY) {
+ if(options & RRDR_OPTION_NULL2ZERO)
+ buffer_strcat(wb, "0");
+ else
+ buffer_strcat(wb, "null");
+ }
+ else {
+ if(unlikely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
+ n = -n;
+
+ if(unlikely(options & RRDR_OPTION_PERCENTAGE))
+ n = n * 100 / total;
+
+ buffer_rrd_value(wb, n);
+ }
+ }
+
+ buffer_strcat(wb, endline);
+ }
+ //info("RRD2CSV(): %s: END", r->st->id);
+}
+
+inline static calculated_number rrdr2value(RRDR *r, long i, uint32_t options, int *all_values_are_null) {
+ long c;
+ RRDDIM *d;
+
+ calculated_number *cn = &r->v[ i * r->d ];
+ uint8_t *co = &r->o[ i * r->d ];
+
+ calculated_number sum = 0, min = 0, max = 0, v;
+ int all_null = 1, init = 1;
+
+ calculated_number total = 1;
+ if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
+ total = 0;
+ for(c = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
+ calculated_number n = cn[c];
+
+ if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
+ n = -n;
+
+ total += n;
+ }
+ // prevent a division by zero
+ if(total == 0) total = 1;
+ }
+
+ // for each dimension
+ for(c = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
+ if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
+ if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
+
+ calculated_number n = cn[c];
+
+ if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
+ n = -n;
+
+ if(unlikely(options & RRDR_OPTION_PERCENTAGE))
+ n = n * 100 / total;
+
+ if(unlikely(init)) {
+ if(n > 0) {
+ min = 0;
+ max = n;
+ }
+ else {
+ min = n;
+ max = 0;
+ }
+ init = 0;
+ }
+
+ if(likely(!(co[c] & RRDR_EMPTY))) {
+ all_null = 0;
+ sum += n;
+ }
+
+ if(n < min) min = n;
+ if(n > max) max = n;
+ }
+
+ if(unlikely(all_null)) {
+ if(likely(all_values_are_null))
+ *all_values_are_null = 1;
+ return 0;
+ }
+ else {
+ if(likely(all_values_are_null))
+ *all_values_are_null = 0;
+ }
+
+ if(options & RRDR_OPTION_MIN2MAX)
+ v = max - min;
+ else
+ v = sum;
+
+ return v;
}
static void rrdr2ssv(RRDR *r, BUFFER *wb, uint32_t options, const char *prefix, const char *separator, const char *suffix)
{
- //info("RRD2SSV(): %s: BEGIN", r->st->id);
- long c, i;
- RRDDIM *d;
-
- buffer_strcat(wb, prefix);
- long start = 0, end = rrdr_rows(r), step = 1;
- if((options & RRDR_OPTION_REVERSED)) {
- start = rrdr_rows(r) - 1;
- end = -1;
- step = -1;
- }
-
- // for each line in the array
- calculated_number total = 1;
- for(i = start; i != end ;i += step) {
-
- calculated_number *cn = &r->v[ i * r->d ];
- uint8_t *co = &r->o[ i * r->d ];
-
- calculated_number sum = 0, min = 0, max = 0, v;
- int all_null = 1, init = 1;
-
- if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
- total = 0;
- for(c = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
- calculated_number n = cn[c];
-
- if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
- n = -n;
-
- total += n;
- }
- // prevent a division by zero
- if(total == 0) total = 1;
- }
-
- // for each dimension
- for(c = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
- if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
- if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
-
- calculated_number n = cn[c];
-
- if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
- n = -n;
-
- if(unlikely(options & RRDR_OPTION_PERCENTAGE))
- n = n * 100 / total;
-
- if(unlikely(init)) {
- if(n > 0) {
- min = 0;
- max = n;
- }
- else {
- min = n;
- max = 0;
- }
- init = 0;
- }
-
- if(likely(!(co[c] & RRDR_EMPTY))) {
- all_null = 0;
- sum += n;
- }
-
- if(n < min) min = n;
- if(n > max) max = n;
- }
-
- if(likely(i != start))
- buffer_strcat(wb, separator);
-
- if(all_null) {
- if(options & RRDR_OPTION_NULL2ZERO)
- buffer_strcat(wb, "0");
- else
- buffer_strcat(wb, "null");
- }
- else {
- if(options & RRDR_OPTION_MIN2MAX)
- v = max - min;
- else
- v = sum;
-
- if(likely(i != start)) {
- if(r->min > v) r->min = v;
- if(r->max < v) r->max = v;
- }
- else {
- r->min = v;
- r->max = v;
- }
-
- buffer_rrd_value(wb, v);
- }
- }
- buffer_strcat(wb, suffix);
- //info("RRD2SSV(): %s: END", r->st->id);
+ //info("RRD2SSV(): %s: BEGIN", r->st->id);
+ long i;
+
+ buffer_strcat(wb, prefix);
+ long start = 0, end = rrdr_rows(r), step = 1;
+ if((options & RRDR_OPTION_REVERSED)) {
+ start = rrdr_rows(r) - 1;
+ end = -1;
+ step = -1;
+ }
+
+ // for each line in the array
+ for(i = start; i != end ;i += step) {
+ int all_values_are_null = 0;
+ calculated_number v = rrdr2value(r, i, options, &all_values_are_null);
+
+ if(likely(i != start)) {
+ if(r->min > v) r->min = v;
+ if(r->max < v) r->max = v;
+ }
+ else {
+ r->min = v;
+ r->max = v;
+ }
+
+ if(likely(i != start))
+ buffer_strcat(wb, separator);
+
+ if(all_values_are_null) {
+ if(options & RRDR_OPTION_NULL2ZERO)
+ buffer_strcat(wb, "0");
+ else
+ buffer_strcat(wb, "null");
+ }
+ else
+ buffer_rrd_value(wb, v);
+ }
+ buffer_strcat(wb, suffix);
+ //info("RRD2SSV(): %s: END", r->st->id);
}
inline static calculated_number *rrdr_line_values(RRDR *r)
{
- return &r->v[ r->c * r->d ];
+ return &r->v[ r->c * r->d ];
}
inline static uint8_t *rrdr_line_options(RRDR *r)
{
- return &r->o[ r->c * r->d ];
+ return &r->o[ r->c * r->d ];
}
inline static int rrdr_line_init(RRDR *r, time_t t)
{
- r->c++;
+ r->c++;
- if(unlikely(r->c >= r->n)) {
- error("requested to step above RRDR size for chart %s", r->st->name);
- r->c = r->n - 1;
- }
+ if(unlikely(r->c >= r->n)) {
+ error("requested to step above RRDR size for chart %s", r->st->name);
+ r->c = r->n - 1;
+ }
- // save the time
- r->t[r->c] = t;
+ // save the time
+ r->t[r->c] = t;
- return 1;
+ return 1;
}
inline static void rrdr_lock_rrdset(RRDR *r) {
- if(unlikely(!r)) {
- error("NULL value given!");
- return;
- }
+ if(unlikely(!r)) {
+ error("NULL value given!");
+ return;
+ }
- pthread_rwlock_rdlock(&r->st->rwlock);
- r->has_st_lock = 1;
+ pthread_rwlock_rdlock(&r->st->rwlock);
+ r->has_st_lock = 1;
}
inline static void rrdr_unlock_rrdset(RRDR *r) {
- if(unlikely(!r)) {
- error("NULL value given!");
- return;
- }
-
- if(likely(r->has_st_lock)) {
- pthread_rwlock_unlock(&r->st->rwlock);
- r->has_st_lock = 0;
- }
+ if(unlikely(!r)) {
+ error("NULL value given!");
+ return;
+ }
+
+ if(likely(r->has_st_lock)) {
+ pthread_rwlock_unlock(&r->st->rwlock);
+ r->has_st_lock = 0;
+ }
}
inline static void rrdr_free(RRDR *r)
{
- if(unlikely(!r)) {
- error("NULL value given!");
- return;
- }
-
- rrdr_unlock_rrdset(r);
- if(likely(r->t)) free(r->t);
- if(likely(r->v)) free(r->v);
- if(likely(r->o)) free(r->o);
- if(likely(r->od)) free(r->od);
- free(r);
+ if(unlikely(!r)) {
+ error("NULL value given!");
+ return;
+ }
+
+ rrdr_unlock_rrdset(r);
+ freez(r->t);
+ freez(r->v);
+ freez(r->o);
+ freez(r->od);
+ freez(r);
}
inline void rrdr_done(RRDR *r)
{
- r->rows = r->c + 1;
- r->c = 0;
+ r->rows = r->c + 1;
+ r->c = 0;
}
static RRDR *rrdr_create(RRDSET *st, long n)
{
- if(unlikely(!st)) {
- error("NULL value given!");
- return NULL;
- }
-
- RRDR *r = calloc(1, sizeof(RRDR));
- if(unlikely(!r)) goto cleanup;
-
- r->st = st;
-
- rrdr_lock_rrdset(r);
-
- RRDDIM *rd;
- for(rd = st->dimensions ; rd ; rd = rd->next) r->d++;
+ if(unlikely(!st)) {
+ error("NULL value given!");
+ return NULL;
+ }
- r->n = n;
+ RRDR *r = callocz(1, sizeof(RRDR));
+ r->st = st;
- r->t = malloc(n * sizeof(time_t));
- if(unlikely(!r->t)) goto cleanup;
+ rrdr_lock_rrdset(r);
- r->v = malloc(n * r->d * sizeof(calculated_number));
- if(unlikely(!r->v)) goto cleanup;
+ RRDDIM *rd;
+ for(rd = st->dimensions ; rd ; rd = rd->next) r->d++;
- r->o = malloc(n * r->d * sizeof(uint8_t));
- if(unlikely(!r->o)) goto cleanup;
+ r->n = n;
- r->od = malloc(r->d * sizeof(uint8_t));
- if(unlikely(!r->od)) goto cleanup;
+ r->t = mallocz(n * sizeof(time_t));
+ r->v = mallocz(n * r->d * sizeof(calculated_number));
+ r->o = mallocz(n * r->d * sizeof(uint8_t));
+ r->od = mallocz(r->d * sizeof(uint8_t));
- // set the hidden flag on hidden dimensions
- int c;
- for(c = 0, rd = st->dimensions ; rd ; c++, rd = rd->next) {
- if(unlikely(rd->flags & RRDDIM_FLAG_HIDDEN)) r->od[c] = RRDR_HIDDEN;
- else r->od[c] = 0;
- }
+ // set the hidden flag on hidden dimensions
+ int c;
+ for(c = 0, rd = st->dimensions ; rd ; c++, rd = rd->next) {
+ if(unlikely(rd->flags & RRDDIM_FLAG_HIDDEN)) r->od[c] = RRDR_HIDDEN;
+ else r->od[c] = 0;
+ }
- r->c = -1;
+ r->c = -1;
+ r->group = 1;
+ r->update_every = 1;
- r->group = 1;
- r->update_every = 1;
-
- return r;
-
-cleanup:
- error("Cannot allocate RRDR memory for %d entries", n);
- if(likely(r)) rrdr_free(r);
- return NULL;
+ return r;
}
-RRDR *rrd2rrdr(RRDSET *st, long points, long long after, long long before, int group_method)
+RRDR *rrd2rrdr(RRDSET *st, long points, long long after, long long before, int group_method, int aligned)
{
- int debug = st->debug;
- int absolute_period_requested = -1;
-
- time_t first_entry_t = rrdset_first_entry_t(st);
- time_t last_entry_t = rrdset_last_entry_t(st);
-
- if(before == 0 && after == 0) {
- before = last_entry_t;
- after = first_entry_t;
- absolute_period_requested = 0;
- }
-
- // allow relative for before and after
- if(before <= st->update_every * st->entries) {
- before = last_entry_t + before;
- absolute_period_requested = 0;
- }
-
- if(after <= st->update_every * st->entries) {
- after = last_entry_t + after;
- absolute_period_requested = 0;
- }
-
- if(absolute_period_requested == -1)
- absolute_period_requested = 1;
+ int debug = st->debug;
+ int absolute_period_requested = -1;
+
+ time_t first_entry_t = rrdset_first_entry_t(st);
+ time_t last_entry_t = rrdset_last_entry_t(st);
+
+ if(before == 0 && after == 0) {
+ before = last_entry_t;
+ after = first_entry_t;
+ absolute_period_requested = 0;
+ }
+
+ // allow relative for before and after
+ if(((before < 0)?-before:before) <= (st->update_every * st->entries)) {
+ before = last_entry_t + before;
+ absolute_period_requested = 0;
+ }
+
+ if(((after < 0)?-after:after) <= (st->update_every * st->entries)) {
+ if(after == 0) after = -st->update_every;
+ after = before + after;
+ absolute_period_requested = 0;
+ }
+
+ if(absolute_period_requested == -1)
+ absolute_period_requested = 1;
+
+ // make sure they are within our timeframe
+ if(before > last_entry_t) before = last_entry_t;
+ if(before < first_entry_t) before = first_entry_t;
+
+ if(after > last_entry_t) after = last_entry_t;
+ if(after < first_entry_t) after = first_entry_t;
+
+ // check if they are upside down
+ if(after > before) {
+ time_t tmp = before;
+ before = after;
+ after = tmp;
+ }
+
+ // the duration of the chart
+ time_t duration = before - after;
+ long available_points = duration / st->update_every;
+
+ if(duration <= 0 || available_points <= 0)
+ return rrdr_create(st, 1);
+
+ // check the wanted points
+ if(points < 0) points = -points;
+ if(points > available_points) points = available_points;
+ if(points == 0) points = available_points;
+
+ // calculate proper grouping of source data
+ long group = available_points / points;
+ if(group <= 0) group = 1;
+
+ // round group to the closest integer
+ if(available_points % points > points / 2) group++;
+
+ time_t after_new = (aligned) ? (after - (after % (group * st->update_every))) : after;
+ time_t before_new = (aligned) ? (before - (before % (group * st->update_every))) : before;
+ long points_new = (before_new - after_new) / st->update_every / group;
+
+ // find the starting and ending slots in our round robin db
+ long start_at_slot = rrdset_time2slot(st, before_new),
+ stop_at_slot = rrdset_time2slot(st, after_new);
- // make sure they are within our timeframe
- if(before > last_entry_t) before = last_entry_t;
- if(before < first_entry_t) before = first_entry_t;
+#ifdef NETDATA_INTERNAL_CHECKS
+ if(after_new < first_entry_t) {
+ error("after_new %u is too small, minimum %u", (uint32_t)after_new, (uint32_t)first_entry_t);
+ }
+ if(after_new > last_entry_t) {
+ error("after_new %u is too big, maximum %u", (uint32_t)after_new, (uint32_t)last_entry_t);
+ }
+ if(before_new < first_entry_t) {
+ error("before_new %u is too small, minimum %u", (uint32_t)before_new, (uint32_t)first_entry_t);
+ }
+ if(before_new > last_entry_t) {
+ error("before_new %u is too big, maximum %u", (uint32_t)before_new, (uint32_t)last_entry_t);
+ }
+ if(start_at_slot < 0 || start_at_slot >= st->entries) {
+ error("start_at_slot is invalid %ld, expected 0 to %ld", start_at_slot, st->entries - 1);
+ }
+ if(stop_at_slot < 0 || stop_at_slot >= st->entries) {
+ error("stop_at_slot is invalid %ld, expected 0 to %ld", stop_at_slot, st->entries - 1);
+ }
+ if(points_new > (before_new - after_new) / group / st->update_every + 1) {
+ error("points_new %ld is more than points %ld", points_new, (before_new - after_new) / group / st->update_every + 1);
+ }
+#endif
- if(after > last_entry_t) after = last_entry_t;
- if(after < first_entry_t) after = first_entry_t;
+ //info("RRD2RRDR(): %s: wanted %ld points, got %ld - group=%ld, wanted duration=%u, got %u - wanted %ld - %ld, got %ld - %ld", st->id, points, points_new, group, before - after, before_new - after_new, after, before, after_new, before_new);
- // check if they are upside down
- if(after > before) {
- time_t tmp = before;
- before = after;
- after = tmp;
- }
+ after = after_new;
+ before = before_new;
+ duration = before - after;
+ points = points_new;
- // the duration of the chart
- time_t duration = before - after;
- long available_points = duration / st->update_every;
+ // Now we have:
+ // before = the end time of the calculation
+ // after = the start time of the calculation
+ // duration = the duration of the calculation
+ // group = the number of source points to aggregate / group together
+ // method = the method of grouping source points
+ // points = the number of points to generate
- if(duration <= 0 || available_points <= 0)
- return rrdr_create(st, 1);
- // check the wanted points
- if(points < 0) points = -points;
- if(points > available_points) points = available_points;
- if(points == 0) points = available_points;
+ // -------------------------------------------------------------------------
+ // initialize our result set
- // calculate proper grouping of source data
- long group = available_points / points;
- if(group <= 0) group = 1;
+ RRDR *r = rrdr_create(st, points);
+ if(!r) {
+#ifdef NETDATA_INTERNAL_CHECKS
+ error("Cannot create RRDR for %s, after=%u, before=%u, duration=%u, points=%ld", st->id, (uint32_t)after, (uint32_t)before, (uint32_t)duration, points);
+#endif
+ return NULL;
+ }
+ if(!r->d) {
+#ifdef NETDATA_INTERNAL_CHECKS
+ error("Returning empty RRDR (no dimensions in RRDSET) for %s, after=%u, before=%u, duration=%u, points=%ld", st->id, (uint32_t)after, (uint32_t)before, (uint32_t)duration, points);
+#endif
+ return r;
+ }
+
+ if(absolute_period_requested == 1)
+ r->result_options |= RRDR_RESULT_OPTION_ABSOLUTE;
+ else
+ r->result_options |= RRDR_RESULT_OPTION_RELATIVE;
+
+ // find how many dimensions we have
+ long dimensions = r->d;
+
+
+ // -------------------------------------------------------------------------
+ // checks for debugging
+
+ if(debug) debug(D_RRD_STATS, "INFO %s first_t: %u, last_t: %u, all_duration: %u, after: %u, before: %u, duration: %u, points: %ld, group: %ld"
+ , st->id
+ , (uint32_t)first_entry_t
+ , (uint32_t)last_entry_t
+ , (uint32_t)(last_entry_t - first_entry_t)
+ , (uint32_t)after
+ , (uint32_t)before
+ , (uint32_t)duration
+ , points
+ , group
+ );
+
+
+ // -------------------------------------------------------------------------
+ // temp arrays for keeping values per dimension
+
+ calculated_number last_values[dimensions]; // keep the last value of each dimension
+ calculated_number group_values[dimensions]; // keep sums when grouping
+ long group_counts[dimensions]; // keep the number of values added to group_values
+ uint8_t group_options[dimensions];
+ uint8_t found_non_zero[dimensions];
+
+
+ // initialize them
+ RRDDIM *rd;
+ long c;
+ for( rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
+ last_values[c] = 0;
+ group_values[c] = (group_method == GROUP_MAX || group_method == GROUP_MIN)?NAN:0;
+ group_counts[c] = 0;
+ group_options[c] = 0;
+ found_non_zero[c] = 0;
+ }
+
+
+ // -------------------------------------------------------------------------
+ // the main loop
+
+ time_t now = rrdset_slot2time(st, start_at_slot),
+ dt = st->update_every,
+ group_start_t = 0;
+
+ if(unlikely(debug)) debug(D_RRD_STATS, "BEGIN %s after_t: %u (stop_at_t: %ld), before_t: %u (start_at_t: %ld), start_t(now): %u, current_entry: %ld, entries: %ld"
+ , st->id
+ , (uint32_t)after
+ , stop_at_slot
+ , (uint32_t)before
+ , start_at_slot
+ , (uint32_t)now
+ , st->current_entry
+ , st->entries
+ );
+
+ r->group = group;
+ r->update_every = group * st->update_every;
+ r->before = now;
+ r->after = now;
+
+ //info("RRD2RRDR(): %s: STARTING", st->id);
+
+ long slot = start_at_slot, counter = 0, stop_now = 0, added = 0, group_count = 0, add_this = 0;
+ for(; !stop_now ; now -= dt, slot--, counter++) {
+ if(unlikely(slot < 0)) slot = st->entries - 1;
+ if(unlikely(slot == stop_at_slot)) stop_now = counter;
+
+ if(unlikely(debug)) debug(D_RRD_STATS, "ROW %s slot: %ld, entries_counter: %ld, group_count: %ld, added: %ld, now: %ld, %s %s"
+ , st->id
+ , slot
+ , counter
+ , group_count + 1
+ , added
+ , now
+ , (group_count + 1 == group)?"PRINT":" - "
+ , (now >= after && now <= before)?"RANGE":" - "
+ );
+
+ // make sure we return data in the proper time range
+ if(unlikely(now > before)) continue;
+ if(unlikely(now < after)) break;
+
+ if(unlikely(group_count == 0)) {
+ group_start_t = now;
+ }
+ group_count++;
+
+ if(unlikely(group_count == group)) {
+ if(unlikely(added >= points)) break;
+ add_this = 1;
+ }
+
+ // do the calculations
+ for(rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
+ storage_number n = rd->values[slot];
+ if(unlikely(!does_storage_number_exist(n))) continue;
+
+ group_counts[c]++;
+
+ calculated_number value = unpack_storage_number(n);
+ if(likely(value != 0.0)) {
+ group_options[c] |= RRDR_NONZERO;
+ found_non_zero[c] = 1;
+ }
+
+ if(unlikely(did_storage_number_reset(n)))
+ group_options[c] |= RRDR_RESET;
+
+ switch(group_method) {
+ case GROUP_MIN:
+ if(unlikely(isnan(group_values[c])) ||
+ fabsl(value) < fabsl(group_values[c]))
+ group_values[c] = value;
+ break;
+
+ case GROUP_MAX:
+ if(unlikely(isnan(group_values[c])) ||
+ fabsl(value) > fabsl(group_values[c]))
+ group_values[c] = value;
+ break;
+
+ default:
+ case GROUP_SUM:
+ case GROUP_AVERAGE:
+ case GROUP_UNDEFINED:
+ group_values[c] += value;
+ break;
+
+ case GROUP_INCREMENTAL_SUM:
+ if(unlikely(slot == start_at_slot))
+ last_values[c] = value;
+
+ group_values[c] += last_values[c] - value;
+ last_values[c] = value;
+ break;
+ }
+ }
+
+ // added it
+ if(unlikely(add_this)) {
+ if(unlikely(!rrdr_line_init(r, group_start_t))) break;
+
+ r->after = now;
+
+ calculated_number *cn = rrdr_line_values(r);
+ uint8_t *co = rrdr_line_options(r);
+
+ for(rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
+
+ // update the dimension options
+ if(likely(found_non_zero[c])) r->od[c] |= RRDR_NONZERO;
+
+ // store the specific point options
+ co[c] = group_options[c];
+
+ // store the value
+ if(unlikely(group_counts[c] == 0)) {
+ cn[c] = 0.0;
+ co[c] |= RRDR_EMPTY;
+ group_values[c] = (group_method == GROUP_MAX || group_method == GROUP_MIN)?NAN:0;
+ }
+ else {
+ switch(group_method) {
+ case GROUP_MIN:
+ case GROUP_MAX:
+ if(unlikely(isnan(group_values[c])))
+ cn[c] = 0;
+ else {
+ cn[c] = group_values[c];
+ group_values[c] = NAN;
+ }
+ break;
+
+ case GROUP_SUM:
+ case GROUP_INCREMENTAL_SUM:
+ cn[c] = group_values[c];
+ group_values[c] = 0;
+ break;
+
+ default:
+ case GROUP_AVERAGE:
+ case GROUP_UNDEFINED:
+ cn[c] = group_values[c] / group_counts[c];
+ group_values[c] = 0;
+ break;
+ }
+
+ if(cn[c] < r->min) r->min = cn[c];
+ if(cn[c] > r->max) r->max = cn[c];
+ }
+
+ // reset for the next loop
+ group_counts[c] = 0;
+ group_options[c] = 0;
+ }
+
+ added++;
+ group_count = 0;
+ add_this = 0;
+ }
+ }
+
+ rrdr_done(r);
+ //info("RRD2RRDR(): %s: END %ld loops made, %ld points generated", st->id, counter, rrdr_rows(r));
+ //error("SHIFT: %s: wanted %ld points, got %ld", st->id, points, rrdr_rows(r));
+ return r;
+}
- // round group to the closest integer
- if(available_points % points > points / 2) group++;
+int rrd2value(RRDSET *st, BUFFER *wb, calculated_number *n, const char *dimensions, long points, long long after, long long before, int group_method, uint32_t options, time_t *db_after, time_t *db_before, int *value_is_null)
+{
+ RRDR *r = rrd2rrdr(st, points, after, before, group_method, !(options & RRDR_OPTION_NOT_ALIGNED));
+ if(!r) {
+ if(value_is_null) *value_is_null = 1;
+ return 500;
+ }
- time_t after_new = after - (after % (group * st->update_every));
- time_t before_new = before - (before % (group * st->update_every));
- long points_new = (before_new - after_new) / st->update_every / group;
+ if(rrdr_rows(r) == 0) {
+ rrdr_free(r);
- // find the starting and ending slots in our round robin db
- long start_at_slot = rrdset_time2slot(st, before_new),
- stop_at_slot = rrdset_time2slot(st, after_new);
+ if(db_after) *db_after = 0;
+ if(db_before) *db_before = 0;
+ if(value_is_null) *value_is_null = 1;
-#ifdef NETDATA_INTERNAL_CHECKS
- if(after_new < first_entry_t) {
- error("after_new %u is too small, minimum %u", after_new, first_entry_t);
- }
- if(after_new > last_entry_t) {
- error("after_new %u is too big, maximum %u", after_new, last_entry_t);
- }
- if(before_new < first_entry_t) {
- error("before_new %u is too small, minimum %u", before_new, first_entry_t);
- }
- if(before_new > last_entry_t) {
- error("before_new %u is too big, maximum %u", before_new, last_entry_t);
- }
- if(start_at_slot < 0 || start_at_slot >= st->entries) {
- error("start_at_slot is invalid %ld, expected %ld to %ld", start_at_slot, 0, st->entries - 1);
- }
- if(stop_at_slot < 0 || stop_at_slot >= st->entries) {
- error("stop_at_slot is invalid %ld, expected %ld to %ld", stop_at_slot, 0, st->entries - 1);
- }
- if(points_new > (before_new - after_new) / group / st->update_every + 1) {
- error("points_new %ld is more than points %ld", points_new, (before_new - after_new) / group / st->update_every + 1);
- }
-#endif
+ return 400;
+ }
- //info("RRD2RRDR(): %s: wanted %ld points, got %ld - group=%ld, wanted duration=%u, got %u - wanted %ld - %ld, got %ld - %ld", st->id, points, points_new, group, before - after, before_new - after_new, after, before, after_new, before_new);
+ if(r->result_options & RRDR_RESULT_OPTION_RELATIVE)
+ wb->options |= WB_CONTENT_NO_CACHEABLE;
+ else if(r->result_options & RRDR_RESULT_OPTION_ABSOLUTE)
+ wb->options |= WB_CONTENT_CACHEABLE;
- after = after_new;
- before = before_new;
- duration = before - after;
- points = points_new;
+ options = rrdr_check_options(r, options, dimensions);
- // Now we have:
- // before = the end time of the calculation
- // after = the start time of the calculation
- // duration = the duration of the calculation
- // group = the number of source points to aggregate / group together
- // method = the method of grouping source points
- // points = the number of points to generate
+ if(dimensions)
+ rrdr_disable_not_selected_dimensions(r, dimensions);
+ if(db_after) *db_after = r->after;
+ if(db_before) *db_before = r->before;
- // -------------------------------------------------------------------------
- // initialize our result set
+ long i = (options & RRDR_OPTION_REVERSED)?rrdr_rows(r) - 1:0;
+ *n = rrdr2value(r, i, options, value_is_null);
- RRDR *r = rrdr_create(st, points);
- if(!r) {
-#ifdef NETDATA_INTERNAL_CHECKS
- error("Cannot create RRDR for %s, after=%u, before=%u, duration=%u, points=%d", st->id, after, before, duration, points);
-#endif
- return NULL;
- }
- if(!r->d) {
-#ifdef NETDATA_INTERNAL_CHECKS
- error("Returning empty RRDR (no dimensions in RRDSET) for %s, after=%u, before=%u, duration=%u, points=%d", st->id, after, before, duration, points);
-#endif
- return r;
- }
-
- if(absolute_period_requested == 1)
- r->result_options |= RRDR_RESULT_OPTION_ABSOLUTE;
- else
- r->result_options |= RRDR_RESULT_OPTION_RELATIVE;
-
- // find how many dimensions we have
- long dimensions = r->d;
-
-
- // -------------------------------------------------------------------------
- // checks for debugging
-
- if(debug) debug(D_RRD_STATS, "INFO %s first_t: %lu, last_t: %lu, all_duration: %lu, after: %lu, before: %lu, duration: %lu, points: %ld, group: %ld"
- , st->id
- , first_entry_t
- , last_entry_t
- , last_entry_t - first_entry_t
- , after
- , before
- , duration
- , points
- , group
- );
-
-
- // -------------------------------------------------------------------------
- // temp arrays for keeping values per dimension
-
- calculated_number group_values[dimensions]; // keep sums when grouping
- long group_counts[dimensions]; // keep the number of values added to group_values
- uint8_t group_options[dimensions];
- uint8_t found_non_zero[dimensions];
-
-
- // initialize them
- RRDDIM *rd;
- long c;
- for( rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
- group_values[c] = 0;
- group_counts[c] = 0;
- group_options[c] = 0;
- found_non_zero[c] = 0;
- }
-
-
- // -------------------------------------------------------------------------
- // the main loop
-
- time_t now = rrdset_slot2time(st, start_at_slot),
- dt = st->update_every,
- group_start_t = 0;
-
- if(unlikely(debug)) debug(D_RRD_STATS, "BEGIN %s after_t: %lu (stop_at_t: %ld), before_t: %lu (start_at_t: %ld), start_t(now): %lu, current_entry: %ld, entries: %ld"
- , st->id
- , after
- , stop_at_slot
- , before
- , start_at_slot
- , now
- , st->current_entry
- , st->entries
- );
-
- r->group = group;
- r->update_every = group * st->update_every;
- r->before = now;
- r->after = now;
-
- //info("RRD2RRDR(): %s: STARTING", st->id);
-
- long slot = start_at_slot, counter = 0, stop_now = 0, added = 0, group_count = 0, add_this = 0;
- for(; !stop_now ; now -= dt, slot--, counter++) {
- if(unlikely(slot < 0)) slot = st->entries - 1;
- if(unlikely(slot == stop_at_slot)) stop_now = counter;
-
- if(unlikely(debug)) debug(D_RRD_STATS, "ROW %s slot: %ld, entries_counter: %ld, group_count: %ld, added: %ld, now: %lu, %s %s"
- , st->id
- , slot
- , counter
- , group_count + 1
- , added
- , now
- , (group_count + 1 == group)?"PRINT":" - "
- , (now >= after && now <= before)?"RANGE":" - "
- );
-
- // make sure we return data in the proper time range
- if(unlikely(now > before)) continue;
- if(unlikely(now < after)) break;
-
- if(unlikely(group_count == 0)) {
- group_start_t = now;
- }
- group_count++;
-
- if(unlikely(group_count == group)) {
- if(unlikely(added >= points)) break;
- add_this = 1;
- }
-
- // do the calculations
- for(rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
- storage_number n = rd->values[slot];
- if(unlikely(!does_storage_number_exist(n))) continue;
-
- group_counts[c]++;
-
- calculated_number value = unpack_storage_number(n);
- if(likely(value != 0.0)) {
- group_options[c] |= RRDR_NONZERO;
- found_non_zero[c] = 1;
- }
-
- if(unlikely(did_storage_number_reset(n)))
- group_options[c] |= RRDR_RESET;
-
- switch(group_method) {
- case GROUP_MAX:
- if(unlikely(fabsl(value) > fabsl(group_values[c])))
- group_values[c] = value;
- break;
-
- default:
- case GROUP_SUM:
- case GROUP_AVERAGE:
- group_values[c] += value;
- break;
- }
- }
-
- // added it
- if(unlikely(add_this)) {
- if(unlikely(!rrdr_line_init(r, group_start_t))) break;
-
- r->after = now;
-
- calculated_number *cn = rrdr_line_values(r);
- uint8_t *co = rrdr_line_options(r);
-
- for(rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
-
- // update the dimension options
- if(likely(found_non_zero[c])) r->od[c] |= RRDR_NONZERO;
-
- // store the specific point options
- co[c] = group_options[c];
-
- // store the value
- if(unlikely(group_counts[c] == 0)) {
- cn[c] = 0.0;
- co[c] |= RRDR_EMPTY;
- }
- else if(unlikely(group_method == GROUP_AVERAGE)) {
- cn[c] = group_values[c] / group_counts[c];
- }
- else {
- cn[c] = group_values[c];
- }
-
- if(cn[c] < r->min) r->min = cn[c];
- if(cn[c] > r->max) r->max = cn[c];
-
- // reset them for the next loop
- group_values[c] = 0;
- group_counts[c] = 0;
- group_options[c] = 0;
- }
-
- added++;
- group_count = 0;
- add_this = 0;
- }
- }
-
- rrdr_done(r);
- //info("RRD2RRDR(): %s: END %ld loops made, %ld points generated", st->id, counter, rrdr_rows(r));
- //error("SHIFT: %s: wanted %ld points, got %ld", st->id, points, rrdr_rows(r));
- return r;
+ rrdr_free(r);
+ return 200;
}
int rrd2format(RRDSET *st, BUFFER *wb, BUFFER *dimensions, uint32_t format, long points, long long after, long long before, int group_method, uint32_t options, time_t *latest_timestamp)
{
- RRDR *r = rrd2rrdr(st, points, after, before, group_method);
- if(!r) {
- buffer_strcat(wb, "Cannot generate output with these parameters on this chart.");
- return 500;
- }
-
- if(r->result_options & RRDR_RESULT_OPTION_RELATIVE)
- wb->options |= WB_CONTENT_NO_CACHEABLE;
- else if(r->result_options & RRDR_RESULT_OPTION_ABSOLUTE)
- wb->options |= WB_CONTENT_CACHEABLE;
-
- options = rrdr_check_options(r, options, (dimensions)?buffer_tostring(dimensions):NULL);
-
- if(dimensions)
- rrdr_disable_not_selected_dimensions(r, buffer_tostring(dimensions));
-
- if(latest_timestamp && rrdr_rows(r) > 0)
- *latest_timestamp = r->before;
-
- switch(format) {
- case DATASOURCE_SSV:
- if(options & RRDR_OPTION_JSON_WRAP) {
- wb->contenttype = CT_APPLICATION_JSON;
- rrdr_json_wrapper_begin(r, wb, format, options, 1);
- rrdr2ssv(r, wb, options, "", " ", "");
- rrdr_json_wrapper_end(r, wb, format, options, 1);
- }
- else {
- wb->contenttype = CT_TEXT_PLAIN;
- rrdr2ssv(r, wb, options, "", " ", "");
- }
- break;
-
- case DATASOURCE_SSV_COMMA:
- if(options & RRDR_OPTION_JSON_WRAP) {
- wb->contenttype = CT_APPLICATION_JSON;
- rrdr_json_wrapper_begin(r, wb, format, options, 1);
- rrdr2ssv(r, wb, options, "", ",", "");
- rrdr_json_wrapper_end(r, wb, format, options, 1);
- }
- else {
- wb->contenttype = CT_TEXT_PLAIN;
- rrdr2ssv(r, wb, options, "", ",", "");
- }
- break;
-
- case DATASOURCE_JS_ARRAY:
- if(options & RRDR_OPTION_JSON_WRAP) {
- wb->contenttype = CT_APPLICATION_JSON;
- rrdr_json_wrapper_begin(r, wb, format, options, 0);
- rrdr2ssv(r, wb, options, "[", ",", "]");
- rrdr_json_wrapper_end(r, wb, format, options, 0);
- }
- else {
- wb->contenttype = CT_APPLICATION_JSON;
- rrdr2ssv(r, wb, options, "[", ",", "]");
- }
- break;
-
- case DATASOURCE_CSV:
- if(options & RRDR_OPTION_JSON_WRAP) {
- wb->contenttype = CT_APPLICATION_JSON;
- rrdr_json_wrapper_begin(r, wb, format, options, 1);
- rrdr2csv(r, wb, options, "", ",", "\\n", "");
- rrdr_json_wrapper_end(r, wb, format, options, 1);
- }
- else {
- wb->contenttype = CT_TEXT_PLAIN;
- rrdr2csv(r, wb, options, "", ",", "\r\n", "");
- }
- break;
-
- case DATASOURCE_CSV_JSON_ARRAY:
- wb->contenttype = CT_APPLICATION_JSON;
- if(options & RRDR_OPTION_JSON_WRAP) {
- rrdr_json_wrapper_begin(r, wb, format, options, 0);
- buffer_strcat(wb, "[\n");
- rrdr2csv(r, wb, options + RRDR_OPTION_LABEL_QUOTES, "[", ",", "]", ",\n");
- buffer_strcat(wb, "\n]");
- rrdr_json_wrapper_end(r, wb, format, options, 0);
- }
- else {
- wb->contenttype = CT_TEXT_PLAIN;
- buffer_strcat(wb, "[\n");
- rrdr2csv(r, wb, options + RRDR_OPTION_LABEL_QUOTES, "[", ",", "]", ",\n");
- buffer_strcat(wb, "\n]");
- }
- break;
-
- case DATASOURCE_TSV:
- if(options & RRDR_OPTION_JSON_WRAP) {
- wb->contenttype = CT_APPLICATION_JSON;
- rrdr_json_wrapper_begin(r, wb, format, options, 1);
- rrdr2csv(r, wb, options, "", "\t", "\\n", "");
- rrdr_json_wrapper_end(r, wb, format, options, 1);
- }
- else {
- wb->contenttype = CT_TEXT_PLAIN;
- rrdr2csv(r, wb, options, "", "\t", "\r\n", "");
- }
- break;
-
- case DATASOURCE_HTML:
- if(options & RRDR_OPTION_JSON_WRAP) {
- wb->contenttype = CT_APPLICATION_JSON;
- rrdr_json_wrapper_begin(r, wb, format, options, 1);
- buffer_strcat(wb, "<html>\\n<center>\\n<table border=\\\"0\\\" cellpadding=\\\"5\\\" cellspacing=\\\"5\\\">\\n");
- rrdr2csv(r, wb, options, "<tr><td>", "</td><td>", "</td></tr>\\n", "");
- buffer_strcat(wb, "</table>\\n</center>\\n</html>\\n");
- rrdr_json_wrapper_end(r, wb, format, options, 1);
- }
- else {
- wb->contenttype = CT_TEXT_HTML;
- buffer_strcat(wb, "<html>\n<center>\n<table border=\"0\" cellpadding=\"5\" cellspacing=\"5\">\n");
- rrdr2csv(r, wb, options, "<tr><td>", "</td><td>", "</td></tr>\n", "");
- buffer_strcat(wb, "</table>\n</center>\n</html>\n");
- }
- break;
-
- case DATASOURCE_DATATABLE_JSONP:
- wb->contenttype = CT_APPLICATION_X_JAVASCRIPT;
-
- if(options & RRDR_OPTION_JSON_WRAP)
- rrdr_json_wrapper_begin(r, wb, format, options, 0);
-
- rrdr2json(r, wb, options, 1);
-
- if(options & RRDR_OPTION_JSON_WRAP)
- rrdr_json_wrapper_end(r, wb, format, options, 0);
- break;
-
- case DATASOURCE_DATATABLE_JSON:
- wb->contenttype = CT_APPLICATION_JSON;
-
- if(options & RRDR_OPTION_JSON_WRAP)
- rrdr_json_wrapper_begin(r, wb, format, options, 0);
-
- rrdr2json(r, wb, options, 1);
-
- if(options & RRDR_OPTION_JSON_WRAP)
- rrdr_json_wrapper_end(r, wb, format, options, 0);
- break;
-
- case DATASOURCE_JSONP:
- wb->contenttype = CT_APPLICATION_X_JAVASCRIPT;
- if(options & RRDR_OPTION_JSON_WRAP)
- rrdr_json_wrapper_begin(r, wb, format, options, 0);
-
- rrdr2json(r, wb, options, 0);
-
- if(options & RRDR_OPTION_JSON_WRAP)
- rrdr_json_wrapper_end(r, wb, format, options, 0);
- break;
-
- case DATASOURCE_JSON:
- default:
- wb->contenttype = CT_APPLICATION_JSON;
-
- if(options & RRDR_OPTION_JSON_WRAP)
- rrdr_json_wrapper_begin(r, wb, format, options, 0);
-
- rrdr2json(r, wb, options, 0);
-
- if(options & RRDR_OPTION_JSON_WRAP)
- rrdr_json_wrapper_end(r, wb, format, options, 0);
- break;
- }
-
- rrdr_free(r);
- return 200;
+ RRDR *r = rrd2rrdr(st, points, after, before, group_method, !(options & RRDR_OPTION_NOT_ALIGNED));
+ if(!r) {
+ buffer_strcat(wb, "Cannot generate output with these parameters on this chart.");
+ return 500;
+ }
+
+ if(r->result_options & RRDR_RESULT_OPTION_RELATIVE)
+ wb->options |= WB_CONTENT_NO_CACHEABLE;
+ else if(r->result_options & RRDR_RESULT_OPTION_ABSOLUTE)
+ wb->options |= WB_CONTENT_CACHEABLE;
+
+ options = rrdr_check_options(r, options, (dimensions)?buffer_tostring(dimensions):NULL);
+
+ if(dimensions)
+ rrdr_disable_not_selected_dimensions(r, buffer_tostring(dimensions));
+
+ if(latest_timestamp && rrdr_rows(r) > 0)
+ *latest_timestamp = r->before;
+
+ switch(format) {
+ case DATASOURCE_SSV:
+ if(options & RRDR_OPTION_JSON_WRAP) {
+ wb->contenttype = CT_APPLICATION_JSON;
+ rrdr_json_wrapper_begin(r, wb, format, options, 1);
+ rrdr2ssv(r, wb, options, "", " ", "");
+ rrdr_json_wrapper_end(r, wb, format, options, 1);
+ }
+ else {
+ wb->contenttype = CT_TEXT_PLAIN;
+ rrdr2ssv(r, wb, options, "", " ", "");
+ }
+ break;
+
+ case DATASOURCE_SSV_COMMA:
+ if(options & RRDR_OPTION_JSON_WRAP) {
+ wb->contenttype = CT_APPLICATION_JSON;
+ rrdr_json_wrapper_begin(r, wb, format, options, 1);
+ rrdr2ssv(r, wb, options, "", ",", "");
+ rrdr_json_wrapper_end(r, wb, format, options, 1);
+ }
+ else {
+ wb->contenttype = CT_TEXT_PLAIN;
+ rrdr2ssv(r, wb, options, "", ",", "");
+ }
+ break;
+
+ case DATASOURCE_JS_ARRAY:
+ if(options & RRDR_OPTION_JSON_WRAP) {
+ wb->contenttype = CT_APPLICATION_JSON;
+ rrdr_json_wrapper_begin(r, wb, format, options, 0);
+ rrdr2ssv(r, wb, options, "[", ",", "]");
+ rrdr_json_wrapper_end(r, wb, format, options, 0);
+ }
+ else {
+ wb->contenttype = CT_APPLICATION_JSON;
+ rrdr2ssv(r, wb, options, "[", ",", "]");
+ }
+ break;
+
+ case DATASOURCE_CSV:
+ if(options & RRDR_OPTION_JSON_WRAP) {
+ wb->contenttype = CT_APPLICATION_JSON;
+ rrdr_json_wrapper_begin(r, wb, format, options, 1);
+ rrdr2csv(r, wb, options, "", ",", "\\n", "");
+ rrdr_json_wrapper_end(r, wb, format, options, 1);
+ }
+ else {
+ wb->contenttype = CT_TEXT_PLAIN;
+ rrdr2csv(r, wb, options, "", ",", "\r\n", "");
+ }
+ break;
+
+ case DATASOURCE_CSV_JSON_ARRAY:
+ wb->contenttype = CT_APPLICATION_JSON;
+ if(options & RRDR_OPTION_JSON_WRAP) {
+ rrdr_json_wrapper_begin(r, wb, format, options, 0);
+ buffer_strcat(wb, "[\n");
+ rrdr2csv(r, wb, options + RRDR_OPTION_LABEL_QUOTES, "[", ",", "]", ",\n");
+ buffer_strcat(wb, "\n]");
+ rrdr_json_wrapper_end(r, wb, format, options, 0);
+ }
+ else {
+ wb->contenttype = CT_TEXT_PLAIN;
+ buffer_strcat(wb, "[\n");
+ rrdr2csv(r, wb, options + RRDR_OPTION_LABEL_QUOTES, "[", ",", "]", ",\n");
+ buffer_strcat(wb, "\n]");
+ }
+ break;
+
+ case DATASOURCE_TSV:
+ if(options & RRDR_OPTION_JSON_WRAP) {
+ wb->contenttype = CT_APPLICATION_JSON;
+ rrdr_json_wrapper_begin(r, wb, format, options, 1);
+ rrdr2csv(r, wb, options, "", "\t", "\\n", "");
+ rrdr_json_wrapper_end(r, wb, format, options, 1);
+ }
+ else {
+ wb->contenttype = CT_TEXT_PLAIN;
+ rrdr2csv(r, wb, options, "", "\t", "\r\n", "");
+ }
+ break;
+
+ case DATASOURCE_HTML:
+ if(options & RRDR_OPTION_JSON_WRAP) {
+ wb->contenttype = CT_APPLICATION_JSON;
+ rrdr_json_wrapper_begin(r, wb, format, options, 1);
+ buffer_strcat(wb, "<html>\\n<center>\\n<table border=\\\"0\\\" cellpadding=\\\"5\\\" cellspacing=\\\"5\\\">\\n");
+ rrdr2csv(r, wb, options, "<tr><td>", "</td><td>", "</td></tr>\\n", "");
+ buffer_strcat(wb, "</table>\\n</center>\\n</html>\\n");
+ rrdr_json_wrapper_end(r, wb, format, options, 1);
+ }
+ else {
+ wb->contenttype = CT_TEXT_HTML;
+ buffer_strcat(wb, "<html>\n<center>\n<table border=\"0\" cellpadding=\"5\" cellspacing=\"5\">\n");
+ rrdr2csv(r, wb, options, "<tr><td>", "</td><td>", "</td></tr>\n", "");
+ buffer_strcat(wb, "</table>\n</center>\n</html>\n");
+ }
+ break;
+
+ case DATASOURCE_DATATABLE_JSONP:
+ wb->contenttype = CT_APPLICATION_X_JAVASCRIPT;
+
+ if(options & RRDR_OPTION_JSON_WRAP)
+ rrdr_json_wrapper_begin(r, wb, format, options, 0);
+
+ rrdr2json(r, wb, options, 1);
+
+ if(options & RRDR_OPTION_JSON_WRAP)
+ rrdr_json_wrapper_end(r, wb, format, options, 0);
+ break;
+
+ case DATASOURCE_DATATABLE_JSON:
+ wb->contenttype = CT_APPLICATION_JSON;
+
+ if(options & RRDR_OPTION_JSON_WRAP)
+ rrdr_json_wrapper_begin(r, wb, format, options, 0);
+
+ rrdr2json(r, wb, options, 1);
+
+ if(options & RRDR_OPTION_JSON_WRAP)
+ rrdr_json_wrapper_end(r, wb, format, options, 0);
+ break;
+
+ case DATASOURCE_JSONP:
+ wb->contenttype = CT_APPLICATION_X_JAVASCRIPT;
+ if(options & RRDR_OPTION_JSON_WRAP)
+ rrdr_json_wrapper_begin(r, wb, format, options, 0);
+
+ rrdr2json(r, wb, options, 0);
+
+ if(options & RRDR_OPTION_JSON_WRAP)
+ rrdr_json_wrapper_end(r, wb, format, options, 0);
+ break;
+
+ case DATASOURCE_JSON:
+ default:
+ wb->contenttype = CT_APPLICATION_JSON;
+
+ if(options & RRDR_OPTION_JSON_WRAP)
+ rrdr_json_wrapper_begin(r, wb, format, options, 0);
+
+ rrdr2json(r, wb, options, 0);
+
+ if(options & RRDR_OPTION_JSON_WRAP)
+ rrdr_json_wrapper_end(r, wb, format, options, 0);
+ break;
+ }
+
+ rrdr_free(r);
+ return 200;
}
time_t rrd_stats_json(int type, RRDSET *st, BUFFER *wb, long points, long group, int group_method, time_t after, time_t before, int only_non_zero)
{
- int c;
- pthread_rwlock_rdlock(&st->rwlock);
+ int c;
+ pthread_rwlock_rdlock(&st->rwlock);
- // -------------------------------------------------------------------------
- // switch from JSON to google JSON
+ // -------------------------------------------------------------------------
+ // switch from JSON to google JSON
- char kq[2] = "\"";
- char sq[2] = "\"";
- switch(type) {
- case DATASOURCE_DATATABLE_JSON:
- case DATASOURCE_DATATABLE_JSONP:
- kq[0] = '\0';
- sq[0] = '\'';
- break;
+ char kq[2] = "\"";
+ char sq[2] = "\"";
+ switch(type) {
+ case DATASOURCE_DATATABLE_JSON:
+ case DATASOURCE_DATATABLE_JSONP:
+ kq[0] = '\0';
+ sq[0] = '\'';
+ break;
- case DATASOURCE_JSON:
- default:
- break;
- }
+ case DATASOURCE_JSON:
+ default:
+ break;
+ }
- // -------------------------------------------------------------------------
- // validate the parameters
+ // -------------------------------------------------------------------------
+ // validate the parameters
- if(points < 1) points = 1;
- if(group < 1) group = 1;
+ if(points < 1) points = 1;
+ if(group < 1) group = 1;
- if(before == 0 || before > rrdset_last_entry_t(st)) before = rrdset_last_entry_t(st);
- if(after == 0 || after < rrdset_first_entry_t(st)) after = rrdset_first_entry_t(st);
+ if(before == 0 || before > rrdset_last_entry_t(st)) before = rrdset_last_entry_t(st);
+ if(after == 0 || after < rrdset_first_entry_t(st)) after = rrdset_first_entry_t(st);
- // ---
+ // ---
- // our return value (the last timestamp printed)
- // this is required to detect re-transmit in google JSONP
- time_t last_timestamp = 0;
+ // our return value (the last timestamp printed)
+ // this is required to detect re-transmit in google JSONP
+ time_t last_timestamp = 0;
- // -------------------------------------------------------------------------
- // find how many dimensions we have
+ // -------------------------------------------------------------------------
+ // find how many dimensions we have
- int dimensions = 0;
- RRDDIM *rd;
- for( rd = st->dimensions ; rd ; rd = rd->next) dimensions++;
- if(!dimensions) {
- pthread_rwlock_unlock(&st->rwlock);
- buffer_strcat(wb, "No dimensions yet.");
- return 0;
- }
+ int dimensions = 0;
+ RRDDIM *rd;
+ for( rd = st->dimensions ; rd ; rd = rd->next) dimensions++;
+ if(!dimensions) {
+ pthread_rwlock_unlock(&st->rwlock);
+ buffer_strcat(wb, "No dimensions yet.");
+ return 0;
+ }
- // -------------------------------------------------------------------------
- // prepare various strings, to speed up the loop
+ // -------------------------------------------------------------------------
+ // prepare various strings, to speed up the loop
- char overflow_annotation[201]; snprintfz(overflow_annotation, 200, ",{%sv%s:%sRESET OR OVERFLOW%s},{%sv%s:%sThe counters have been wrapped.%s}", kq, kq, sq, sq, kq, kq, sq, sq);
- char normal_annotation[201]; snprintfz(normal_annotation, 200, ",{%sv%s:null},{%sv%s:null}", kq, kq, kq, kq);
- char pre_date[51]; snprintfz(pre_date, 50, " {%sc%s:[{%sv%s:%s", kq, kq, kq, kq, sq);
- char post_date[21]; snprintfz(post_date, 20, "%s}", sq);
- char pre_value[21]; snprintfz(pre_value, 20, ",{%sv%s:", kq, kq);
- char post_value[21]; strcpy(post_value, "}");
+ char overflow_annotation[201]; snprintfz(overflow_annotation, 200, ",{%sv%s:%sRESET OR OVERFLOW%s},{%sv%s:%sThe counters have been wrapped.%s}", kq, kq, sq, sq, kq, kq, sq, sq);
+ char normal_annotation[201]; snprintfz(normal_annotation, 200, ",{%sv%s:null},{%sv%s:null}", kq, kq, kq, kq);
+ char pre_date[51]; snprintfz(pre_date, 50, " {%sc%s:[{%sv%s:%s", kq, kq, kq, kq, sq);
+ char post_date[21]; snprintfz(post_date, 20, "%s}", sq);
+ char pre_value[21]; snprintfz(pre_value, 20, ",{%sv%s:", kq, kq);
+ char post_value[21]; strcpy(post_value, "}");
- // -------------------------------------------------------------------------
- // checks for debugging
+ // -------------------------------------------------------------------------
+ // checks for debugging
- if(st->debug) {
- debug(D_RRD_STATS, "%s first_entry_t = %lu, last_entry_t = %lu, duration = %lu, after = %lu, before = %lu, duration = %lu, entries_to_show = %lu, group = %lu"
- , st->id
- , rrdset_first_entry_t(st)
- , rrdset_last_entry_t(st)
- , rrdset_last_entry_t(st) - rrdset_first_entry_t(st)
- , after
- , before
- , before - after
- , points
- , group
- );
+ if(st->debug) {
+ debug(D_RRD_STATS, "%s first_entry_t = %ld, last_entry_t = %ld, duration = %ld, after = %ld, before = %ld, duration = %ld, entries_to_show = %ld, group = %ld"
+ , st->id
+ , rrdset_first_entry_t(st)
+ , rrdset_last_entry_t(st)
+ , rrdset_last_entry_t(st) - rrdset_first_entry_t(st)
+ , after
+ , before
+ , before - after
+ , points
+ , group
+ );
- if(before < after)
- debug(D_RRD_STATS, "WARNING: %s The newest value in the database (%lu) is earlier than the oldest (%lu)", st->name, before, after);
+ if(before < after)
+ debug(D_RRD_STATS, "WARNING: %s The newest value in the database (%ld) is earlier than the oldest (%ld)", st->name, before, after);
- if((before - after) > st->entries * st->update_every)
- debug(D_RRD_STATS, "WARNING: %s The time difference between the oldest and the newest entries (%lu) is higher than the capacity of the database (%lu)", st->name, before - after, st->entries * st->update_every);
- }
-
-
- // -------------------------------------------------------------------------
- // temp arrays for keeping values per dimension
-
- calculated_number group_values[dimensions]; // keep sums when grouping
- int print_hidden[dimensions]; // keep hidden flags
- int found_non_zero[dimensions];
- int found_non_existing[dimensions];
+ if((before - after) > st->entries * st->update_every)
+ debug(D_RRD_STATS, "WARNING: %s The time difference between the oldest and the newest entries (%ld) is higher than the capacity of the database (%ld)", st->name, before - after, st->entries * st->update_every);
+ }
+
+
+ // -------------------------------------------------------------------------
+ // temp arrays for keeping values per dimension
+
+ calculated_number group_values[dimensions]; // keep sums when grouping
+ int print_hidden[dimensions]; // keep hidden flags
+ int found_non_zero[dimensions];
+ int found_non_existing[dimensions];
- // initialize them
- for( rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
- group_values[c] = 0;
- print_hidden[c] = (rd->flags & RRDDIM_FLAG_HIDDEN)?1:0;
- found_non_zero[c] = 0;
- found_non_existing[c] = 0;
- }
+ // initialize them
+ for( rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
+ group_values[c] = 0;
+ print_hidden[c] = (rd->flags & RRDDIM_FLAG_HIDDEN)?1:0;
+ found_non_zero[c] = 0;
+ found_non_existing[c] = 0;
+ }
- // error("OLD: points=%d after=%d before=%d group=%d, duration=%d", entries_to_show, before - (st->update_every * group * entries_to_show), before, group, before - after + 1);
- // rrd2array(st, entries_to_show, before - (st->update_every * group * entries_to_show), before, group_method, only_non_zero);
- // rrd2rrdr(st, entries_to_show, before - (st->update_every * group * entries_to_show), before, group_method);
-
- // -------------------------------------------------------------------------
- // remove dimensions that contain only zeros
-
- int max_loop = 1;
- if(only_non_zero) max_loop = 2;
-
- for(; max_loop ; max_loop--) {
-
- // -------------------------------------------------------------------------
- // print the JSON header
-
- buffer_sprintf(wb, "{\n %scols%s:\n [\n", kq, kq);
- buffer_sprintf(wb, " {%sid%s:%s%s,%slabel%s:%stime%s,%spattern%s:%s%s,%stype%s:%sdatetime%s},\n", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq);
- buffer_sprintf(wb, " {%sid%s:%s%s,%slabel%s:%s%s,%spattern%s:%s%s,%stype%s:%sstring%s,%sp%s:{%srole%s:%sannotation%s}},\n", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, kq, kq, sq, sq);
- buffer_sprintf(wb, " {%sid%s:%s%s,%slabel%s:%s%s,%spattern%s:%s%s,%stype%s:%sstring%s,%sp%s:{%srole%s:%sannotationText%s}}", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, kq, kq, sq, sq);
-
- // print the header for each dimension
- // and update the print_hidden array for the dimensions that should be hidden
- int pc = 0;
- for( rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
- if(!print_hidden[c]) {
- pc++;
- buffer_sprintf(wb, ",\n {%sid%s:%s%s,%slabel%s:%s%s%s,%spattern%s:%s%s,%stype%s:%snumber%s}", kq, kq, sq, sq, kq, kq, sq, rd->name, sq, kq, kq, sq, sq, kq, kq, sq, sq);
- }
- }
- if(!pc) {
- buffer_sprintf(wb, ",\n {%sid%s:%s%s,%slabel%s:%s%s%s,%spattern%s:%s%s,%stype%s:%snumber%s}", kq, kq, sq, sq, kq, kq, sq, "no data", sq, kq, kq, sq, sq, kq, kq, sq, sq);
- }
-
- // print the begin of row data
- buffer_sprintf(wb, "\n ],\n %srows%s:\n [\n", kq, kq);
-
-
- // -------------------------------------------------------------------------
- // the main loop
-
- int annotate_reset = 0;
- int annotation_count = 0;
-
- long t = rrdset_time2slot(st, before),
- stop_at_t = rrdset_time2slot(st, after),
- stop_now = 0;
-
- t -= t % group;
-
- time_t now = rrdset_slot2time(st, t),
- dt = st->update_every;
-
- long count = 0, printed = 0, group_count = 0;
- last_timestamp = 0;
-
- if(st->debug) debug(D_RRD_STATS, "%s: REQUEST after:%lu before:%lu, points:%d, group:%d, CHART cur:%ld first: %lu last:%lu, CALC start_t:%ld, stop_t:%ld"
- , st->id
- , after
- , before
- , points
- , group
- , st->current_entry
- , rrdset_first_entry_t(st)
- , rrdset_last_entry_t(st)
- , t
- , stop_at_t
- );
-
- long counter = 0;
- for(; !stop_now ; now -= dt, t--, counter++) {
- if(t < 0) t = st->entries - 1;
- if(t == stop_at_t) stop_now = counter;
-
- int print_this = 0;
-
- if(st->debug) debug(D_RRD_STATS, "%s t = %ld, count = %ld, group_count = %ld, printed = %ld, now = %lu, %s %s"
- , st->id
- , t
- , count + 1
- , group_count + 1
- , printed
- , now
- , (group_count + 1 == group)?"PRINT":" - "
- , (now >= after && now <= before)?"RANGE":" - "
- );
-
-
- // make sure we return data in the proper time range
- if(now > before) continue;
- if(now < after) break;
-
- //if(rrdset_slot2time(st, t) != now)
- // error("%s: slot=%ld, now=%ld, slot2time=%ld, diff=%ld, last_entry_t=%ld, rrdset_last_slot=%ld", st->id, t, now, rrdset_slot2time(st,t), now - rrdset_slot2time(st,t), rrdset_last_entry_t(st), rrdset_last_slot(st));
-
- count++;
- group_count++;
-
- // check if we have to print this now
- if(group_count == group) {
- if(printed >= points) {
- // debug(D_RRD_STATS, "Already printed all rows. Stopping.");
- break;
- }
-
- // generate the local date time
- struct tm tmbuf, *tm = localtime_r(&now, &tmbuf);
- if(!tm) { error("localtime() failed."); continue; }
- if(now > last_timestamp) last_timestamp = now;
-
- if(printed) buffer_strcat(wb, "]},\n");
- buffer_strcat(wb, pre_date);
- buffer_jsdate(wb, tm->tm_year + 1900, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
- buffer_strcat(wb, post_date);
-
- print_this = 1;
- }
-
- // do the calculations
- for(rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
- storage_number n = rd->values[t];
- calculated_number value = unpack_storage_number(n);
-
- if(!does_storage_number_exist(n)) {
- value = 0.0;
- found_non_existing[c]++;
- }
- if(did_storage_number_reset(n)) annotate_reset = 1;
-
- switch(group_method) {
- case GROUP_MAX:
- if(abs(value) > abs(group_values[c])) group_values[c] = value;
- break;
-
- case GROUP_SUM:
- group_values[c] += value;
- break;
-
- default:
- case GROUP_AVERAGE:
- group_values[c] += value;
- if(print_this) group_values[c] /= ( group_count - found_non_existing[c] );
- break;
- }
- }
-
- if(print_this) {
- if(annotate_reset) {
- annotation_count++;
- buffer_strcat(wb, overflow_annotation);
- annotate_reset = 0;
- }
- else
- buffer_strcat(wb, normal_annotation);
-
- pc = 0;
- for(c = 0 ; c < dimensions ; c++) {
- if(found_non_existing[c] == group_count) {
- // all entries are non-existing
- pc++;
- buffer_strcat(wb, pre_value);
- buffer_strcat(wb, "null");
- buffer_strcat(wb, post_value);
- }
- else if(!print_hidden[c]) {
- pc++;
- buffer_strcat(wb, pre_value);
- buffer_rrd_value(wb, group_values[c]);
- buffer_strcat(wb, post_value);
-
- if(group_values[c]) found_non_zero[c]++;
- }
-
- // reset them for the next loop
- group_values[c] = 0;
- found_non_existing[c] = 0;
- }
-
- // if all dimensions are hidden, print a null
- if(!pc) {
- buffer_strcat(wb, pre_value);
- buffer_strcat(wb, "null");
- buffer_strcat(wb, post_value);
- }
-
- printed++;
- group_count = 0;
- }
- }
-
- if(printed) buffer_strcat(wb, "]}");
- buffer_strcat(wb, "\n ]\n}\n");
-
- if(only_non_zero && max_loop > 1) {
- int changed = 0;
- for(rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
- group_values[c] = 0;
- found_non_existing[c] = 0;
-
- if(!print_hidden[c] && !found_non_zero[c]) {
- changed = 1;
- print_hidden[c] = 1;
- }
- }
-
- if(changed) buffer_flush(wb);
- else break;
- }
- else break;
-
- } // max_loop
-
- debug(D_RRD_STATS, "RRD_STATS_JSON: %s total %ld bytes", st->name, wb->len);
-
- pthread_rwlock_unlock(&st->rwlock);
- return last_timestamp;
+ // error("OLD: points=%d after=%d before=%d group=%d, duration=%d", entries_to_show, before - (st->update_every * group * entries_to_show), before, group, before - after + 1);
+ // rrd2array(st, entries_to_show, before - (st->update_every * group * entries_to_show), before, group_method, only_non_zero);
+ // rrd2rrdr(st, entries_to_show, before - (st->update_every * group * entries_to_show), before, group_method);
+
+ // -------------------------------------------------------------------------
+ // remove dimensions that contain only zeros
+
+ int max_loop = 1;
+ if(only_non_zero) max_loop = 2;
+
+ for(; max_loop ; max_loop--) {
+
+ // -------------------------------------------------------------------------
+ // print the JSON header
+
+ buffer_sprintf(wb, "{\n %scols%s:\n [\n", kq, kq);
+ buffer_sprintf(wb, " {%sid%s:%s%s,%slabel%s:%stime%s,%spattern%s:%s%s,%stype%s:%sdatetime%s},\n", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq);
+ buffer_sprintf(wb, " {%sid%s:%s%s,%slabel%s:%s%s,%spattern%s:%s%s,%stype%s:%sstring%s,%sp%s:{%srole%s:%sannotation%s}},\n", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, kq, kq, sq, sq);
+ buffer_sprintf(wb, " {%sid%s:%s%s,%slabel%s:%s%s,%spattern%s:%s%s,%stype%s:%sstring%s,%sp%s:{%srole%s:%sannotationText%s}}", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, kq, kq, sq, sq);
+
+ // print the header for each dimension
+ // and update the print_hidden array for the dimensions that should be hidden
+ int pc = 0;
+ for( rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
+ if(!print_hidden[c]) {
+ pc++;
+ buffer_sprintf(wb, ",\n {%sid%s:%s%s,%slabel%s:%s%s%s,%spattern%s:%s%s,%stype%s:%snumber%s}", kq, kq, sq, sq, kq, kq, sq, rd->name, sq, kq, kq, sq, sq, kq, kq, sq, sq);
+ }
+ }
+ if(!pc) {
+ buffer_sprintf(wb, ",\n {%sid%s:%s%s,%slabel%s:%s%s%s,%spattern%s:%s%s,%stype%s:%snumber%s}", kq, kq, sq, sq, kq, kq, sq, "no data", sq, kq, kq, sq, sq, kq, kq, sq, sq);
+ }
+
+ // print the begin of row data
+ buffer_sprintf(wb, "\n ],\n %srows%s:\n [\n", kq, kq);
+
+
+ // -------------------------------------------------------------------------
+ // the main loop
+
+ int annotate_reset = 0;
+ int annotation_count = 0;
+
+ long t = rrdset_time2slot(st, before),
+ stop_at_t = rrdset_time2slot(st, after),
+ stop_now = 0;
+
+ t -= t % group;
+
+ time_t now = rrdset_slot2time(st, t),
+ dt = st->update_every;
+
+ long count = 0, printed = 0, group_count = 0;
+ last_timestamp = 0;
+
+ if(st->debug) debug(D_RRD_STATS, "%s: REQUEST after:%u before:%u, points:%ld, group:%ld, CHART cur:%ld first: %u last:%u, CALC start_t:%ld, stop_t:%ld"
+ , st->id
+ , (uint32_t)after
+ , (uint32_t)before
+ , points
+ , group
+ , st->current_entry
+ , (uint32_t)rrdset_first_entry_t(st)
+ , (uint32_t)rrdset_last_entry_t(st)
+ , t
+ , stop_at_t
+ );
+
+ long counter = 0;
+ for(; !stop_now ; now -= dt, t--, counter++) {
+ if(t < 0) t = st->entries - 1;
+ if(t == stop_at_t) stop_now = counter;
+
+ int print_this = 0;
+
+ if(st->debug) debug(D_RRD_STATS, "%s t = %ld, count = %ld, group_count = %ld, printed = %ld, now = %ld, %s %s"
+ , st->id
+ , t
+ , count + 1
+ , group_count + 1
+ , printed
+ , now
+ , (group_count + 1 == group)?"PRINT":" - "
+ , (now >= after && now <= before)?"RANGE":" - "
+ );
+
+
+ // make sure we return data in the proper time range
+ if(now > before) continue;
+ if(now < after) break;
+
+ //if(rrdset_slot2time(st, t) != now)
+ // error("%s: slot=%ld, now=%ld, slot2time=%ld, diff=%ld, last_entry_t=%ld, rrdset_last_slot=%ld", st->id, t, now, rrdset_slot2time(st,t), now - rrdset_slot2time(st,t), rrdset_last_entry_t(st), rrdset_last_slot(st));
+
+ count++;
+ group_count++;
+
+ // check if we have to print this now
+ if(group_count == group) {
+ if(printed >= points) {
+ // debug(D_RRD_STATS, "Already printed all rows. Stopping.");
+ break;
+ }
+
+ // generate the local date time
+ struct tm tmbuf, *tm = localtime_r(&now, &tmbuf);
+ if(!tm) { error("localtime() failed."); continue; }
+ if(now > last_timestamp) last_timestamp = now;
+
+ if(printed) buffer_strcat(wb, "]},\n");
+ buffer_strcat(wb, pre_date);
+ buffer_jsdate(wb, tm->tm_year + 1900, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
+ buffer_strcat(wb, post_date);
+
+ print_this = 1;
+ }
+
+ // do the calculations
+ for(rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
+ storage_number n = rd->values[t];
+ calculated_number value = unpack_storage_number(n);
+
+ if(!does_storage_number_exist(n)) {
+ value = 0.0;
+ found_non_existing[c]++;
+ }
+ if(did_storage_number_reset(n)) annotate_reset = 1;
+
+ switch(group_method) {
+ case GROUP_MAX:
+ if(abs(value) > abs(group_values[c])) group_values[c] = value;
+ break;
+
+ case GROUP_SUM:
+ group_values[c] += value;
+ break;
+
+ default:
+ case GROUP_AVERAGE:
+ group_values[c] += value;
+ if(print_this) group_values[c] /= ( group_count - found_non_existing[c] );
+ break;
+ }
+ }
+
+ if(print_this) {
+ if(annotate_reset) {
+ annotation_count++;
+ buffer_strcat(wb, overflow_annotation);
+ annotate_reset = 0;
+ }
+ else
+ buffer_strcat(wb, normal_annotation);
+
+ pc = 0;
+ for(c = 0 ; c < dimensions ; c++) {
+ if(found_non_existing[c] == group_count) {
+ // all entries are non-existing
+ pc++;
+ buffer_strcat(wb, pre_value);
+ buffer_strcat(wb, "null");
+ buffer_strcat(wb, post_value);
+ }
+ else if(!print_hidden[c]) {
+ pc++;
+ buffer_strcat(wb, pre_value);
+ buffer_rrd_value(wb, group_values[c]);
+ buffer_strcat(wb, post_value);
+
+ if(group_values[c]) found_non_zero[c]++;
+ }
+
+ // reset them for the next loop
+ group_values[c] = 0;
+ found_non_existing[c] = 0;
+ }
+
+ // if all dimensions are hidden, print a null
+ if(!pc) {
+ buffer_strcat(wb, pre_value);
+ buffer_strcat(wb, "null");
+ buffer_strcat(wb, post_value);
+ }
+
+ printed++;
+ group_count = 0;
+ }
+ }
+
+ if(printed) buffer_strcat(wb, "]}");
+ buffer_strcat(wb, "\n ]\n}\n");
+
+ if(only_non_zero && max_loop > 1) {
+ int changed = 0;
+ for(rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
+ group_values[c] = 0;
+ found_non_existing[c] = 0;
+
+ if(!print_hidden[c] && !found_non_zero[c]) {
+ changed = 1;
+ print_hidden[c] = 1;
+ }
+ }
+
+ if(changed) buffer_flush(wb);
+ else break;
+ }
+ else break;
+
+ } // max_loop
+
+ debug(D_RRD_STATS, "RRD_STATS_JSON: %s total %lu bytes", st->name, wb->len);
+
+ pthread_rwlock_unlock(&st->rwlock);
+ return last_timestamp;
}
diff --git a/src/rrd2json.h b/src/rrd2json.h
index d1f850d4a..e3e5cb690 100644
--- a/src/rrd2json.h
+++ b/src/rrd2json.h
@@ -1,8 +1,3 @@
-#include <time.h>
-
-#include "web_buffer.h"
-#include "rrd.h"
-
#ifndef NETDATA_RRD2JSON_H
#define NETDATA_RRD2JSON_H 1
@@ -35,22 +30,26 @@ extern char *hostname;
#define DATASOURCE_FORMAT_SSV_COMMA "ssvcomma"
#define DATASOURCE_FORMAT_CSV_JSON_ARRAY "csvjsonarray"
-#define GROUP_AVERAGE 0
-#define GROUP_MAX 1
-#define GROUP_SUM 2
+#define GROUP_UNDEFINED 0
+#define GROUP_AVERAGE 1
+#define GROUP_MIN 2
+#define GROUP_MAX 3
+#define GROUP_SUM 4
+#define GROUP_INCREMENTAL_SUM 5
-#define RRDR_OPTION_NONZERO 0x00000001 // don't output dimensions will just zero values
-#define RRDR_OPTION_REVERSED 0x00000002 // output the rows in reverse order (oldest to newest)
-#define RRDR_OPTION_ABSOLUTE 0x00000004 // values positive, for DATASOURCE_SSV before summing
-#define RRDR_OPTION_MIN2MAX 0x00000008 // for DATASOURCE_SSV, out max - min, instead of sum
-#define RRDR_OPTION_SECONDS 0x00000010 // output seconds, instead of dates
-#define RRDR_OPTION_MILLISECONDS 0x00000020 // output milliseconds, instead of dates
-#define RRDR_OPTION_NULL2ZERO 0x00000040 // do not show nulls, convert them to zeros
-#define RRDR_OPTION_OBJECTSROWS 0x00000080 // each row of values should be an object, not an array
-#define RRDR_OPTION_GOOGLE_JSON 0x00000100 // comply with google JSON/JSONP specs
-#define RRDR_OPTION_JSON_WRAP 0x00000200 // wrap the response in a JSON header with info about the result
-#define RRDR_OPTION_LABEL_QUOTES 0x00000400 // in CSV output, wrap header labels in double quotes
-#define RRDR_OPTION_PERCENTAGE 0x00000800 // give values as percentage of total
+#define RRDR_OPTION_NONZERO 0x00000001 // don't output dimensions will just zero values
+#define RRDR_OPTION_REVERSED 0x00000002 // output the rows in reverse order (oldest to newest)
+#define RRDR_OPTION_ABSOLUTE 0x00000004 // values positive, for DATASOURCE_SSV before summing
+#define RRDR_OPTION_MIN2MAX 0x00000008 // when adding dimensions, use max - min, instead of sum
+#define RRDR_OPTION_SECONDS 0x00000010 // output seconds, instead of dates
+#define RRDR_OPTION_MILLISECONDS 0x00000020 // output milliseconds, instead of dates
+#define RRDR_OPTION_NULL2ZERO 0x00000040 // do not show nulls, convert them to zeros
+#define RRDR_OPTION_OBJECTSROWS 0x00000080 // each row of values should be an object, not an array
+#define RRDR_OPTION_GOOGLE_JSON 0x00000100 // comply with google JSON/JSONP specs
+#define RRDR_OPTION_JSON_WRAP 0x00000200 // wrap the response in a JSON header with info about the result
+#define RRDR_OPTION_LABEL_QUOTES 0x00000400 // in CSV output, wrap header labels in double quotes
+#define RRDR_OPTION_PERCENTAGE 0x00000800 // give values as percentage of total
+#define RRDR_OPTION_NOT_ALIGNED 0x00001000 // do not align charts for persistant timeframes
extern void rrd_stats_api_v1_chart(RRDSET *st, BUFFER *wb);
extern void rrd_stats_api_v1_charts(BUFFER *wb);
@@ -64,6 +63,6 @@ extern void rrd_stats_all_json(BUFFER *wb);
extern time_t rrd_stats_json(int type, RRDSET *st, BUFFER *wb, long entries_to_show, long group, int group_method, time_t after, time_t before, int only_non_zero);
extern int rrd2format(RRDSET *st, BUFFER *out, BUFFER *dimensions, uint32_t format, long points, long long after, long long before, int group_method, uint32_t options, time_t *latest_timestamp);
-
+extern int rrd2value(RRDSET *st, BUFFER *wb, calculated_number *n, const char *dimensions, long points, long long after, long long before, int group_method, uint32_t options, time_t *db_before, time_t *db_after, int *value_is_null);
#endif /* NETDATA_RRD2JSON_H */
diff --git a/src/storage_number.c b/src/storage_number.c
index b5c5f4067..27fe5f2c7 100644
--- a/src/storage_number.c
+++ b/src/storage_number.c
@@ -1,206 +1,168 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#ifdef STORAGE_WITH_MATH
-#include <math.h>
-#endif
-
#include "common.h"
-#include "log.h"
-#include "storage_number.h"
-#if __GNUC__
-#if __x86_64__ || __ppc64__
-#define ENVIRONMENT64
-#else
-#define ENVIRONMENT32
-#endif
-#endif
+extern char *print_number_lu_r(char *str, unsigned long uvalue);
+extern char *print_number_llu_r(char *str, unsigned long long uvalue);
storage_number pack_storage_number(calculated_number value, uint32_t flags)
{
- // bit 32 = sign 0:positive, 1:negative
- // bit 31 = 0:divide, 1:multiply
- // bit 30, 29, 28 = (multiplier or divider) 0-6 (7 total)
- // bit 27, 26, 25 flags
- // bit 24 to bit 1 = the value
-
- storage_number r = get_storage_number_flags(flags);
- if(!value) return r;
-
- int m = 0;
- calculated_number n = value;
-
- // if the value is negative
- // add the sign bit and make it positive
- if(n < 0) {
- r += (1 << 31); // the sign bit 32
- n = -n;
- }
-
- // make its integer part fit in 0x00ffffff
- // by dividing it by 10 up to 7 times
- // and increasing the multiplier
- while(m < 7 && n > (calculated_number)0x00ffffff) {
- n /= 10;
- m++;
- }
-
- if(m) {
- // the value was too big and we divided it
- // so we add a multiplier to unpack it
- r += (1 << 30) + (m << 27); // the multiplier m
-
- if(n > (calculated_number)0x00ffffff) {
- error("Number " CALCULATED_NUMBER_FORMAT " is too big.", value);
- r += 0x00ffffff;
- return r;
- }
- }
- else {
- // 0x0019999e is the number that can be multiplied
- // by 10 to give 0x00ffffff
- // while the value is below 0x0019999e we can
- // multiply it by 10, up to 7 times, increasing
- // the multiplier
- while(m < 7 && n < (calculated_number)0x0019999e) {
- n *= 10;
- m++;
- }
-
- // the value was small enough and we multiplied it
- // so we add a divider to unpack it
- r += (0 << 30) + (m << 27); // the divider m
- }
+ // bit 32 = sign 0:positive, 1:negative
+ // bit 31 = 0:divide, 1:multiply
+ // bit 30, 29, 28 = (multiplier or divider) 0-6 (7 total)
+ // bit 27, 26, 25 flags
+ // bit 24 to bit 1 = the value
+
+ storage_number r = get_storage_number_flags(flags);
+ if(!value) return r;
+
+ int m = 0;
+ calculated_number n = value;
+
+ // if the value is negative
+ // add the sign bit and make it positive
+ if(n < 0) {
+ r += (1 << 31); // the sign bit 32
+ n = -n;
+ }
+
+ // make its integer part fit in 0x00ffffff
+ // by dividing it by 10 up to 7 times
+ // and increasing the multiplier
+ while(m < 7 && n > (calculated_number)0x00ffffff) {
+ n /= 10;
+ m++;
+ }
+
+ if(m) {
+ // the value was too big and we divided it
+ // so we add a multiplier to unpack it
+ r += (1 << 30) + (m << 27); // the multiplier m
+
+ if(n > (calculated_number)0x00ffffff) {
+ error("Number " CALCULATED_NUMBER_FORMAT " is too big.", value);
+ r += 0x00ffffff;
+ return r;
+ }
+ }
+ else {
+ // 0x0019999e is the number that can be multiplied
+ // by 10 to give 0x00ffffff
+ // while the value is below 0x0019999e we can
+ // multiply it by 10, up to 7 times, increasing
+ // the multiplier
+ while(m < 7 && n < (calculated_number)0x0019999e) {
+ n *= 10;
+ m++;
+ }
+
+ // the value was small enough and we multiplied it
+ // so we add a divider to unpack it
+ r += (0 << 30) + (m << 27); // the divider m
+ }
#ifdef STORAGE_WITH_MATH
- // without this there are rounding problems
- // example: 0.9 becomes 0.89
- r += lrint((double) n);
+ // without this there are rounding problems
+ // example: 0.9 becomes 0.89
+ r += lrint((double) n);
#else
- r += (storage_number)n;
+ r += (storage_number)n;
#endif
- return r;
+ return r;
}
calculated_number unpack_storage_number(storage_number value)
{
- if(!value) return 0;
-
- int sign = 0, exp = 0;
+ if(!value) return 0;
- value ^= get_storage_number_flags(value);
+ int sign = 0, exp = 0;
- if(value & (1 << 31)) {
- sign = 1;
- value ^= 1 << 31;
- }
+ value ^= get_storage_number_flags(value);
- if(value & (1 << 30)) {
- exp = 1;
- value ^= 1 << 30;
- }
+ if(value & (1 << 31)) {
+ sign = 1;
+ value ^= 1 << 31;
+ }
- int mul = value >> 27;
- value ^= mul << 27;
+ if(value & (1 << 30)) {
+ exp = 1;
+ value ^= 1 << 30;
+ }
- calculated_number n = value;
+ int mul = value >> 27;
+ value ^= mul << 27;
- // fprintf(stderr, "UNPACK: %08X, sign = %d, exp = %d, mul = %d, n = " CALCULATED_NUMBER_FORMAT "\n", value, sign, exp, mul, n);
+ calculated_number n = value;
- while(mul > 0) {
- if(exp) n *= 10;
- else n /= 10;
- mul--;
- }
+ // fprintf(stderr, "UNPACK: %08X, sign = %d, exp = %d, mul = %d, n = " CALCULATED_NUMBER_FORMAT "\n", value, sign, exp, mul, n);
- if(sign) n = -n;
- return n;
-}
-
-#ifdef ENVIRONMENT32
-// This trick seems to give an 80% speed increase in 32bit systems
-// print_calculated_number_llu_r() will just print the digits up to the
-// point the remaining value fits in 32 bits, and then calls
-// print_calculated_number_lu_r() to print the rest with 32 bit arithmetic.
-
-static char *print_calculated_number_lu_r(char *str, unsigned long uvalue) {
- char *wstr = str;
+ while(mul > 0) {
+ if(exp) n *= 10;
+ else n /= 10;
+ mul--;
+ }
- // print each digit
- do *wstr++ = (char)('0' + (uvalue % 10)); while(uvalue /= 10);
- return wstr;
+ if(sign) n = -n;
+ return n;
}
-static char *print_calculated_number_llu_r(char *str, unsigned long long uvalue) {
- char *wstr = str;
-
- // print each digit
- do *wstr++ = (char)('0' + (uvalue % 10)); while((uvalue /= 10) && uvalue > (unsigned long long)0xffffffff);
- if(uvalue) return print_calculated_number_lu_r(wstr, uvalue);
- return wstr;
-}
-#endif
-
int print_calculated_number(char *str, calculated_number value)
{
- char *wstr = str;
+ char *wstr = str;
- int sign = (value < 0) ? 1 : 0;
- if(sign) value = -value;
+ int sign = (value < 0) ? 1 : 0;
+ if(sign) value = -value;
#ifdef STORAGE_WITH_MATH
- // without llrint() there are rounding problems
- // for example 0.9 becomes 0.89
- unsigned long long uvalue = (unsigned long long int) llrint(value * (calculated_number)100000);
+ // without llrint() there are rounding problems
+ // for example 0.9 becomes 0.89
+ unsigned long long uvalue = (unsigned long long int) llrint(value * (calculated_number)100000);
#else
- unsigned long long uvalue = value * (calculated_number)100000;
+ unsigned long long uvalue = value * (calculated_number)100000;
#endif
#ifdef ENVIRONMENT32
- if(uvalue > (unsigned long long)0xffffffff)
- wstr = print_calculated_number_llu_r(str, uvalue);
- else
- wstr = print_calculated_number_lu_r(str, uvalue);
+ if(uvalue > (unsigned long long)0xffffffff)
+ wstr = print_number_llu_r(str, uvalue);
+ else
+ wstr = print_number_lu_r(str, uvalue);
#else
- do *wstr++ = (char)('0' + (uvalue % 10)); while(uvalue /= 10);
+ do *wstr++ = (char)('0' + (uvalue % 10)); while(uvalue /= 10);
#endif
- // make sure we have 6 bytes at least
- while((wstr - str) < 6) *wstr++ = '0';
+ // make sure we have 6 bytes at least
+ while((wstr - str) < 6) *wstr++ = '0';
- // put the sign back
- if(sign) *wstr++ = '-';
+ // put the sign back
+ if(sign) *wstr++ = '-';
- // reverse it
+ // reverse it
char *begin = str, *end = --wstr, aux;
while (end > begin) aux = *end, *end-- = *begin, *begin++ = aux;
- // wstr--;
- // strreverse(str, wstr);
-
- // remove trailing zeros
- int decimal = 5;
- while(decimal > 0 && *wstr == '0') {
- *wstr-- = '\0';
- decimal--;
- }
-
- // terminate it, one position to the right
- // to let space for a dot
- wstr[2] = '\0';
-
- // make space for the dot
- int i;
- for(i = 0; i < decimal ;i++) {
- wstr[1] = wstr[0];
- wstr--;
- }
-
- // put the dot
- if(wstr[2] == '\0') { wstr[1] = '\0'; decimal--; }
- else wstr[1] = '.';
-
- // return the buffer length
- return (int) ((wstr - str) + 2 + decimal );
+ // wstr--;
+ // strreverse(str, wstr);
+
+ // remove trailing zeros
+ int decimal = 5;
+ while(decimal > 0 && *wstr == '0') {
+ *wstr-- = '\0';
+ decimal--;
+ }
+
+ // terminate it, one position to the right
+ // to let space for a dot
+ wstr[2] = '\0';
+
+ // make space for the dot
+ int i;
+ for(i = 0; i < decimal ;i++) {
+ wstr[1] = wstr[0];
+ wstr--;
+ }
+
+ // put the dot
+ if(wstr[2] == '\0') { wstr[1] = '\0'; decimal--; }
+ else wstr[1] = '.';
+
+ // return the buffer length
+ return (int) ((wstr - str) + 2 + decimal );
}
diff --git a/src/storage_number.h b/src/storage_number.h
index f35e99ddd..74d24a322 100644
--- a/src/storage_number.h
+++ b/src/storage_number.h
@@ -14,19 +14,18 @@ typedef long double collected_number;
#define COLLECTED_NUMBER_FORMAT "%0.7Lf"
*/
-typedef int32_t storage_number;
-typedef uint32_t ustorage_number;
-#define STORAGE_NUMBER_FORMAT "%d"
-
-#define SN_NOT_EXISTS (0x0 << 24)
-#define SN_EXISTS (0x1 << 24)
-#define SN_EXISTS_RESET (0x2 << 24)
-#define SN_EXISTS_UNDEF1 (0x3 << 24)
-#define SN_EXISTS_UNDEF2 (0x4 << 24)
-#define SN_EXISTS_UNDEF3 (0x5 << 24)
-#define SN_EXISTS_UNDEF4 (0x6 << 24)
-
-#define SN_FLAGS_MASK (~(0x6 << 24))
+typedef uint32_t storage_number;
+#define STORAGE_NUMBER_FORMAT "%u"
+
+#define SN_NOT_EXISTS (0x0 << 24)
+#define SN_EXISTS (0x1 << 24)
+#define SN_EXISTS_RESET (0x2 << 24)
+#define SN_EXISTS_UNDEF1 (0x3 << 24)
+#define SN_EXISTS_UNDEF2 (0x4 << 24)
+#define SN_EXISTS_UNDEF3 (0x5 << 24)
+#define SN_EXISTS_UNDEF4 (0x6 << 24)
+
+#define SN_FLAGS_MASK (~(0x6 << 24))
// extract the flags
#define get_storage_number_flags(value) ((((storage_number)value) & (1 << 24)) | (((storage_number)value) & (2 << 24)) | (((storage_number)value) & (4 << 24)))
diff --git a/src/sys_fs_cgroup.c b/src/sys_fs_cgroup.c
index 9f3d3f0fd..892f737c1 100644
--- a/src/sys_fs_cgroup.c
+++ b/src/sys_fs_cgroup.c
@@ -1,22 +1,4 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <inttypes.h>
-#include <sys/types.h>
-#include <dirent.h>
-#include <string.h>
-
#include "common.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "log.h"
-#include "rrd.h"
-#include "main.h"
-#include "popen.h"
-#include "proc_self_mountinfo.h"
// ----------------------------------------------------------------------------
// cgroup globals
@@ -24,164 +6,187 @@
static int cgroup_enable_cpuacct_stat = CONFIG_ONDEMAND_ONDEMAND;
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_new_cgroups_detected_at_runtime = 1;
static int cgroup_check_for_new_every = 10;
static char *cgroup_cpuacct_base = NULL;
static char *cgroup_blkio_base = NULL;
static char *cgroup_memory_base = NULL;
+static char *cgroup_devices_base = NULL;
static int cgroup_root_count = 0;
static int cgroup_root_max = 500;
static int cgroup_max_depth = 0;
void read_cgroup_plugin_configuration() {
- cgroup_check_for_new_every = config_get_number("plugin:cgroups", "check for new cgroups every", cgroup_check_for_new_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);
-
- char filename[FILENAME_MAX + 1], *s;
- struct mountinfo *mi, *root = mountinfo_read();
-
- mi = mountinfo_find_by_filesystem_mount_source(root, "cgroup", "cpuacct");
- if(!mi) mi = mountinfo_find_by_filesystem_super_option(root, "cgroup", "cpuacct");
- if(!mi) s = "/sys/fs/cgroup/cpuacct";
- else s = mi->mount_point;
- snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, s);
- cgroup_cpuacct_base = config_get("plugin:cgroups", "path to /sys/fs/cgroup/cpuacct", filename);
-
- mi = mountinfo_find_by_filesystem_mount_source(root, "cgroup", "blkio");
- if(!mi) mi = mountinfo_find_by_filesystem_super_option(root, "cgroup", "blkio");
- if(!mi) s = "/sys/fs/cgroup/blkio";
- else s = mi->mount_point;
- snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, s);
- cgroup_blkio_base = config_get("plugin:cgroups", "path to /sys/fs/cgroup/blkio", filename);
-
- mi = mountinfo_find_by_filesystem_mount_source(root, "cgroup", "memory");
- if(!mi) mi = mountinfo_find_by_filesystem_super_option(root, "cgroup", "memory");
- if(!mi) s = "/sys/fs/cgroup/memory";
- else s = mi->mount_point;
- snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, s);
- cgroup_memory_base = config_get("plugin:cgroups", "path to /sys/fs/cgroup/memory", filename);
-
- cgroup_root_max = config_get_number("plugin:cgroups", "max cgroups to allow", cgroup_root_max);
- cgroup_max_depth = config_get_number("plugin:cgroups", "max cgroups depth to monitor", cgroup_max_depth);
-
- cgroup_enable_new_cgroups_detected_at_runtime = config_get_boolean("plugin:cgroups", "enable new cgroups detected at run time", cgroup_enable_new_cgroups_detected_at_runtime);
-
- mountinfo_free(root);
+ cgroup_check_for_new_every = config_get_number("plugin:cgroups", "check for new cgroups every", cgroup_check_for_new_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);
+
+ char filename[FILENAME_MAX + 1], *s;
+ struct mountinfo *mi, *root = mountinfo_read();
+
+ mi = mountinfo_find_by_filesystem_super_option(root, "cgroup", "cpuacct");
+ if(!mi) mi = mountinfo_find_by_filesystem_mount_source(root, "cgroup", "cpuacct");
+ if(!mi) {
+ error("Cannot find cgroup cpuacct mountinfo. Assuming default: /sys/fs/cgroup/cpuacct");
+ s = "/sys/fs/cgroup/cpuacct";
+ }
+ else s = mi->mount_point;
+ snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, s);
+ cgroup_cpuacct_base = config_get("plugin:cgroups", "path to /sys/fs/cgroup/cpuacct", filename);
+
+ mi = mountinfo_find_by_filesystem_super_option(root, "cgroup", "blkio");
+ if(!mi) mi = mountinfo_find_by_filesystem_mount_source(root, "cgroup", "blkio");
+ if(!mi) {
+ error("Cannot find cgroup blkio mountinfo. Assuming default: /sys/fs/cgroup/blkio");
+ s = "/sys/fs/cgroup/blkio";
+ }
+ else s = mi->mount_point;
+ snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, s);
+ cgroup_blkio_base = config_get("plugin:cgroups", "path to /sys/fs/cgroup/blkio", filename);
+
+ mi = mountinfo_find_by_filesystem_super_option(root, "cgroup", "memory");
+ if(!mi) mi = mountinfo_find_by_filesystem_mount_source(root, "cgroup", "memory");
+ if(!mi) {
+ error("Cannot find cgroup memory mountinfo. Assuming default: /sys/fs/cgroup/memory");
+ s = "/sys/fs/cgroup/memory";
+ }
+ else s = mi->mount_point;
+ snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, s);
+ cgroup_memory_base = config_get("plugin:cgroups", "path to /sys/fs/cgroup/memory", filename);
+
+ mi = mountinfo_find_by_filesystem_super_option(root, "cgroup", "devices");
+ if(!mi) mi = mountinfo_find_by_filesystem_mount_source(root, "cgroup", "devices");
+ if(!mi) {
+ error("Cannot find cgroup devices mountinfo. Assuming default: /sys/fs/cgroup/devices");
+ s = "/sys/fs/cgroup/devices";
+ }
+ else s = mi->mount_point;
+ snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, s);
+ cgroup_devices_base = config_get("plugin:cgroups", "path to /sys/fs/cgroup/devices", filename);
+
+ cgroup_root_max = config_get_number("plugin:cgroups", "max cgroups to allow", cgroup_root_max);
+ cgroup_max_depth = config_get_number("plugin:cgroups", "max cgroups depth to monitor", cgroup_max_depth);
+
+ cgroup_enable_new_cgroups_detected_at_runtime = config_get_boolean("plugin:cgroups", "enable new cgroups detected at run time", cgroup_enable_new_cgroups_detected_at_runtime);
+
+ mountinfo_free(root);
}
// ----------------------------------------------------------------------------
// cgroup objects
struct blkio {
- int updated;
+ int updated;
- char *filename;
+ char *filename;
- unsigned long long Read;
- unsigned long long Write;
+ unsigned long long Read;
+ unsigned long long Write;
/*
- unsigned long long Sync;
- unsigned long long Async;
- unsigned long long Total;
+ unsigned long long Sync;
+ unsigned long long Async;
+ unsigned long long Total;
*/
};
// https://www.kernel.org/doc/Documentation/cgroup-v1/memory.txt
struct memory {
- int updated;
-
- char *filename;
-
- int has_dirty_swap;
-
- unsigned long long cache;
- unsigned long long rss;
- unsigned long long rss_huge;
- unsigned long long mapped_file;
- unsigned long long writeback;
- unsigned long long dirty;
- unsigned long long swap;
- unsigned long long pgpgin;
- unsigned long long pgpgout;
- unsigned long long pgfault;
- unsigned long long pgmajfault;
+ int updated;
+
+ char *filename;
+
+ int has_dirty_swap;
+
+ unsigned long long cache;
+ unsigned long long rss;
+ unsigned long long rss_huge;
+ unsigned long long mapped_file;
+ unsigned long long writeback;
+ unsigned long long dirty;
+ unsigned long long swap;
+ unsigned long long pgpgin;
+ unsigned long long pgpgout;
+ unsigned long long pgfault;
+ unsigned long long pgmajfault;
/*
- unsigned long long inactive_anon;
- unsigned long long active_anon;
- unsigned long long inactive_file;
- unsigned long long active_file;
- unsigned long long unevictable;
- unsigned long long hierarchical_memory_limit;
- unsigned long long total_cache;
- unsigned long long total_rss;
- unsigned long long total_rss_huge;
- unsigned long long total_mapped_file;
- unsigned long long total_writeback;
- unsigned long long total_dirty;
- unsigned long long total_swap;
- unsigned long long total_pgpgin;
- unsigned long long total_pgpgout;
- unsigned long long total_pgfault;
- unsigned long long total_pgmajfault;
- unsigned long long total_inactive_anon;
- unsigned long long total_active_anon;
- unsigned long long total_inactive_file;
- unsigned long long total_active_file;
- unsigned long long total_unevictable;
+ unsigned long long inactive_anon;
+ unsigned long long active_anon;
+ unsigned long long inactive_file;
+ unsigned long long active_file;
+ unsigned long long unevictable;
+ unsigned long long hierarchical_memory_limit;
+ unsigned long long total_cache;
+ unsigned long long total_rss;
+ unsigned long long total_rss_huge;
+ unsigned long long total_mapped_file;
+ unsigned long long total_writeback;
+ unsigned long long total_dirty;
+ unsigned long long total_swap;
+ unsigned long long total_pgpgin;
+ unsigned long long total_pgpgout;
+ unsigned long long total_pgfault;
+ unsigned long long total_pgmajfault;
+ unsigned long long total_inactive_anon;
+ unsigned long long total_active_anon;
+ unsigned long long total_inactive_file;
+ unsigned long long total_active_file;
+ unsigned long long total_unevictable;
*/
};
// https://www.kernel.org/doc/Documentation/cgroup-v1/cpuacct.txt
struct cpuacct_stat {
- int updated;
+ int updated;
- char *filename;
+ char *filename;
- unsigned long long user;
- unsigned long long system;
+ unsigned long long user;
+ unsigned long long system;
};
// https://www.kernel.org/doc/Documentation/cgroup-v1/cpuacct.txt
struct cpuacct_usage {
- int updated;
+ int updated;
- char *filename;
+ char *filename;
- unsigned int cpus;
- unsigned long long *cpu_percpu;
+ unsigned int cpus;
+ unsigned long long *cpu_percpu;
};
struct cgroup {
- int available; // found in the filesystem
- int enabled; // enabled in the config
+ int available; // found in the filesystem
+ int enabled; // enabled in the config
+
+ char *id;
+ uint32_t hash;
- char *id;
- uint32_t hash;
+ char *chart_id;
+ uint32_t hash_chart;
- char *chart_id;
- char *chart_title;
+ char *chart_title;
- struct cpuacct_stat cpuacct_stat;
- struct cpuacct_usage cpuacct_usage;
+ struct cpuacct_stat cpuacct_stat;
+ struct cpuacct_usage cpuacct_usage;
- struct memory memory;
+ struct memory memory;
- struct blkio io_service_bytes; // bytes
- struct blkio io_serviced; // operations
+ struct blkio io_service_bytes; // bytes
+ struct blkio io_serviced; // operations
- struct blkio throttle_io_service_bytes; // bytes
- struct blkio throttle_io_serviced; // operations
+ struct blkio throttle_io_service_bytes; // bytes
+ struct blkio throttle_io_serviced; // operations
- struct blkio io_merged; // operations
- struct blkio io_queued; // operations
+ struct blkio io_merged; // operations
+ struct blkio io_queued; // operations
- struct cgroup *next;
+ struct cgroup *next;
} *cgroup_root = NULL;
@@ -189,393 +194,389 @@ struct cgroup {
// read values from /sys
void cgroup_read_cpuacct_stat(struct cpuacct_stat *cp) {
- static procfile *ff = NULL;
+ static procfile *ff = NULL;
- static uint32_t user_hash = 0;
- static uint32_t system_hash = 0;
+ static uint32_t user_hash = 0;
+ static uint32_t system_hash = 0;
- if(unlikely(user_hash == 0)) {
- user_hash = simple_hash("user");
- system_hash = simple_hash("system");
- }
+ if(unlikely(user_hash == 0)) {
+ user_hash = simple_hash("user");
+ system_hash = simple_hash("system");
+ }
- cp->updated = 0;
- if(cp->filename) {
- ff = procfile_reopen(ff, cp->filename, NULL, PROCFILE_FLAG_DEFAULT);
- if(!ff) return;
+ cp->updated = 0;
+ if(cp->filename) {
+ ff = procfile_reopen(ff, cp->filename, NULL, PROCFILE_FLAG_DEFAULT);
+ if(!ff) return;
- ff = procfile_readall(ff);
- if(!ff) return;
+ ff = procfile_readall(ff);
+ if(!ff) return;
- unsigned long i, lines = procfile_lines(ff);
+ unsigned long i, lines = procfile_lines(ff);
- if(lines < 1) {
- error("File '%s' should have 1+ lines.", cp->filename);
- return;
- }
+ if(lines < 1) {
+ error("File '%s' should have 1+ lines.", cp->filename);
+ return;
+ }
- for(i = 0; i < lines ; i++) {
- char *s = procfile_lineword(ff, i, 0);
- uint32_t hash = simple_hash(s);
+ for(i = 0; i < lines ; i++) {
+ char *s = procfile_lineword(ff, i, 0);
+ uint32_t hash = simple_hash(s);
- if(hash == user_hash && !strcmp(s, "user"))
- cp->user = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+ if(hash == user_hash && !strcmp(s, "user"))
+ cp->user = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
- else if(hash == system_hash && !strcmp(s, "system"))
- cp->system = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
- }
+ else if(hash == system_hash && !strcmp(s, "system"))
+ cp->system = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+ }
- cp->updated = 1;
+ cp->updated = 1;
- // fprintf(stderr, "READ '%s': user: %llu, system: %llu\n", cp->filename, cp->user, cp->system);
- }
+ // fprintf(stderr, "READ '%s': user: %llu, system: %llu\n", cp->filename, cp->user, cp->system);
+ }
}
void cgroup_read_cpuacct_usage(struct cpuacct_usage *ca) {
- static procfile *ff = NULL;
-
- ca->updated = 0;
- if(ca->filename) {
- ff = procfile_reopen(ff, ca->filename, NULL, PROCFILE_FLAG_DEFAULT);
- if(!ff) return;
-
- ff = procfile_readall(ff);
- if(!ff) return;
-
- if(procfile_lines(ff) < 1) {
- error("File '%s' should have 1+ lines but has %d.", ca->filename, procfile_lines(ff));
- return;
- }
-
- unsigned long i = procfile_linewords(ff, 0);
- if(i <= 0) return;
-
- // we may have 1 more CPU reported
- while(i > 0) {
- char *s = procfile_lineword(ff, 0, i - 1);
- if(!*s) i--;
- else break;
- }
-
- if(i != ca->cpus) {
- free(ca->cpu_percpu);
-
- ca->cpu_percpu = malloc(sizeof(unsigned long long) * i);
- if(!ca->cpu_percpu)
- fatal("Cannot allocate memory (%z bytes)", sizeof(unsigned long long) * i);
-
- ca->cpus = i;
- }
-
- for(i = 0; i < ca->cpus ;i++) {
- ca->cpu_percpu[i] = strtoull(procfile_lineword(ff, 0, i), NULL, 10);
- // fprintf(stderr, "READ '%s': cpu%d/%d: %llu ('%s')\n", ca->filename, i, ca->cpus, ca->cpu_percpu[i], procfile_lineword(ff, 0, i));
- }
-
- ca->updated = 1;
- }
+ static procfile *ff = NULL;
+
+ ca->updated = 0;
+ if(ca->filename) {
+ ff = procfile_reopen(ff, ca->filename, NULL, PROCFILE_FLAG_DEFAULT);
+ if(!ff) return;
+
+ ff = procfile_readall(ff);
+ if(!ff) return;
+
+ if(procfile_lines(ff) < 1) {
+ error("File '%s' should have 1+ lines but has %u.", ca->filename, procfile_lines(ff));
+ return;
+ }
+
+ unsigned long i = procfile_linewords(ff, 0);
+ if(i <= 0) return;
+
+ // we may have 1 more CPU reported
+ while(i > 0) {
+ char *s = procfile_lineword(ff, 0, i - 1);
+ if(!*s) i--;
+ else break;
+ }
+
+ if(i != ca->cpus) {
+ freez(ca->cpu_percpu);
+ ca->cpu_percpu = mallocz(sizeof(unsigned long long) * i);
+ ca->cpus = (unsigned int)i;
+ }
+
+ for(i = 0; i < ca->cpus ;i++) {
+ ca->cpu_percpu[i] = strtoull(procfile_lineword(ff, 0, i), NULL, 10);
+ // fprintf(stderr, "READ '%s': cpu%d/%d: %llu ('%s')\n", ca->filename, i, ca->cpus, ca->cpu_percpu[i], procfile_lineword(ff, 0, i));
+ }
+
+ ca->updated = 1;
+ }
}
void cgroup_read_blkio(struct blkio *io) {
- static procfile *ff = NULL;
+ static procfile *ff = NULL;
- static uint32_t Read_hash = 0;
- static uint32_t Write_hash = 0;
+ static uint32_t Read_hash = 0;
+ static uint32_t Write_hash = 0;
/*
- static uint32_t Sync_hash = 0;
- static uint32_t Async_hash = 0;
- static uint32_t Total_hash = 0;
+ static uint32_t Sync_hash = 0;
+ static uint32_t Async_hash = 0;
+ static uint32_t Total_hash = 0;
*/
- if(unlikely(Read_hash == 0)) {
- Read_hash = simple_hash("Read");
- Write_hash = simple_hash("Write");
+ if(unlikely(Read_hash == 0)) {
+ Read_hash = simple_hash("Read");
+ Write_hash = simple_hash("Write");
/*
- Sync_hash = simple_hash("Sync");
- Async_hash = simple_hash("Async");
- Total_hash = simple_hash("Total");
+ Sync_hash = simple_hash("Sync");
+ Async_hash = simple_hash("Async");
+ Total_hash = simple_hash("Total");
*/
- }
+ }
- io->updated = 0;
- if(io->filename) {
- ff = procfile_reopen(ff, io->filename, NULL, PROCFILE_FLAG_DEFAULT);
- if(!ff) return;
+ io->updated = 0;
+ if(io->filename) {
+ ff = procfile_reopen(ff, io->filename, NULL, PROCFILE_FLAG_DEFAULT);
+ if(!ff) return;
- ff = procfile_readall(ff);
- if(!ff) return;
+ ff = procfile_readall(ff);
+ if(!ff) return;
- unsigned long i, lines = procfile_lines(ff);
+ unsigned long i, lines = procfile_lines(ff);
- if(lines < 1) {
- error("File '%s' should have 1+ lines.", io->filename);
- return;
- }
+ if(lines < 1) {
+ error("File '%s' should have 1+ lines.", io->filename);
+ return;
+ }
- io->Read = 0;
- io->Write = 0;
+ io->Read = 0;
+ io->Write = 0;
/*
- io->Sync = 0;
- io->Async = 0;
- io->Total = 0;
+ io->Sync = 0;
+ io->Async = 0;
+ io->Total = 0;
*/
- for(i = 0; i < lines ; i++) {
- char *s = procfile_lineword(ff, i, 1);
- uint32_t hash = simple_hash(s);
+ for(i = 0; i < lines ; i++) {
+ char *s = procfile_lineword(ff, i, 1);
+ uint32_t hash = simple_hash(s);
- if(hash == Read_hash && !strcmp(s, "Read"))
- io->Read += strtoull(procfile_lineword(ff, i, 2), NULL, 10);
+ if(hash == Read_hash && !strcmp(s, "Read"))
+ io->Read += strtoull(procfile_lineword(ff, i, 2), NULL, 10);
- else if(hash == Write_hash && !strcmp(s, "Write"))
- io->Write += strtoull(procfile_lineword(ff, i, 2), NULL, 10);
+ else if(hash == Write_hash && !strcmp(s, "Write"))
+ io->Write += strtoull(procfile_lineword(ff, i, 2), NULL, 10);
/*
- else if(hash == Sync_hash && !strcmp(s, "Sync"))
- io->Sync += strtoull(procfile_lineword(ff, i, 2), NULL, 10);
+ else if(hash == Sync_hash && !strcmp(s, "Sync"))
+ io->Sync += strtoull(procfile_lineword(ff, i, 2), NULL, 10);
- else if(hash == Async_hash && !strcmp(s, "Async"))
- io->Async += strtoull(procfile_lineword(ff, i, 2), NULL, 10);
+ else if(hash == Async_hash && !strcmp(s, "Async"))
+ io->Async += strtoull(procfile_lineword(ff, i, 2), NULL, 10);
- else if(hash == Total_hash && !strcmp(s, "Total"))
- io->Total += strtoull(procfile_lineword(ff, i, 2), NULL, 10);
+ else if(hash == Total_hash && !strcmp(s, "Total"))
+ io->Total += strtoull(procfile_lineword(ff, i, 2), NULL, 10);
*/
- }
+ }
- io->updated = 1;
- // fprintf(stderr, "READ '%s': Read: %llu, Write: %llu, Sync: %llu, Async: %llu, Total: %llu\n", io->filename, io->Read, io->Write, io->Sync, io->Async, io->Total);
- }
+ io->updated = 1;
+ // fprintf(stderr, "READ '%s': Read: %llu, Write: %llu, Sync: %llu, Async: %llu, Total: %llu\n", io->filename, io->Read, io->Write, io->Sync, io->Async, io->Total);
+ }
}
void cgroup_read_memory(struct memory *mem) {
- static procfile *ff = NULL;
-
- static uint32_t cache_hash = 0;
- static uint32_t rss_hash = 0;
- static uint32_t rss_huge_hash = 0;
- static uint32_t mapped_file_hash = 0;
- static uint32_t writeback_hash = 0;
- static uint32_t dirty_hash = 0;
- static uint32_t swap_hash = 0;
- static uint32_t pgpgin_hash = 0;
- static uint32_t pgpgout_hash = 0;
- static uint32_t pgfault_hash = 0;
- static uint32_t pgmajfault_hash = 0;
+ static procfile *ff = NULL;
+
+ static uint32_t cache_hash = 0;
+ static uint32_t rss_hash = 0;
+ static uint32_t rss_huge_hash = 0;
+ static uint32_t mapped_file_hash = 0;
+ static uint32_t writeback_hash = 0;
+ static uint32_t dirty_hash = 0;
+ static uint32_t swap_hash = 0;
+ static uint32_t pgpgin_hash = 0;
+ static uint32_t pgpgout_hash = 0;
+ static uint32_t pgfault_hash = 0;
+ static uint32_t pgmajfault_hash = 0;
/*
- static uint32_t inactive_anon_hash = 0;
- static uint32_t active_anon_hash = 0;
- static uint32_t inactive_file_hash = 0;
- static uint32_t active_file_hash = 0;
- static uint32_t unevictable_hash = 0;
- static uint32_t hierarchical_memory_limit_hash = 0;
- static uint32_t total_cache_hash = 0;
- static uint32_t total_rss_hash = 0;
- static uint32_t total_rss_huge_hash = 0;
- static uint32_t total_mapped_file_hash = 0;
- static uint32_t total_writeback_hash = 0;
- static uint32_t total_dirty_hash = 0;
- static uint32_t total_swap_hash = 0;
- static uint32_t total_pgpgin_hash = 0;
- static uint32_t total_pgpgout_hash = 0;
- static uint32_t total_pgfault_hash = 0;
- static uint32_t total_pgmajfault_hash = 0;
- static uint32_t total_inactive_anon_hash = 0;
- static uint32_t total_active_anon_hash = 0;
- static uint32_t total_inactive_file_hash = 0;
- static uint32_t total_active_file_hash = 0;
- static uint32_t total_unevictable_hash = 0;
+ static uint32_t inactive_anon_hash = 0;
+ static uint32_t active_anon_hash = 0;
+ static uint32_t inactive_file_hash = 0;
+ static uint32_t active_file_hash = 0;
+ static uint32_t unevictable_hash = 0;
+ static uint32_t hierarchical_memory_limit_hash = 0;
+ static uint32_t total_cache_hash = 0;
+ static uint32_t total_rss_hash = 0;
+ static uint32_t total_rss_huge_hash = 0;
+ static uint32_t total_mapped_file_hash = 0;
+ static uint32_t total_writeback_hash = 0;
+ static uint32_t total_dirty_hash = 0;
+ static uint32_t total_swap_hash = 0;
+ static uint32_t total_pgpgin_hash = 0;
+ static uint32_t total_pgpgout_hash = 0;
+ static uint32_t total_pgfault_hash = 0;
+ static uint32_t total_pgmajfault_hash = 0;
+ static uint32_t total_inactive_anon_hash = 0;
+ static uint32_t total_active_anon_hash = 0;
+ static uint32_t total_inactive_file_hash = 0;
+ static uint32_t total_active_file_hash = 0;
+ static uint32_t total_unevictable_hash = 0;
*/
- if(unlikely(cache_hash == 0)) {
- cache_hash = simple_hash("cache");
- rss_hash = simple_hash("rss");
- rss_huge_hash = simple_hash("rss_huge");
- mapped_file_hash = simple_hash("mapped_file");
- writeback_hash = simple_hash("writeback");
- dirty_hash = simple_hash("dirty");
- swap_hash = simple_hash("swap");
- pgpgin_hash = simple_hash("pgpgin");
- pgpgout_hash = simple_hash("pgpgout");
- pgfault_hash = simple_hash("pgfault");
- pgmajfault_hash = simple_hash("pgmajfault");
+ if(unlikely(cache_hash == 0)) {
+ cache_hash = simple_hash("cache");
+ rss_hash = simple_hash("rss");
+ rss_huge_hash = simple_hash("rss_huge");
+ mapped_file_hash = simple_hash("mapped_file");
+ writeback_hash = simple_hash("writeback");
+ dirty_hash = simple_hash("dirty");
+ swap_hash = simple_hash("swap");
+ pgpgin_hash = simple_hash("pgpgin");
+ pgpgout_hash = simple_hash("pgpgout");
+ pgfault_hash = simple_hash("pgfault");
+ pgmajfault_hash = simple_hash("pgmajfault");
/*
- inactive_anon_hash = simple_hash("inactive_anon");
- active_anon_hash = simple_hash("active_anon");
- inactive_file_hash = simple_hash("inactive_file");
- active_file_hash = simple_hash("active_file");
- unevictable_hash = simple_hash("unevictable");
- hierarchical_memory_limit_hash = simple_hash("hierarchical_memory_limit");
- total_cache_hash = simple_hash("total_cache");
- total_rss_hash = simple_hash("total_rss");
- total_rss_huge_hash = simple_hash("total_rss_huge");
- total_mapped_file_hash = simple_hash("total_mapped_file");
- total_writeback_hash = simple_hash("total_writeback");
- total_dirty_hash = simple_hash("total_dirty");
- total_swap_hash = simple_hash("total_swap");
- total_pgpgin_hash = simple_hash("total_pgpgin");
- total_pgpgout_hash = simple_hash("total_pgpgout");
- total_pgfault_hash = simple_hash("total_pgfault");
- total_pgmajfault_hash = simple_hash("total_pgmajfault");
- total_inactive_anon_hash = simple_hash("total_inactive_anon");
- total_active_anon_hash = simple_hash("total_active_anon");
- total_inactive_file_hash = simple_hash("total_inactive_file");
- total_active_file_hash = simple_hash("total_active_file");
- total_unevictable_hash = simple_hash("total_unevictable");
+ inactive_anon_hash = simple_hash("inactive_anon");
+ active_anon_hash = simple_hash("active_anon");
+ inactive_file_hash = simple_hash("inactive_file");
+ active_file_hash = simple_hash("active_file");
+ unevictable_hash = simple_hash("unevictable");
+ hierarchical_memory_limit_hash = simple_hash("hierarchical_memory_limit");
+ total_cache_hash = simple_hash("total_cache");
+ total_rss_hash = simple_hash("total_rss");
+ total_rss_huge_hash = simple_hash("total_rss_huge");
+ total_mapped_file_hash = simple_hash("total_mapped_file");
+ total_writeback_hash = simple_hash("total_writeback");
+ total_dirty_hash = simple_hash("total_dirty");
+ total_swap_hash = simple_hash("total_swap");
+ total_pgpgin_hash = simple_hash("total_pgpgin");
+ total_pgpgout_hash = simple_hash("total_pgpgout");
+ total_pgfault_hash = simple_hash("total_pgfault");
+ total_pgmajfault_hash = simple_hash("total_pgmajfault");
+ total_inactive_anon_hash = simple_hash("total_inactive_anon");
+ total_active_anon_hash = simple_hash("total_active_anon");
+ total_inactive_file_hash = simple_hash("total_inactive_file");
+ total_active_file_hash = simple_hash("total_active_file");
+ total_unevictable_hash = simple_hash("total_unevictable");
*/
- }
+ }
- mem->updated = 0;
- if(mem->filename) {
- ff = procfile_reopen(ff, mem->filename, NULL, PROCFILE_FLAG_DEFAULT);
- if(!ff) return;
+ mem->updated = 0;
+ if(mem->filename) {
+ ff = procfile_reopen(ff, mem->filename, NULL, PROCFILE_FLAG_DEFAULT);
+ if(!ff) return;
- ff = procfile_readall(ff);
- if(!ff) return;
+ ff = procfile_readall(ff);
+ if(!ff) return;
- unsigned long i, lines = procfile_lines(ff);
+ unsigned long i, lines = procfile_lines(ff);
- if(lines < 1) {
- error("File '%s' should have 1+ lines.", mem->filename);
- return;
- }
+ if(lines < 1) {
+ error("File '%s' should have 1+ lines.", mem->filename);
+ return;
+ }
- for(i = 0; i < lines ; i++) {
- char *s = procfile_lineword(ff, i, 0);
- uint32_t hash = simple_hash(s);
+ for(i = 0; i < lines ; i++) {
+ char *s = procfile_lineword(ff, i, 0);
+ uint32_t hash = simple_hash(s);
- if(hash == cache_hash && !strcmp(s, "cache"))
- mem->cache = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+ if(hash == cache_hash && !strcmp(s, "cache"))
+ mem->cache = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
- else if(hash == rss_hash && !strcmp(s, "rss"))
- mem->rss = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+ else if(hash == rss_hash && !strcmp(s, "rss"))
+ mem->rss = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
- else if(hash == rss_huge_hash && !strcmp(s, "rss_huge"))
- mem->rss_huge = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+ else if(hash == rss_huge_hash && !strcmp(s, "rss_huge"))
+ mem->rss_huge = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
- else if(hash == mapped_file_hash && !strcmp(s, "mapped_file"))
- mem->mapped_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+ else if(hash == mapped_file_hash && !strcmp(s, "mapped_file"))
+ mem->mapped_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
- else if(hash == writeback_hash && !strcmp(s, "writeback"))
- mem->writeback = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+ else if(hash == writeback_hash && !strcmp(s, "writeback"))
+ mem->writeback = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
- else if(hash == dirty_hash && !strcmp(s, "dirty")) {
- mem->dirty = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
- mem->has_dirty_swap = 1;
- }
+ else if(hash == dirty_hash && !strcmp(s, "dirty")) {
+ mem->dirty = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+ mem->has_dirty_swap = 1;
+ }
- else if(hash == swap_hash && !strcmp(s, "swap")) {
- mem->swap = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
- mem->has_dirty_swap = 1;
- }
+ else if(hash == swap_hash && !strcmp(s, "swap")) {
+ mem->swap = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+ mem->has_dirty_swap = 1;
+ }
- else if(hash == pgpgin_hash && !strcmp(s, "pgpgin"))
- mem->pgpgin = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+ else if(hash == pgpgin_hash && !strcmp(s, "pgpgin"))
+ mem->pgpgin = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
- else if(hash == pgpgout_hash && !strcmp(s, "pgpgout"))
- mem->pgpgout = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+ else if(hash == pgpgout_hash && !strcmp(s, "pgpgout"))
+ mem->pgpgout = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
- else if(hash == pgfault_hash && !strcmp(s, "pgfault"))
- mem->pgfault = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+ else if(hash == pgfault_hash && !strcmp(s, "pgfault"))
+ mem->pgfault = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
- else if(hash == pgmajfault_hash && !strcmp(s, "pgmajfault"))
- mem->pgmajfault = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+ else if(hash == pgmajfault_hash && !strcmp(s, "pgmajfault"))
+ mem->pgmajfault = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
/*
- else if(hash == inactive_anon_hash && !strcmp(s, "inactive_anon"))
- mem->inactive_anon = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+ else if(hash == inactive_anon_hash && !strcmp(s, "inactive_anon"))
+ mem->inactive_anon = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
- else if(hash == active_anon_hash && !strcmp(s, "active_anon"))
- mem->active_anon = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+ else if(hash == active_anon_hash && !strcmp(s, "active_anon"))
+ mem->active_anon = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
- else if(hash == inactive_file_hash && !strcmp(s, "inactive_file"))
- mem->inactive_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+ else if(hash == inactive_file_hash && !strcmp(s, "inactive_file"))
+ mem->inactive_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
- else if(hash == active_file_hash && !strcmp(s, "active_file"))
- mem->active_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+ else if(hash == active_file_hash && !strcmp(s, "active_file"))
+ mem->active_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
- else if(hash == unevictable_hash && !strcmp(s, "unevictable"))
- mem->unevictable = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+ else if(hash == unevictable_hash && !strcmp(s, "unevictable"))
+ mem->unevictable = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
- else if(hash == hierarchical_memory_limit_hash && !strcmp(s, "hierarchical_memory_limit"))
- mem->hierarchical_memory_limit = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+ else if(hash == hierarchical_memory_limit_hash && !strcmp(s, "hierarchical_memory_limit"))
+ mem->hierarchical_memory_limit = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
- else if(hash == total_cache_hash && !strcmp(s, "total_cache"))
- mem->total_cache = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+ else if(hash == total_cache_hash && !strcmp(s, "total_cache"))
+ mem->total_cache = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
- else if(hash == total_rss_hash && !strcmp(s, "total_rss"))
- mem->total_rss = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+ else if(hash == total_rss_hash && !strcmp(s, "total_rss"))
+ mem->total_rss = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
- else if(hash == total_rss_huge_hash && !strcmp(s, "total_rss_huge"))
- mem->total_rss_huge = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+ else if(hash == total_rss_huge_hash && !strcmp(s, "total_rss_huge"))
+ mem->total_rss_huge = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
- else if(hash == total_mapped_file_hash && !strcmp(s, "total_mapped_file"))
- mem->total_mapped_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+ else if(hash == total_mapped_file_hash && !strcmp(s, "total_mapped_file"))
+ mem->total_mapped_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
- else if(hash == total_writeback_hash && !strcmp(s, "total_writeback"))
- mem->total_writeback = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+ else if(hash == total_writeback_hash && !strcmp(s, "total_writeback"))
+ mem->total_writeback = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
- else if(hash == total_dirty_hash && !strcmp(s, "total_dirty"))
- mem->total_dirty = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+ else if(hash == total_dirty_hash && !strcmp(s, "total_dirty"))
+ mem->total_dirty = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
- else if(hash == total_swap_hash && !strcmp(s, "total_swap"))
- mem->total_swap = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+ else if(hash == total_swap_hash && !strcmp(s, "total_swap"))
+ mem->total_swap = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
- else if(hash == total_pgpgin_hash && !strcmp(s, "total_pgpgin"))
- mem->total_pgpgin = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+ else if(hash == total_pgpgin_hash && !strcmp(s, "total_pgpgin"))
+ mem->total_pgpgin = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
- else if(hash == total_pgpgout_hash && !strcmp(s, "total_pgpgout"))
- mem->total_pgpgout = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+ else if(hash == total_pgpgout_hash && !strcmp(s, "total_pgpgout"))
+ mem->total_pgpgout = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
- else if(hash == total_pgfault_hash && !strcmp(s, "total_pgfault"))
- mem->total_pgfault = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+ else if(hash == total_pgfault_hash && !strcmp(s, "total_pgfault"))
+ mem->total_pgfault = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
- else if(hash == total_pgmajfault_hash && !strcmp(s, "total_pgmajfault"))
- mem->total_pgmajfault = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+ else if(hash == total_pgmajfault_hash && !strcmp(s, "total_pgmajfault"))
+ mem->total_pgmajfault = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
- else if(hash == total_inactive_anon_hash && !strcmp(s, "total_inactive_anon"))
- mem->total_inactive_anon = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+ else if(hash == total_inactive_anon_hash && !strcmp(s, "total_inactive_anon"))
+ mem->total_inactive_anon = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
- else if(hash == total_active_anon_hash && !strcmp(s, "total_active_anon"))
- mem->total_active_anon = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+ else if(hash == total_active_anon_hash && !strcmp(s, "total_active_anon"))
+ mem->total_active_anon = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
- else if(hash == total_inactive_file_hash && !strcmp(s, "total_inactive_file"))
- mem->total_inactive_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+ else if(hash == total_inactive_file_hash && !strcmp(s, "total_inactive_file"))
+ mem->total_inactive_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
- else if(hash == total_active_file_hash && !strcmp(s, "total_active_file"))
- mem->total_active_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+ else if(hash == total_active_file_hash && !strcmp(s, "total_active_file"))
+ mem->total_active_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
- else if(hash == total_unevictable_hash && !strcmp(s, "total_unevictable"))
- mem->total_unevictable = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+ else if(hash == total_unevictable_hash && !strcmp(s, "total_unevictable"))
+ mem->total_unevictable = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
*/
- }
+ }
- // fprintf(stderr, "READ: '%s', cache: %llu, rss: %llu, rss_huge: %llu, mapped_file: %llu, writeback: %llu, dirty: %llu, swap: %llu, pgpgin: %llu, pgpgout: %llu, pgfault: %llu, pgmajfault: %llu, inactive_anon: %llu, active_anon: %llu, inactive_file: %llu, active_file: %llu, unevictable: %llu, hierarchical_memory_limit: %llu, total_cache: %llu, total_rss: %llu, total_rss_huge: %llu, total_mapped_file: %llu, total_writeback: %llu, total_dirty: %llu, total_swap: %llu, total_pgpgin: %llu, total_pgpgout: %llu, total_pgfault: %llu, total_pgmajfault: %llu, total_inactive_anon: %llu, total_active_anon: %llu, total_inactive_file: %llu, total_active_file: %llu, total_unevictable: %llu\n", mem->filename, mem->cache, mem->rss, mem->rss_huge, mem->mapped_file, mem->writeback, mem->dirty, mem->swap, mem->pgpgin, mem->pgpgout, mem->pgfault, mem->pgmajfault, mem->inactive_anon, mem->active_anon, mem->inactive_file, mem->active_file, mem->unevictable, mem->hierarchical_memory_limit, mem->total_cache, mem->total_rss, mem->total_rss_huge, mem->total_mapped_file, mem->total_writeback, mem->total_dirty, mem->total_swap, mem->total_pgpgin, mem->total_pgpgout, mem->total_pgfault, mem->total_pgmajfault, mem->total_inactive_anon, mem->total_active_anon, mem->total_inactive_file, mem->total_active_file, mem->total_unevictable);
+ // fprintf(stderr, "READ: '%s', cache: %llu, rss: %llu, rss_huge: %llu, mapped_file: %llu, writeback: %llu, dirty: %llu, swap: %llu, pgpgin: %llu, pgpgout: %llu, pgfault: %llu, pgmajfault: %llu, inactive_anon: %llu, active_anon: %llu, inactive_file: %llu, active_file: %llu, unevictable: %llu, hierarchical_memory_limit: %llu, total_cache: %llu, total_rss: %llu, total_rss_huge: %llu, total_mapped_file: %llu, total_writeback: %llu, total_dirty: %llu, total_swap: %llu, total_pgpgin: %llu, total_pgpgout: %llu, total_pgfault: %llu, total_pgmajfault: %llu, total_inactive_anon: %llu, total_active_anon: %llu, total_inactive_file: %llu, total_active_file: %llu, total_unevictable: %llu\n", mem->filename, mem->cache, mem->rss, mem->rss_huge, mem->mapped_file, mem->writeback, mem->dirty, mem->swap, mem->pgpgin, mem->pgpgout, mem->pgfault, mem->pgmajfault, mem->inactive_anon, mem->active_anon, mem->inactive_file, mem->active_file, mem->unevictable, mem->hierarchical_memory_limit, mem->total_cache, mem->total_rss, mem->total_rss_huge, mem->total_mapped_file, mem->total_writeback, mem->total_dirty, mem->total_swap, mem->total_pgpgin, mem->total_pgpgout, mem->total_pgfault, mem->total_pgmajfault, mem->total_inactive_anon, mem->total_active_anon, mem->total_inactive_file, mem->total_active_file, mem->total_unevictable);
- mem->updated = 1;
- }
+ mem->updated = 1;
+ }
}
void cgroup_read(struct cgroup *cg) {
- debug(D_CGROUP, "reading metrics for cgroups '%s'", cg->id);
-
- cgroup_read_cpuacct_stat(&cg->cpuacct_stat);
- cgroup_read_cpuacct_usage(&cg->cpuacct_usage);
- cgroup_read_memory(&cg->memory);
- cgroup_read_blkio(&cg->io_service_bytes);
- cgroup_read_blkio(&cg->io_serviced);
- cgroup_read_blkio(&cg->throttle_io_service_bytes);
- cgroup_read_blkio(&cg->throttle_io_serviced);
- cgroup_read_blkio(&cg->io_merged);
- cgroup_read_blkio(&cg->io_queued);
+ debug(D_CGROUP, "reading metrics for cgroups '%s'", cg->id);
+
+ cgroup_read_cpuacct_stat(&cg->cpuacct_stat);
+ cgroup_read_cpuacct_usage(&cg->cpuacct_usage);
+ cgroup_read_memory(&cg->memory);
+ cgroup_read_blkio(&cg->io_service_bytes);
+ cgroup_read_blkio(&cg->io_serviced);
+ cgroup_read_blkio(&cg->throttle_io_service_bytes);
+ cgroup_read_blkio(&cg->throttle_io_serviced);
+ cgroup_read_blkio(&cg->io_merged);
+ cgroup_read_blkio(&cg->io_queued);
}
void read_all_cgroups(struct cgroup *root) {
- debug(D_CGROUP, "reading metrics for all cgroups");
+ debug(D_CGROUP, "reading metrics for all cgroups");
- struct cgroup *cg;
+ struct cgroup *cg;
- for(cg = root; cg ; cg = cg->next)
- if(cg->enabled && cg->available)
- cgroup_read(cg);
+ for(cg = root; cg ; cg = cg->next)
+ if(cg->enabled && cg->available)
+ cgroup_read(cg);
}
// ----------------------------------------------------------------------------
@@ -584,177 +585,189 @@ void read_all_cgroups(struct cgroup *root) {
#define CGROUP_CHARTID_LINE_MAX 1024
void cgroup_get_chart_id(struct cgroup *cg) {
- debug(D_CGROUP, "getting the name of cgroup '%s'", cg->id);
-
- pid_t cgroup_pid;
- char buffer[CGROUP_CHARTID_LINE_MAX + 1];
-
- snprintfz(buffer, CGROUP_CHARTID_LINE_MAX, "exec %s '%s'",
- config_get("plugin:cgroups", "script to get cgroup names", PLUGINS_DIR "/cgroup-name.sh"), cg->chart_id);
-
- debug(D_CGROUP, "executing command '%s' for cgroup '%s'", buffer, cg->id);
- FILE *fp = mypopen(buffer, &cgroup_pid);
- if(!fp) {
- error("CGROUP: Cannot popen(\"%s\", \"r\").", buffer);
- return;
- }
- debug(D_CGROUP, "reading from command '%s' for cgroup '%s'", buffer, cg->id);
- char *s = fgets(buffer, CGROUP_CHARTID_LINE_MAX, fp);
- debug(D_CGROUP, "closing command for cgroup '%s'", cg->id);
- mypclose(fp, cgroup_pid);
- debug(D_CGROUP, "closed command for cgroup '%s'", cg->id);
-
- if(s && *s && *s != '\n') {
- debug(D_CGROUP, "cgroup '%s' should be renamed to '%s'", cg->id, s);
-
- trim(s);
-
- free(cg->chart_title);
- cg->chart_title = strdup(s);
- if(!cg->chart_title)
- fatal("CGROUP: Cannot allocate memory for chart name of cgroup '%s' chart name: '%s'", cg->id, s);
-
- netdata_fix_chart_name(cg->chart_title);
-
- free(cg->chart_id);
- cg->chart_id = strdup(s);
- if(!cg->chart_id)
- fatal("CGROUP: Cannot allocate memory for chart id of cgroup '%s' chart id: '%s'", cg->id, s);
-
- netdata_fix_chart_id(cg->chart_id);
-
- debug(D_CGROUP, "cgroup '%s' renamed to '%s' (title: '%s')", cg->id, cg->chart_id, cg->chart_title);
- }
- else debug(D_CGROUP, "cgroup '%s' is not to be renamed (will be shown as '%s')", cg->id, cg->chart_id);
+ debug(D_CGROUP, "getting the name of cgroup '%s'", cg->id);
+
+ pid_t cgroup_pid;
+ char buffer[CGROUP_CHARTID_LINE_MAX + 1];
+
+ snprintfz(buffer, CGROUP_CHARTID_LINE_MAX, "exec %s '%s'",
+ config_get("plugin:cgroups", "script to get cgroup names", PLUGINS_DIR "/cgroup-name.sh"), cg->chart_id);
+
+ debug(D_CGROUP, "executing command '%s' for cgroup '%s'", buffer, cg->id);
+ FILE *fp = mypopen(buffer, &cgroup_pid);
+ if(!fp) {
+ error("CGROUP: Cannot popen(\"%s\", \"r\").", buffer);
+ return;
+ }
+ debug(D_CGROUP, "reading from command '%s' for cgroup '%s'", buffer, cg->id);
+ char *s = fgets(buffer, CGROUP_CHARTID_LINE_MAX, fp);
+ debug(D_CGROUP, "closing command for cgroup '%s'", cg->id);
+ mypclose(fp, cgroup_pid);
+ debug(D_CGROUP, "closed command for cgroup '%s'", cg->id);
+
+ if(s && *s && *s != '\n') {
+ debug(D_CGROUP, "cgroup '%s' should be renamed to '%s'", cg->id, s);
+
+ trim(s);
+
+ freez(cg->chart_title);
+ cg->chart_title = strdupz(s);
+ netdata_fix_chart_name(cg->chart_title);
+
+ freez(cg->chart_id);
+ cg->chart_id = strdupz(s);
+ netdata_fix_chart_id(cg->chart_id);
+ cg->hash_chart = simple_hash(cg->chart_id);
+
+ debug(D_CGROUP, "cgroup '%s' renamed to '%s' (title: '%s')", cg->id, cg->chart_id, cg->chart_title);
+ }
+ else debug(D_CGROUP, "cgroup '%s' is not to be renamed (will be shown as '%s')", cg->id, cg->chart_id);
}
struct cgroup *cgroup_add(const char *id) {
- debug(D_CGROUP, "adding cgroup '%s'", id);
-
- if(cgroup_root_count >= cgroup_root_max) {
- info("Maximum number of cgroups reached (%d). Not adding cgroup '%s'", cgroup_root_count, id);
- return NULL;
- }
-
- int def = cgroup_enable_new_cgroups_detected_at_runtime;
- const char *chart_id = id;
- if(!*chart_id) {
- chart_id = "/";
-
- // disable by default the root cgroup
- def = 0;
- debug(D_CGROUP, "cgroup '%s' is the root container (by default %s)", id, (def)?"enabled":"disabled");
- }
- else {
- if(*chart_id == '/') chart_id++;
-
- size_t len = strlen(chart_id);
-
- // disable by default the parent cgroup
- // for known cgroup managers
- if(!strcmp(chart_id, "lxc") ||
- !strcmp(chart_id, "docker") ||
- !strcmp(chart_id, "libvirt") ||
- !strcmp(chart_id, "qemu") ||
- !strcmp(chart_id, "systemd") ||
- !strcmp(chart_id, "system.slice") ||
- !strcmp(chart_id, "machine.slice") ||
- !strcmp(chart_id, "user") ||
- !strcmp(chart_id, "system") ||
- !strcmp(chart_id, "machine") ||
- // starts with them
- (len > 6 && !strncmp(chart_id, "user/", 6)) ||
- (len > 11 && !strncmp(chart_id, "user.slice/", 11)) ||
- // ends with them
- (len > 5 && !strncmp(&chart_id[len - 5], ".user", 5)) ||
- (len > 5 && !strncmp(&chart_id[len - 5], ".swap", 5)) ||
- (len > 6 && !strncmp(&chart_id[len - 6], ".slice", 6)) ||
- (len > 6 && !strncmp(&chart_id[len - 6], ".mount", 6)) ||
- (len > 8 && !strncmp(&chart_id[len - 8], ".session", 8)) ||
- (len > 8 && !strncmp(&chart_id[len - 8], ".service", 8)) ||
- (len > 10 && !strncmp(&chart_id[len - 10], ".partition", 10))
- ) {
- def = 0;
- debug(D_CGROUP, "cgroup '%s' is %s (by default)", id, (def)?"enabled":"disabled");
- }
- }
-
- struct cgroup *cg = calloc(1, sizeof(struct cgroup));
- if(!cg) fatal("Cannot allocate memory for cgroup '%s'", id);
-
- debug(D_CGROUP, "adding cgroup '%s'", id);
-
- cg->id = strdup(id);
- if(!cg->id) fatal("Cannot allocate memory for cgroup '%s'", id);
-
- cg->hash = simple_hash(cg->id);
-
- cg->chart_id = strdup(chart_id);
- if(!cg->chart_id) fatal("Cannot allocate memory for cgroup '%s'", id);
-
- cg->chart_title = strdup(chart_id);
- if(!cg->chart_title) fatal("Cannot allocate memory for cgroup '%s'", id);
-
- if(!cgroup_root)
- cgroup_root = cg;
- else {
- // append it
- struct cgroup *e;
- for(e = cgroup_root; e->next ;e = e->next) ;
- e->next = cg;
- }
-
- cgroup_root_count++;
-
- // fix the name by calling the external script
- cgroup_get_chart_id(cg);
-
- char option[FILENAME_MAX + 1];
- snprintfz(option, FILENAME_MAX, "enable cgroup %s", cg->chart_title);
- cg->enabled = config_get_boolean("plugin:cgroups", option, def);
-
- debug(D_CGROUP, "Added cgroup '%s' with chart id '%s' and title '%s' as %s (default was %s)", cg->id, cg->chart_id, cg->chart_title, (cg->enabled)?"enabled":"disabled", (def)?"enabled":"disabled");
-
- return cg;
+ debug(D_CGROUP, "adding cgroup '%s'", id);
+
+ if(cgroup_root_count >= cgroup_root_max) {
+ info("Maximum number of cgroups reached (%d). Not adding cgroup '%s'", cgroup_root_count, id);
+ return NULL;
+ }
+
+ int def = cgroup_enable_new_cgroups_detected_at_runtime;
+ const char *chart_id = id;
+ if(!*chart_id) {
+ chart_id = "/";
+
+ // disable by default the root cgroup
+ def = 0;
+ debug(D_CGROUP, "cgroup '%s' is the root container (by default %s)", id, (def)?"enabled":"disabled");
+ }
+ else {
+ if(*chart_id == '/') chart_id++;
+
+ size_t len = strlen(chart_id);
+
+ // disable by default the parent cgroup
+ // for known cgroup managers
+ if(!strcmp(chart_id, "lxc") ||
+ !strcmp(chart_id, "docker") ||
+ !strcmp(chart_id, "libvirt") ||
+ !strcmp(chart_id, "qemu") ||
+ !strcmp(chart_id, "systemd") ||
+ !strcmp(chart_id, "system.slice") ||
+ !strcmp(chart_id, "machine.slice") ||
+ !strcmp(chart_id, "init.scope") ||
+ !strcmp(chart_id, "user") ||
+ !strcmp(chart_id, "system") ||
+ !strcmp(chart_id, "machine") ||
+ // starts with them
+ (len > 6 && !strncmp(chart_id, "user/", 6)) ||
+ (len > 11 && !strncmp(chart_id, "user.slice/", 11)) ||
+ // ends with them
+ (len > 5 && !strncmp(&chart_id[len - 5], ".user", 5)) ||
+ (len > 5 && !strncmp(&chart_id[len - 5], ".swap", 5)) ||
+ (len > 6 && !strncmp(&chart_id[len - 6], ".slice", 6)) ||
+ (len > 6 && !strncmp(&chart_id[len - 6], ".mount", 6)) ||
+ (len > 8 && !strncmp(&chart_id[len - 8], ".session", 8)) ||
+ (len > 8 && !strncmp(&chart_id[len - 8], ".service", 8)) ||
+ (len > 10 && !strncmp(&chart_id[len - 10], ".partition", 10))
+ ) {
+ def = 0;
+ debug(D_CGROUP, "cgroup '%s' is %s (by default)", id, (def)?"enabled":"disabled");
+ }
+ }
+
+ struct cgroup *cg = callocz(1, sizeof(struct cgroup));
+
+ cg->id = strdupz(id);
+ cg->hash = simple_hash(cg->id);
+
+ cg->chart_id = strdupz(chart_id);
+ netdata_fix_chart_id(cg->chart_id);
+ cg->hash_chart = simple_hash(cg->chart_id);
+
+ cg->chart_title = strdupz(chart_id);
+
+ if(!cgroup_root)
+ cgroup_root = cg;
+ else {
+ // append it
+ struct cgroup *e;
+ for(e = cgroup_root; e->next ;e = e->next) ;
+ e->next = cg;
+ }
+
+ cgroup_root_count++;
+
+ // fix the name by calling the external script
+ cgroup_get_chart_id(cg);
+
+ debug(D_CGROUP, "adding cgroup '%s' with chart id '%s'", id, chart_id);
+
+ char option[FILENAME_MAX + 1];
+ snprintfz(option, FILENAME_MAX, "enable cgroup %s", cg->chart_title);
+ cg->enabled = config_get_boolean("plugin:cgroups", option, def);
+
+ if(cg->enabled) {
+ struct cgroup *t;
+ for (t = cgroup_root; t; t = t->next) {
+ if (t != cg && t->enabled && t->hash_chart == cg->hash_chart && !strcmp(t->chart_id, cg->chart_id)) {
+ if (!strncmp(t->chart_id, "/system.slice/", 14) && !strncmp(cg->chart_id, "/init.scope/system.slice/", 25)) {
+ error("Control group with chart id '%s' already exists with id '%s' and is enabled. Swapping them by enabling cgroup with id '%s' and disabling cgroup with id '%s'.",
+ cg->chart_id, t->id, cg->id, t->id);
+ t->enabled = 0;
+ } else {
+ error("Control group with chart id '%s' already exists with id '%s' and is enabled. Disabling cgroup with id '%s'.",
+ cg->chart_id, t->id, cg->id);
+ cg->enabled = 0;
+ }
+
+ break;
+ }
+ }
+ }
+
+ debug(D_CGROUP, "Added cgroup '%s' with chart id '%s' and title '%s' as %s (default was %s)", cg->id, cg->chart_id, cg->chart_title, (cg->enabled)?"enabled":"disabled", (def)?"enabled":"disabled");
+
+ return cg;
}
void cgroup_free(struct cgroup *cg) {
- debug(D_CGROUP, "Removing cgroup '%s' with chart id '%s' (was %s and %s)", cg->id, cg->chart_id, (cg->enabled)?"enabled":"disabled", (cg->available)?"available":"not available");
-
- free(cg->cpuacct_usage.cpu_percpu);
-
- free(cg->cpuacct_stat.filename);
- free(cg->cpuacct_usage.filename);
- free(cg->memory.filename);
- free(cg->io_service_bytes.filename);
- free(cg->io_serviced.filename);
- free(cg->throttle_io_service_bytes.filename);
- free(cg->throttle_io_serviced.filename);
- free(cg->io_merged.filename);
- free(cg->io_queued.filename);
-
- free(cg->id);
- free(cg->chart_id);
- free(cg->chart_title);
- free(cg);
-
- cgroup_root_count--;
+ debug(D_CGROUP, "Removing cgroup '%s' with chart id '%s' (was %s and %s)", cg->id, cg->chart_id, (cg->enabled)?"enabled":"disabled", (cg->available)?"available":"not available");
+
+ freez(cg->cpuacct_usage.cpu_percpu);
+
+ freez(cg->cpuacct_stat.filename);
+ freez(cg->cpuacct_usage.filename);
+ freez(cg->memory.filename);
+ freez(cg->io_service_bytes.filename);
+ freez(cg->io_serviced.filename);
+ freez(cg->throttle_io_service_bytes.filename);
+ freez(cg->throttle_io_serviced.filename);
+ freez(cg->io_merged.filename);
+ freez(cg->io_queued.filename);
+
+ freez(cg->id);
+ freez(cg->chart_id);
+ freez(cg->chart_title);
+ freez(cg);
+
+ cgroup_root_count--;
}
// find if a given cgroup exists
struct cgroup *cgroup_find(const char *id) {
- debug(D_CGROUP, "searching for cgroup '%s'", id);
+ debug(D_CGROUP, "searching for cgroup '%s'", id);
- uint32_t hash = simple_hash(id);
+ uint32_t hash = simple_hash(id);
- struct cgroup *cg;
- for(cg = cgroup_root; cg ; cg = cg->next) {
- if(hash == cg->hash && strcmp(id, cg->id) == 0)
- break;
- }
+ struct cgroup *cg;
+ for(cg = cgroup_root; cg ; cg = cg->next) {
+ if(hash == cg->hash && strcmp(id, cg->id) == 0)
+ break;
+ }
- debug(D_CGROUP, "cgroup_find('%s') %s", id, (cg)?"found":"not found");
- return cg;
+ debug(D_CGROUP, "cgroup_find('%s') %s", id, (cg)?"found":"not found");
+ return cg;
}
// ----------------------------------------------------------------------------
@@ -762,198 +775,252 @@ struct cgroup *cgroup_find(const char *id) {
// callback for find_file_in_subdirs()
void found_subdir_in_dir(const char *dir) {
- debug(D_CGROUP, "examining cgroup dir '%s'", dir);
-
- struct cgroup *cg = cgroup_find(dir);
- if(!cg) {
- if(*dir && cgroup_max_depth > 0) {
- int depth = 0;
- const char *s;
-
- for(s = dir; *s ;s++)
- if(unlikely(*s == '/'))
- depth++;
-
- if(depth > cgroup_max_depth) {
- info("cgroup '%s' is too deep (%d, while max is %d)", dir, depth, cgroup_max_depth);
- return;
- }
- }
- debug(D_CGROUP, "will add dir '%s' as cgroup", dir);
- cg = cgroup_add(dir);
- }
-
- if(cg) cg->available = 1;
+ debug(D_CGROUP, "examining cgroup dir '%s'", dir);
+
+ struct cgroup *cg = cgroup_find(dir);
+ if(!cg) {
+ if(*dir && cgroup_max_depth > 0) {
+ int depth = 0;
+ const char *s;
+
+ for(s = dir; *s ;s++)
+ if(unlikely(*s == '/'))
+ depth++;
+
+ if(depth > cgroup_max_depth) {
+ info("cgroup '%s' is too deep (%d, while max is %d)", dir, depth, cgroup_max_depth);
+ return;
+ }
+ }
+ debug(D_CGROUP, "will add dir '%s' as cgroup", dir);
+ cg = cgroup_add(dir);
+ }
+
+ if(cg) cg->available = 1;
}
-void find_dir_in_subdirs(const char *base, const char *this, void (*callback)(const char *)) {
- int enabled = -1;
- if(!this) this = base;
- size_t dirlen = strlen(this), baselen = strlen(base);
- const char *relative_path = &this[baselen];
-
- DIR *dir = opendir(this);
- if(!dir) return;
-
- callback(relative_path);
-
- struct dirent *de = NULL;
- while((de = readdir(dir))) {
- if(de->d_type == DT_DIR
- && (
- (de->d_name[0] == '.' && de->d_name[1] == '\0')
- || (de->d_name[0] == '.' && de->d_name[1] == '.' && de->d_name[2] == '\0')
- ))
- continue;
-
- debug(D_CGROUP, "examining '%s/%s'", this, de->d_name);
-
- if(de->d_type == DT_DIR) {
- if(enabled == -1) {
- const char *r = relative_path;
- if(*r == '\0') r = "/";
- else if (*r == '/') r++;
-
- // we check for this option here
- // so that the config will not have settings
- // for leaf directories
- char option[FILENAME_MAX + 1];
- snprintfz(option, FILENAME_MAX, "search for cgroups under %s", r);
- option[FILENAME_MAX] = '\0';
- enabled = config_get_boolean("plugin:cgroups", option, 1);
- }
-
- if(enabled) {
- char *s = malloc(dirlen + strlen(de->d_name) + 2);
- if(s) {
- strcpy(s, this);
- strcat(s, "/");
- strcat(s, de->d_name);
- find_dir_in_subdirs(base, s, callback);
- free(s);
- }
- }
- }
- }
-
- closedir(dir);
+int find_dir_in_subdirs(const char *base, const char *this, void (*callback)(const char *)) {
+ debug(D_CGROUP, "searching for directories in '%s'", base);
+
+ int ret = -1;
+ int enabled = -1;
+ if(!this) this = base;
+ size_t dirlen = strlen(this), baselen = strlen(base);
+ const char *relative_path = &this[baselen];
+
+ DIR *dir = opendir(this);
+ if(!dir) {
+ error("Cannot read cgroups directory '%s'", base);
+ return ret;
+ }
+ ret = 1;
+
+ callback(relative_path);
+
+ struct dirent *de = NULL;
+ while((de = readdir(dir))) {
+ if(de->d_type == DT_DIR
+ && (
+ (de->d_name[0] == '.' && de->d_name[1] == '\0')
+ || (de->d_name[0] == '.' && de->d_name[1] == '.' && de->d_name[2] == '\0')
+ ))
+ continue;
+
+ debug(D_CGROUP, "examining '%s/%s'", this, de->d_name);
+
+ if(de->d_type == DT_DIR) {
+ if(enabled == -1) {
+ const char *r = relative_path;
+ if(*r == '\0') r = "/";
+ else if (*r == '/') r++;
+
+ // we check for this option here
+ // so that the config will not have settings
+ // for leaf directories
+ char option[FILENAME_MAX + 1];
+ snprintfz(option, FILENAME_MAX, "search for cgroups under %s", r);
+ option[FILENAME_MAX] = '\0';
+ enabled = config_get_boolean("plugin:cgroups", option, 1);
+ }
+
+ if(enabled) {
+ char *s = mallocz(dirlen + strlen(de->d_name) + 2);
+ strcpy(s, this);
+ strcat(s, "/");
+ strcat(s, de->d_name);
+ int ret2 = find_dir_in_subdirs(base, s, callback);
+ if(ret2 > 0) ret += ret2;
+ freez(s);
+ }
+ }
+ }
+
+ closedir(dir);
+ return ret;
}
void mark_all_cgroups_as_not_available() {
- debug(D_CGROUP, "marking all cgroups as not available");
+ debug(D_CGROUP, "marking all cgroups as not available");
- struct cgroup *cg;
+ struct cgroup *cg;
- // mark all as not available
- for(cg = cgroup_root; cg ; cg = cg->next)
- cg->available = 0;
+ // mark all as not available
+ for(cg = cgroup_root; cg ; cg = cg->next)
+ cg->available = 0;
}
void cleanup_all_cgroups() {
- struct cgroup *cg = cgroup_root, *last = NULL;
-
- for(; cg ;) {
- if(!cg->available) {
-
- if(!last)
- cgroup_root = cg->next;
- else
- last->next = cg->next;
-
- cgroup_free(cg);
-
- if(!last)
- cg = cgroup_root;
- else
- cg = last->next;
- }
- else {
- last = cg;
- cg = cg->next;
- }
- }
+ struct cgroup *cg = cgroup_root, *last = NULL;
+
+ for(; cg ;) {
+ if(!cg->available) {
+
+ if(!last)
+ cgroup_root = cg->next;
+ else
+ last->next = cg->next;
+
+ cgroup_free(cg);
+
+ if(!last)
+ cg = cgroup_root;
+ else
+ cg = last->next;
+ }
+ else {
+ last = cg;
+ cg = cg->next;
+ }
+ }
}
void find_all_cgroups() {
- debug(D_CGROUP, "searching for cgroups");
-
- mark_all_cgroups_as_not_available();
-
- if(cgroup_enable_cpuacct_stat || cgroup_enable_cpuacct_usage)
- find_dir_in_subdirs(cgroup_cpuacct_base, NULL, found_subdir_in_dir);
-
- if(cgroup_enable_blkio)
- find_dir_in_subdirs(cgroup_blkio_base, NULL, found_subdir_in_dir);
-
- if(cgroup_enable_memory)
- find_dir_in_subdirs(cgroup_memory_base, NULL, found_subdir_in_dir);
-
- // remove any non-existing cgroups
- cleanup_all_cgroups();
-
- struct cgroup *cg;
- for(cg = cgroup_root; cg ; cg = cg->next) {
- // fprintf(stderr, " >>> CGROUP '%s' (%u - %s) with name '%s'\n", cg->id, cg->hash, cg->available?"available":"stopped", cg->name);
-
- if(unlikely(!cg->available))
- continue;
-
- debug(D_CGROUP, "checking paths for cgroup '%s'", cg->id);
-
- // check for newly added cgroups
- // and update the filenames they read
- char filename[FILENAME_MAX + 1];
- if(cgroup_enable_cpuacct_stat && !cg->cpuacct_stat.filename) {
- snprintfz(filename, FILENAME_MAX, "%s%s/cpuacct.stat", cgroup_cpuacct_base, cg->id);
- cg->cpuacct_stat.filename = strdup(filename);
- debug(D_CGROUP, "cpuacct.stat filename for cgroup '%s': '%s'", cg->id, cg->cpuacct_stat.filename);
- }
- if(cgroup_enable_cpuacct_usage && !cg->cpuacct_usage.filename) {
- snprintfz(filename, FILENAME_MAX, "%s%s/cpuacct.usage_percpu", cgroup_cpuacct_base, cg->id);
- cg->cpuacct_usage.filename = strdup(filename);
- debug(D_CGROUP, "cpuacct.usage_percpu filename for cgroup '%s': '%s'", cg->id, cg->cpuacct_usage.filename);
- }
- if(cgroup_enable_memory && !cg->memory.filename) {
- snprintfz(filename, FILENAME_MAX, "%s%s/memory.stat", cgroup_memory_base, cg->id);
- cg->memory.filename = strdup(filename);
- debug(D_CGROUP, "memory.stat filename for cgroup '%s': '%s'", cg->id, cg->memory.filename);
- }
- if(cgroup_enable_blkio) {
- if(!cg->io_service_bytes.filename) {
- snprintfz(filename, FILENAME_MAX, "%s%s/blkio.io_service_bytes", cgroup_blkio_base, cg->id);
- cg->io_service_bytes.filename = strdup(filename);
- debug(D_CGROUP, "io_service_bytes filename for cgroup '%s': '%s'", cg->id, cg->io_service_bytes.filename);
- }
- if(!cg->io_serviced.filename) {
- snprintfz(filename, FILENAME_MAX, "%s%s/blkio.io_serviced", cgroup_blkio_base, cg->id);
- cg->io_serviced.filename = strdup(filename);
- debug(D_CGROUP, "io_serviced filename for cgroup '%s': '%s'", cg->id, cg->io_serviced.filename);
- }
- if(!cg->throttle_io_service_bytes.filename) {
- snprintfz(filename, FILENAME_MAX, "%s%s/blkio.throttle.io_service_bytes", cgroup_blkio_base, cg->id);
- cg->throttle_io_service_bytes.filename = strdup(filename);
- debug(D_CGROUP, "throttle_io_service_bytes filename for cgroup '%s': '%s'", cg->id, cg->throttle_io_service_bytes.filename);
- }
- if(!cg->throttle_io_serviced.filename) {
- snprintfz(filename, FILENAME_MAX, "%s%s/blkio.throttle.io_serviced", cgroup_blkio_base, cg->id);
- cg->throttle_io_serviced.filename = strdup(filename);
- debug(D_CGROUP, "throttle_io_serviced filename for cgroup '%s': '%s'", cg->id, cg->throttle_io_serviced.filename);
- }
- if(!cg->io_merged.filename) {
- snprintfz(filename, FILENAME_MAX, "%s%s/blkio.io_merged", cgroup_blkio_base, cg->id);
- cg->io_merged.filename = strdup(filename);
- debug(D_CGROUP, "io_merged filename for cgroup '%s': '%s'", cg->id, cg->io_merged.filename);
- }
- if(!cg->io_queued.filename) {
- snprintfz(filename, FILENAME_MAX, "%s%s/blkio.io_queued", cgroup_blkio_base, cg->id);
- cg->io_queued.filename = strdup(filename);
- debug(D_CGROUP, "io_queued filename for cgroup '%s': '%s'", cg->id, cg->io_queued.filename);
- }
- }
- }
-
- debug(D_CGROUP, "done searching for cgroups");
- return;
+ debug(D_CGROUP, "searching for cgroups");
+
+ mark_all_cgroups_as_not_available();
+
+ if(cgroup_enable_cpuacct_stat || cgroup_enable_cpuacct_usage) {
+ if (find_dir_in_subdirs(cgroup_cpuacct_base, NULL, found_subdir_in_dir) == -1) {
+ cgroup_enable_cpuacct_stat = cgroup_enable_cpuacct_usage = 0;
+ error("disabled cgroup cpu statistics.");
+ }
+ }
+
+ if(cgroup_enable_blkio) {
+ if (find_dir_in_subdirs(cgroup_blkio_base, NULL, found_subdir_in_dir) == -1) {
+ cgroup_enable_blkio = 0;
+ error("disabled cgroup blkio statistics.");
+ }
+ }
+
+ if(cgroup_enable_memory) {
+ if(find_dir_in_subdirs(cgroup_memory_base, NULL, found_subdir_in_dir) == -1) {
+ cgroup_enable_memory = 0;
+ error("disabled cgroup memory statistics.");
+ }
+ }
+
+ if(cgroup_enable_devices) {
+ if(find_dir_in_subdirs(cgroup_devices_base, NULL, found_subdir_in_dir) == -1) {
+ cgroup_enable_devices = 0;
+ error("disabled cgroup devices statistics.");
+ }
+ }
+
+ // remove any non-existing cgroups
+ cleanup_all_cgroups();
+
+ struct cgroup *cg;
+ struct stat buf;
+ for(cg = cgroup_root; cg ; cg = cg->next) {
+ // fprintf(stderr, " >>> CGROUP '%s' (%u - %s) with name '%s'\n", cg->id, cg->hash, cg->available?"available":"stopped", cg->name);
+
+ if(unlikely(!cg->available))
+ continue;
+
+ debug(D_CGROUP, "checking paths for cgroup '%s'", cg->id);
+
+ // check for newly added cgroups
+ // and update the filenames they read
+ char filename[FILENAME_MAX + 1];
+ if(cgroup_enable_cpuacct_stat && !cg->cpuacct_stat.filename) {
+ snprintfz(filename, FILENAME_MAX, "%s%s/cpuacct.stat", cgroup_cpuacct_base, cg->id);
+ if(stat(filename, &buf) != -1) {
+ cg->cpuacct_stat.filename = strdupz(filename);
+ debug(D_CGROUP, "cpuacct.stat filename for cgroup '%s': '%s'", cg->id, cg->cpuacct_stat.filename);
+ }
+ else debug(D_CGROUP, "cpuacct.stat file for cgroup '%s': '%s' does not exist.", cg->id, filename);
+ }
+ if(cgroup_enable_cpuacct_usage && !cg->cpuacct_usage.filename) {
+ snprintfz(filename, FILENAME_MAX, "%s%s/cpuacct.usage_percpu", cgroup_cpuacct_base, cg->id);
+ if(stat(filename, &buf) != -1) {
+ cg->cpuacct_usage.filename = strdupz(filename);
+ debug(D_CGROUP, "cpuacct.usage_percpu filename for cgroup '%s': '%s'", cg->id, cg->cpuacct_usage.filename);
+ }
+ else debug(D_CGROUP, "cpuacct.usage_percpu file for cgroup '%s': '%s' does not exist.", cg->id, filename);
+ }
+ if(cgroup_enable_memory && !cg->memory.filename) {
+ snprintfz(filename, FILENAME_MAX, "%s%s/memory.stat", cgroup_memory_base, cg->id);
+ if(stat(filename, &buf) != -1) {
+ cg->memory.filename = strdupz(filename);
+ debug(D_CGROUP, "memory.stat filename for cgroup '%s': '%s'", cg->id, cg->memory.filename);
+ }
+ else debug(D_CGROUP, "memory.stat file for cgroup '%s': '%s' does not exist.", cg->id, filename);
+ }
+ if(cgroup_enable_blkio) {
+ if(!cg->io_service_bytes.filename) {
+ snprintfz(filename, FILENAME_MAX, "%s%s/blkio.io_service_bytes", cgroup_blkio_base, cg->id);
+ if(stat(filename, &buf) != -1) {
+ cg->io_service_bytes.filename = strdupz(filename);
+ debug(D_CGROUP, "io_service_bytes filename for cgroup '%s': '%s'", cg->id, cg->io_service_bytes.filename);
+ }
+ else debug(D_CGROUP, "io_service_bytes file for cgroup '%s': '%s' does not exist.", cg->id, filename);
+ }
+ if(!cg->io_serviced.filename) {
+ snprintfz(filename, FILENAME_MAX, "%s%s/blkio.io_serviced", cgroup_blkio_base, cg->id);
+ if(stat(filename, &buf) != -1) {
+ cg->io_serviced.filename = strdupz(filename);
+ debug(D_CGROUP, "io_serviced filename for cgroup '%s': '%s'", cg->id, cg->io_serviced.filename);
+ }
+ else debug(D_CGROUP, "io_serviced file for cgroup '%s': '%s' does not exist.", cg->id, filename);
+ }
+ if(!cg->throttle_io_service_bytes.filename) {
+ snprintfz(filename, FILENAME_MAX, "%s%s/blkio.throttle.io_service_bytes", cgroup_blkio_base, cg->id);
+ if(stat(filename, &buf) != -1) {
+ cg->throttle_io_service_bytes.filename = strdupz(filename);
+ debug(D_CGROUP, "throttle_io_service_bytes filename for cgroup '%s': '%s'", cg->id, cg->throttle_io_service_bytes.filename);
+ }
+ else debug(D_CGROUP, "throttle_io_service_bytes file for cgroup '%s': '%s' does not exist.", cg->id, filename);
+ }
+ if(!cg->throttle_io_serviced.filename) {
+ snprintfz(filename, FILENAME_MAX, "%s%s/blkio.throttle.io_serviced", cgroup_blkio_base, cg->id);
+ if(stat(filename, &buf) != -1) {
+ cg->throttle_io_serviced.filename = strdupz(filename);
+ debug(D_CGROUP, "throttle_io_serviced filename for cgroup '%s': '%s'", cg->id, cg->throttle_io_serviced.filename);
+ }
+ else debug(D_CGROUP, "throttle_io_serviced file for cgroup '%s': '%s' does not exist.", cg->id, filename);
+ }
+ if(!cg->io_merged.filename) {
+ snprintfz(filename, FILENAME_MAX, "%s%s/blkio.io_merged", cgroup_blkio_base, cg->id);
+ if(stat(filename, &buf) != -1) {
+ cg->io_merged.filename = strdupz(filename);
+ debug(D_CGROUP, "io_merged filename for cgroup '%s': '%s'", cg->id, cg->io_merged.filename);
+ }
+ else debug(D_CGROUP, "io_merged file for cgroup '%s': '%s' does not exist.", cg->id, filename);
+ }
+ if(!cg->io_queued.filename) {
+ snprintfz(filename, FILENAME_MAX, "%s%s/blkio.io_queued", cgroup_blkio_base, cg->id);
+ if(stat(filename, &buf) != -1) {
+ cg->io_queued.filename = strdupz(filename);
+ debug(D_CGROUP, "io_queued filename for cgroup '%s': '%s'", cg->id, cg->io_queued.filename);
+ }
+ else debug(D_CGROUP, "io_queued file for cgroup '%s': '%s' does not exist.", cg->id, filename);
+ }
+ }
+ }
+
+ debug(D_CGROUP, "done searching for cgroups");
+ return;
}
// ----------------------------------------------------------------------------
@@ -962,349 +1029,349 @@ void find_all_cgroups() {
#define CHART_TITLE_MAX 300
void update_cgroup_charts(int update_every) {
- debug(D_CGROUP, "updating cgroups charts");
-
- char type[RRD_ID_LENGTH_MAX + 1];
- char title[CHART_TITLE_MAX + 1];
-
- struct cgroup *cg;
- RRDSET *st;
-
- for(cg = cgroup_root; cg ; cg = cg->next) {
- if(!cg->available || !cg->enabled)
- continue;
-
- if(cg->id[0] == '\0')
- strcpy(type, "cgroup_root");
- else if(cg->id[0] == '/')
- snprintfz(type, RRD_ID_LENGTH_MAX, "cgroup_%s", cg->chart_id);
- else
- snprintfz(type, RRD_ID_LENGTH_MAX, "cgroup_%s", cg->chart_id);
-
- netdata_fix_chart_id(type);
-
- if(cg->cpuacct_stat.updated) {
- st = rrdset_find_bytype(type, "cpu");
- if(!st) {
- snprintfz(title, CHART_TITLE_MAX, "CPU Usage for cgroup %s", cg->chart_title);
- st = rrdset_create(type, "cpu", NULL, "cpu", "cgroup.cpu", title, "%", 40000, update_every, RRDSET_TYPE_STACKED);
-
- rrddim_add(st, "user", NULL, 100, hz, RRDDIM_INCREMENTAL);
- rrddim_add(st, "system", NULL, 100, hz, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "user", cg->cpuacct_stat.user);
- rrddim_set(st, "system", cg->cpuacct_stat.system);
- rrdset_done(st);
- }
-
- if(cg->cpuacct_usage.updated) {
- char id[RRD_ID_LENGTH_MAX + 1];
- unsigned int i;
-
- st = rrdset_find_bytype(type, "cpu_per_core");
- if(!st) {
- snprintfz(title, CHART_TITLE_MAX, "CPU Usage Per Core for cgroup %s", cg->chart_title);
- st = rrdset_create(type, "cpu_per_core", NULL, "cpu", "cgroup.cpu_per_core", title, "%", 40100, update_every, RRDSET_TYPE_STACKED);
-
- for(i = 0; i < cg->cpuacct_usage.cpus ;i++) {
- snprintfz(id, CHART_TITLE_MAX, "cpu%d", i);
- rrddim_add(st, id, NULL, 100, 1000000, RRDDIM_INCREMENTAL);
- }
- }
- else rrdset_next(st);
-
- for(i = 0; i < cg->cpuacct_usage.cpus ;i++) {
- snprintfz(id, CHART_TITLE_MAX, "cpu%d", i);
- rrddim_set(st, id, cg->cpuacct_usage.cpu_percpu[i]);
- }
- rrdset_done(st);
- }
-
- if(cg->memory.updated) {
- if(cg->memory.cache + cg->memory.rss + cg->memory.rss_huge + cg->memory.mapped_file > 0) {
- st = rrdset_find_bytype(type, "mem");
- if(!st) {
- snprintfz(title, CHART_TITLE_MAX, "Memory Usage for cgroup %s", cg->chart_title);
- st = rrdset_create(type, "mem", NULL, "mem", "cgroup.mem", title, "MB", 40200, update_every,
- RRDSET_TYPE_STACKED);
-
- rrddim_add(st, "cache", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
- rrddim_add(st, "rss", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
- if(cg->memory.has_dirty_swap)
- rrddim_add(st, "swap", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
- rrddim_add(st, "rss_huge", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
- rrddim_add(st, "mapped_file", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "cache", cg->memory.cache);
- rrddim_set(st, "rss", cg->memory.rss);
- if(cg->memory.has_dirty_swap)
- rrddim_set(st, "swap", cg->memory.swap);
- rrddim_set(st, "rss_huge", cg->memory.rss_huge);
- rrddim_set(st, "mapped_file", cg->memory.mapped_file);
- rrdset_done(st);
- }
-
- st = rrdset_find_bytype(type, "writeback");
- if(!st) {
- snprintfz(title, CHART_TITLE_MAX, "Writeback Memory for cgroup %s", cg->chart_title);
- st = rrdset_create(type, "writeback", NULL, "mem", "cgroup.writeback", title, "MB", 40300,
- update_every, RRDSET_TYPE_AREA);
-
- if(cg->memory.has_dirty_swap)
- rrddim_add(st, "dirty", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
- rrddim_add(st, "writeback", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
- }
- else rrdset_next(st);
-
- if(cg->memory.has_dirty_swap)
- rrddim_set(st, "dirty", cg->memory.dirty);
- rrddim_set(st, "writeback", cg->memory.writeback);
- rrdset_done(st);
-
- if(cg->memory.pgpgin + cg->memory.pgpgout > 0) {
- st = rrdset_find_bytype(type, "mem_activity");
- if(!st) {
- snprintfz(title, CHART_TITLE_MAX, "Memory Activity for cgroup %s", cg->chart_title);
- st = rrdset_create(type, "mem_activity", NULL, "mem", "cgroup.mem_activity", title, "MB/s",
- 40400, update_every, RRDSET_TYPE_LINE);
-
- rrddim_add(st, "pgpgin", "in", sysconf(_SC_PAGESIZE), 1024 * 1024, RRDDIM_INCREMENTAL);
- rrddim_add(st, "pgpgout", "out", -sysconf(_SC_PAGESIZE), 1024 * 1024, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "pgpgin", cg->memory.pgpgin);
- rrddim_set(st, "pgpgout", cg->memory.pgpgout);
- rrdset_done(st);
- }
-
- if(cg->memory.pgfault + cg->memory.pgmajfault > 0) {
- st = rrdset_find_bytype(type, "pgfaults");
- if(!st) {
- snprintfz(title, CHART_TITLE_MAX, "Memory Page Faults for cgroup %s", cg->chart_title);
- st = rrdset_create(type, "pgfaults", NULL, "mem", "cgroup.pgfaults", title, "MB/s", 40500,
- update_every, RRDSET_TYPE_LINE);
-
- rrddim_add(st, "pgfault", NULL, sysconf(_SC_PAGESIZE), 1024 * 1024, RRDDIM_INCREMENTAL);
- rrddim_add(st, "pgmajfault", "swap", -sysconf(_SC_PAGESIZE), 1024 * 1024, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "pgfault", cg->memory.pgfault);
- rrddim_set(st, "pgmajfault", cg->memory.pgmajfault);
- rrdset_done(st);
- }
- }
-
- if(cg->io_service_bytes.updated && cg->io_service_bytes.Read + cg->io_service_bytes.Write > 0) {
- st = rrdset_find_bytype(type, "io");
- if(!st) {
- snprintfz(title, CHART_TITLE_MAX, "I/O Bandwidth (all disks) for cgroup %s", cg->chart_title);
- st = rrdset_create(type, "io", NULL, "disk", "cgroup.io", title, "KB/s", 41200,
- update_every, RRDSET_TYPE_LINE);
-
- rrddim_add(st, "read", NULL, 1, 1024, RRDDIM_INCREMENTAL);
- rrddim_add(st, "write", NULL, -1, 1024, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "read", cg->io_service_bytes.Read);
- rrddim_set(st, "write", cg->io_service_bytes.Write);
- rrdset_done(st);
- }
-
- if(cg->io_serviced.updated && cg->io_serviced.Read + cg->io_serviced.Write > 0) {
- st = rrdset_find_bytype(type, "serviced_ops");
- if(!st) {
- snprintfz(title, CHART_TITLE_MAX, "Serviced I/O Operations (all disks) for cgroup %s", cg->chart_title);
- st = rrdset_create(type, "serviced_ops", NULL, "disk", "cgroup.serviced_ops", title, "operations/s", 41200,
- update_every, RRDSET_TYPE_LINE);
-
- rrddim_add(st, "read", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "write", NULL, -1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "read", cg->io_serviced.Read);
- rrddim_set(st, "write", cg->io_serviced.Write);
- rrdset_done(st);
- }
-
- if(cg->throttle_io_service_bytes.updated && cg->throttle_io_service_bytes.Read + cg->throttle_io_service_bytes.Write > 0) {
- st = rrdset_find_bytype(type, "io");
- if(!st) {
- snprintfz(title, CHART_TITLE_MAX, "Throttle I/O Bandwidth (all disks) for cgroup %s", cg->chart_title);
- st = rrdset_create(type, "io", NULL, "disk", "cgroup.io", title, "KB/s", 41200,
- update_every, RRDSET_TYPE_LINE);
-
- rrddim_add(st, "read", NULL, 1, 1024, RRDDIM_INCREMENTAL);
- rrddim_add(st, "write", NULL, -1, 1024, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "read", cg->throttle_io_service_bytes.Read);
- rrddim_set(st, "write", cg->throttle_io_service_bytes.Write);
- rrdset_done(st);
- }
-
-
- if(cg->throttle_io_serviced.updated && cg->throttle_io_serviced.Read + cg->throttle_io_serviced.Write > 0) {
- st = rrdset_find_bytype(type, "throttle_serviced_ops");
- if(!st) {
- snprintfz(title, CHART_TITLE_MAX, "Throttle Serviced I/O Operations (all disks) for cgroup %s", cg->chart_title);
- st = rrdset_create(type, "throttle_serviced_ops", NULL, "disk", "cgroup.throttle_serviced_ops", title, "operations/s", 41200,
- update_every, RRDSET_TYPE_LINE);
-
- rrddim_add(st, "read", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "write", NULL, -1, 1, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "read", cg->throttle_io_serviced.Read);
- rrddim_set(st, "write", cg->throttle_io_serviced.Write);
- rrdset_done(st);
- }
-
- if(cg->io_queued.updated) {
- st = rrdset_find_bytype(type, "queued_ops");
- if(!st) {
- snprintfz(title, CHART_TITLE_MAX, "Queued I/O Operations (all disks) for cgroup %s", cg->chart_title);
- st = rrdset_create(type, "queued_ops", NULL, "disk", "cgroup.queued_ops", title, "operations", 42000,
- update_every, RRDSET_TYPE_LINE);
-
- rrddim_add(st, "read", NULL, 1, 1, RRDDIM_ABSOLUTE);
- rrddim_add(st, "write", NULL, -1, 1, RRDDIM_ABSOLUTE);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "read", cg->io_queued.Read);
- rrddim_set(st, "write", cg->io_queued.Write);
- rrdset_done(st);
- }
-
- if(cg->io_merged.updated && cg->io_merged.Read + cg->io_merged.Write > 0) {
- st = rrdset_find_bytype(type, "merged_ops");
- if(!st) {
- snprintfz(title, CHART_TITLE_MAX, "Merged I/O Operations (all disks) for cgroup %s", cg->chart_title);
- st = rrdset_create(type, "merged_ops", NULL, "disk", "cgroup.merged_ops", title, "operations/s", 42100,
- update_every, RRDSET_TYPE_LINE);
-
- rrddim_add(st, "read", NULL, 1, 1024, RRDDIM_INCREMENTAL);
- rrddim_add(st, "write", NULL, -1, 1024, RRDDIM_INCREMENTAL);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "read", cg->io_merged.Read);
- rrddim_set(st, "write", cg->io_merged.Write);
- rrdset_done(st);
- }
- }
-
- debug(D_CGROUP, "done updating cgroups charts");
+ debug(D_CGROUP, "updating cgroups charts");
+
+ char type[RRD_ID_LENGTH_MAX + 1];
+ char title[CHART_TITLE_MAX + 1];
+
+ struct cgroup *cg;
+ RRDSET *st;
+
+ for(cg = cgroup_root; cg ; cg = cg->next) {
+ if(!cg->available || !cg->enabled)
+ continue;
+
+ if(cg->id[0] == '\0')
+ strcpy(type, "cgroup_root");
+ else if(cg->id[0] == '/')
+ snprintfz(type, RRD_ID_LENGTH_MAX, "cgroup_%s", cg->chart_id);
+ else
+ snprintfz(type, RRD_ID_LENGTH_MAX, "cgroup_%s", cg->chart_id);
+
+ netdata_fix_chart_id(type);
+
+ if(cg->cpuacct_stat.updated) {
+ st = rrdset_find_bytype(type, "cpu");
+ if(!st) {
+ snprintfz(title, CHART_TITLE_MAX, "CPU Usage for cgroup %s", cg->chart_title);
+ st = rrdset_create(type, "cpu", NULL, "cpu", "cgroup.cpu", title, "%", 40000, update_every, RRDSET_TYPE_STACKED);
+
+ rrddim_add(st, "user", NULL, 100, hz, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "system", NULL, 100, hz, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "user", cg->cpuacct_stat.user);
+ rrddim_set(st, "system", cg->cpuacct_stat.system);
+ rrdset_done(st);
+ }
+
+ if(cg->cpuacct_usage.updated) {
+ char id[RRD_ID_LENGTH_MAX + 1];
+ unsigned int i;
+
+ st = rrdset_find_bytype(type, "cpu_per_core");
+ if(!st) {
+ snprintfz(title, CHART_TITLE_MAX, "CPU Usage Per Core for cgroup %s", cg->chart_title);
+ st = rrdset_create(type, "cpu_per_core", NULL, "cpu", "cgroup.cpu_per_core", title, "%", 40100, update_every, RRDSET_TYPE_STACKED);
+
+ for(i = 0; i < cg->cpuacct_usage.cpus ;i++) {
+ snprintfz(id, CHART_TITLE_MAX, "cpu%u", i);
+ rrddim_add(st, id, NULL, 100, 1000000000, RRDDIM_INCREMENTAL);
+ }
+ }
+ else rrdset_next(st);
+
+ for(i = 0; i < cg->cpuacct_usage.cpus ;i++) {
+ snprintfz(id, CHART_TITLE_MAX, "cpu%u", i);
+ rrddim_set(st, id, cg->cpuacct_usage.cpu_percpu[i]);
+ }
+ rrdset_done(st);
+ }
+
+ if(cg->memory.updated) {
+ if(cg->memory.cache + cg->memory.rss + cg->memory.rss_huge + cg->memory.mapped_file > 0) {
+ st = rrdset_find_bytype(type, "mem");
+ if(!st) {
+ snprintfz(title, CHART_TITLE_MAX, "Memory Usage for cgroup %s", cg->chart_title);
+ st = rrdset_create(type, "mem", NULL, "mem", "cgroup.mem", title, "MB", 40200, update_every,
+ RRDSET_TYPE_STACKED);
+
+ rrddim_add(st, "cache", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
+ rrddim_add(st, "rss", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
+ if(cg->memory.has_dirty_swap)
+ rrddim_add(st, "swap", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
+ rrddim_add(st, "rss_huge", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
+ rrddim_add(st, "mapped_file", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "cache", cg->memory.cache);
+ rrddim_set(st, "rss", cg->memory.rss);
+ if(cg->memory.has_dirty_swap)
+ rrddim_set(st, "swap", cg->memory.swap);
+ rrddim_set(st, "rss_huge", cg->memory.rss_huge);
+ rrddim_set(st, "mapped_file", cg->memory.mapped_file);
+ rrdset_done(st);
+ }
+
+ st = rrdset_find_bytype(type, "writeback");
+ if(!st) {
+ snprintfz(title, CHART_TITLE_MAX, "Writeback Memory for cgroup %s", cg->chart_title);
+ st = rrdset_create(type, "writeback", NULL, "mem", "cgroup.writeback", title, "MB", 40300,
+ update_every, RRDSET_TYPE_AREA);
+
+ if(cg->memory.has_dirty_swap)
+ rrddim_add(st, "dirty", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
+ rrddim_add(st, "writeback", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
+ }
+ else rrdset_next(st);
+
+ if(cg->memory.has_dirty_swap)
+ rrddim_set(st, "dirty", cg->memory.dirty);
+ rrddim_set(st, "writeback", cg->memory.writeback);
+ rrdset_done(st);
+
+ if(cg->memory.pgpgin + cg->memory.pgpgout > 0) {
+ st = rrdset_find_bytype(type, "mem_activity");
+ if(!st) {
+ snprintfz(title, CHART_TITLE_MAX, "Memory Activity for cgroup %s", cg->chart_title);
+ st = rrdset_create(type, "mem_activity", NULL, "mem", "cgroup.mem_activity", title, "MB/s",
+ 40400, update_every, RRDSET_TYPE_LINE);
+
+ rrddim_add(st, "pgpgin", "in", sysconf(_SC_PAGESIZE), 1024 * 1024, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "pgpgout", "out", -sysconf(_SC_PAGESIZE), 1024 * 1024, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "pgpgin", cg->memory.pgpgin);
+ rrddim_set(st, "pgpgout", cg->memory.pgpgout);
+ rrdset_done(st);
+ }
+
+ if(cg->memory.pgfault + cg->memory.pgmajfault > 0) {
+ st = rrdset_find_bytype(type, "pgfaults");
+ if(!st) {
+ snprintfz(title, CHART_TITLE_MAX, "Memory Page Faults for cgroup %s", cg->chart_title);
+ st = rrdset_create(type, "pgfaults", NULL, "mem", "cgroup.pgfaults", title, "MB/s", 40500,
+ update_every, RRDSET_TYPE_LINE);
+
+ rrddim_add(st, "pgfault", NULL, sysconf(_SC_PAGESIZE), 1024 * 1024, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "pgmajfault", "swap", -sysconf(_SC_PAGESIZE), 1024 * 1024, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "pgfault", cg->memory.pgfault);
+ rrddim_set(st, "pgmajfault", cg->memory.pgmajfault);
+ rrdset_done(st);
+ }
+ }
+
+ if(cg->io_service_bytes.updated && cg->io_service_bytes.Read + cg->io_service_bytes.Write > 0) {
+ st = rrdset_find_bytype(type, "io");
+ if(!st) {
+ snprintfz(title, CHART_TITLE_MAX, "I/O Bandwidth (all disks) for cgroup %s", cg->chart_title);
+ st = rrdset_create(type, "io", NULL, "disk", "cgroup.io", title, "KB/s", 41200,
+ update_every, RRDSET_TYPE_LINE);
+
+ rrddim_add(st, "read", NULL, 1, 1024, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "write", NULL, -1, 1024, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "read", cg->io_service_bytes.Read);
+ rrddim_set(st, "write", cg->io_service_bytes.Write);
+ rrdset_done(st);
+ }
+
+ if(cg->io_serviced.updated && cg->io_serviced.Read + cg->io_serviced.Write > 0) {
+ st = rrdset_find_bytype(type, "serviced_ops");
+ if(!st) {
+ snprintfz(title, CHART_TITLE_MAX, "Serviced I/O Operations (all disks) for cgroup %s", cg->chart_title);
+ st = rrdset_create(type, "serviced_ops", NULL, "disk", "cgroup.serviced_ops", title, "operations/s", 41200,
+ update_every, RRDSET_TYPE_LINE);
+
+ rrddim_add(st, "read", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "write", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "read", cg->io_serviced.Read);
+ rrddim_set(st, "write", cg->io_serviced.Write);
+ rrdset_done(st);
+ }
+
+ if(cg->throttle_io_service_bytes.updated && cg->throttle_io_service_bytes.Read + cg->throttle_io_service_bytes.Write > 0) {
+ st = rrdset_find_bytype(type, "io");
+ if(!st) {
+ snprintfz(title, CHART_TITLE_MAX, "Throttle I/O Bandwidth (all disks) for cgroup %s", cg->chart_title);
+ st = rrdset_create(type, "io", NULL, "disk", "cgroup.io", title, "KB/s", 41200,
+ update_every, RRDSET_TYPE_LINE);
+
+ rrddim_add(st, "read", NULL, 1, 1024, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "write", NULL, -1, 1024, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "read", cg->throttle_io_service_bytes.Read);
+ rrddim_set(st, "write", cg->throttle_io_service_bytes.Write);
+ rrdset_done(st);
+ }
+
+
+ if(cg->throttle_io_serviced.updated && cg->throttle_io_serviced.Read + cg->throttle_io_serviced.Write > 0) {
+ st = rrdset_find_bytype(type, "throttle_serviced_ops");
+ if(!st) {
+ snprintfz(title, CHART_TITLE_MAX, "Throttle Serviced I/O Operations (all disks) for cgroup %s", cg->chart_title);
+ st = rrdset_create(type, "throttle_serviced_ops", NULL, "disk", "cgroup.throttle_serviced_ops", title, "operations/s", 41200,
+ update_every, RRDSET_TYPE_LINE);
+
+ rrddim_add(st, "read", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "write", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "read", cg->throttle_io_serviced.Read);
+ rrddim_set(st, "write", cg->throttle_io_serviced.Write);
+ rrdset_done(st);
+ }
+
+ if(cg->io_queued.updated) {
+ st = rrdset_find_bytype(type, "queued_ops");
+ if(!st) {
+ snprintfz(title, CHART_TITLE_MAX, "Queued I/O Operations (all disks) for cgroup %s", cg->chart_title);
+ st = rrdset_create(type, "queued_ops", NULL, "disk", "cgroup.queued_ops", title, "operations", 42000,
+ update_every, RRDSET_TYPE_LINE);
+
+ rrddim_add(st, "read", NULL, 1, 1, RRDDIM_ABSOLUTE);
+ rrddim_add(st, "write", NULL, -1, 1, RRDDIM_ABSOLUTE);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "read", cg->io_queued.Read);
+ rrddim_set(st, "write", cg->io_queued.Write);
+ rrdset_done(st);
+ }
+
+ if(cg->io_merged.updated && cg->io_merged.Read + cg->io_merged.Write > 0) {
+ st = rrdset_find_bytype(type, "merged_ops");
+ if(!st) {
+ snprintfz(title, CHART_TITLE_MAX, "Merged I/O Operations (all disks) for cgroup %s", cg->chart_title);
+ st = rrdset_create(type, "merged_ops", NULL, "disk", "cgroup.merged_ops", title, "operations/s", 42100,
+ update_every, RRDSET_TYPE_LINE);
+
+ rrddim_add(st, "read", NULL, 1, 1024, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "write", NULL, -1, 1024, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "read", cg->io_merged.Read);
+ rrddim_set(st, "write", cg->io_merged.Write);
+ rrdset_done(st);
+ }
+ }
+
+ debug(D_CGROUP, "done updating cgroups charts");
}
// ----------------------------------------------------------------------------
// cgroups main
int do_sys_fs_cgroup(int update_every, unsigned long long dt) {
- static int cgroup_global_config_read = 0;
- static time_t last_run = 0;
- time_t now = time(NULL);
+ (void)dt;
- if(dt) {};
+ static int cgroup_global_config_read = 0;
+ static time_t last_run = 0;
+ time_t now = time(NULL);
- if(unlikely(!cgroup_global_config_read)) {
- read_cgroup_plugin_configuration();
- cgroup_global_config_read = 1;
- }
+ 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;
- }
+ 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);
+ read_all_cgroups(cgroup_root);
+ update_cgroup_charts(update_every);
- return 0;
+ return 0;
}
void *cgroups_main(void *ptr)
{
- if(ptr) { ; }
+ if(ptr) { ; }
- info("CGROUP Plugin thread created with task id %d", gettid());
+ info("CGROUP Plugin thread created with task id %d", gettid());
- if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
- error("Cannot set pthread cancel type to DEFERRED.");
+ if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
+ error("Cannot set pthread cancel type to DEFERRED.");
- if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
- error("Cannot set pthread cancel state to ENABLE.");
+ if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
+ error("Cannot set pthread cancel state to ENABLE.");
- struct rusage thread;
+ 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);
+ // 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);
- // keep track of the time each module was called
- unsigned long long sutime_sys_fs_cgroup = 0ULL;
+ // keep track of the time each module was called
+ unsigned long long sutime_sys_fs_cgroup = 0ULL;
- // the next time we will run - aligned properly
- unsigned long long sunext = (time(NULL) - (time(NULL) % rrd_update_every) + rrd_update_every) * 1000000ULL;
- unsigned long long sunow;
+ // the next time we will run - aligned properly
+ unsigned long long sunext = (time(NULL) - (time(NULL) % rrd_update_every) + rrd_update_every) * 1000000ULL;
+ unsigned long long sunow;
- RRDSET *stcpu_thread = NULL;
+ RRDSET *stcpu_thread = NULL;
- for(;1;) {
- if(unlikely(netdata_exit)) break;
+ for(;1;) {
+ if(unlikely(netdata_exit)) break;
- // delay until it is our time to run
- while((sunow = timems()) < sunext)
- usleep((useconds_t)(sunext - sunow));
+ // delay until it is our time to run
+ while((sunow = time_usec()) < sunext)
+ sleep_usec(sunext - sunow);
- // find the next time we need to run
- while(timems() > sunext)
- sunext += rrd_update_every * 1000000ULL;
+ // find the next time we need to run
+ while(time_usec() > sunext)
+ sunext += rrd_update_every * 1000000ULL;
- if(unlikely(netdata_exit)) break;
+ if(unlikely(netdata_exit)) break;
- // BEGIN -- the job to be done
+ // BEGIN -- the job to be done
- if(!vdo_sys_fs_cgroup) {
- debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_sys_fs_cgroup().");
- sunow = timems();
- 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(netdata_exit)) break;
+ if(!vdo_sys_fs_cgroup) {
+ debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_sys_fs_cgroup().");
+ sunow = time_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(netdata_exit)) break;
- // END -- the job is done
+ // END -- the job is done
- // --------------------------------------------------------------------
+ // --------------------------------------------------------------------
- if(!vdo_cpu_netdata) {
- getrusage(RUSAGE_THREAD, &thread);
+ 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, "proc.internal", NULL, "NetData CGroups Plugin CPU usage", "milliseconds/s", 132000, rrd_update_every, RRDSET_TYPE_STACKED);
+ if(!stcpu_thread) stcpu_thread = rrdset_find("netdata.plugin_cgroups_cpu");
+ if(!stcpu_thread) {
+ stcpu_thread = rrdset_create("netdata", "plugin_cgroups_cpu", NULL, "proc.internal", NULL, "NetData CGroups Plugin CPU usage", "milliseconds/s", 132000, rrd_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);
- }
- else rrdset_next(stcpu_thread);
+ rrddim_add(stcpu_thread, "user", NULL, 1, 1000, RRDDIM_INCREMENTAL);
+ rrddim_add(stcpu_thread, "system", NULL, 1, 1000, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(stcpu_thread);
- rrddim_set(stcpu_thread, "user" , thread.ru_utime.tv_sec * 1000000ULL + thread.ru_utime.tv_usec);
- rrddim_set(stcpu_thread, "system", thread.ru_stime.tv_sec * 1000000ULL + thread.ru_stime.tv_usec);
- rrdset_done(stcpu_thread);
- }
- }
+ rrddim_set(stcpu_thread, "user" , thread.ru_utime.tv_sec * 1000000ULL + thread.ru_utime.tv_usec);
+ rrddim_set(stcpu_thread, "system", thread.ru_stime.tv_sec * 1000000ULL + thread.ru_stime.tv_usec);
+ rrdset_done(stcpu_thread);
+ }
+ }
- pthread_exit(NULL);
- return NULL;
+ pthread_exit(NULL);
+ return NULL;
}
diff --git a/src/sys_kernel_mm_ksm.c b/src/sys_kernel_mm_ksm.c
index 928ac8c62..8c51be1df 100644
--- a/src/sys_kernel_mm_ksm.c
+++ b/src/sys_kernel_mm_ksm.c
@@ -1,21 +1,9 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
#include "common.h"
-#include "log.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "rrd.h"
-#include "plugin_proc.h"
-typedef struct name_value {
- char filename[FILENAME_MAX + 1];
- unsigned long long value;
-} NAME_VALUE;
+typedef struct ksm_name_value {
+ char filename[FILENAME_MAX + 1];
+ unsigned long long value;
+} KSM_NAME_VALUE;
#define PAGES_SHARED 0
#define PAGES_SHARING 1
@@ -23,128 +11,128 @@ typedef struct name_value {
#define PAGES_VOLATILE 3
#define PAGES_TO_SCAN 4
-NAME_VALUE values[] = {
- [PAGES_SHARED] = { "/sys/kernel/mm/ksm/pages_shared", 0ULL },
- [PAGES_SHARING] = { "/sys/kernel/mm/ksm/pages_sharing", 0ULL },
- [PAGES_UNSHARED] = { "/sys/kernel/mm/ksm/pages_unshared", 0ULL },
- [PAGES_VOLATILE] = { "/sys/kernel/mm/ksm/pages_volatile", 0ULL },
- [PAGES_TO_SCAN] = { "/sys/kernel/mm/ksm/pages_to_scan", 0ULL },
+KSM_NAME_VALUE values[] = {
+ [PAGES_SHARED] = { "/sys/kernel/mm/ksm/pages_shared", 0ULL },
+ [PAGES_SHARING] = { "/sys/kernel/mm/ksm/pages_sharing", 0ULL },
+ [PAGES_UNSHARED] = { "/sys/kernel/mm/ksm/pages_unshared", 0ULL },
+ [PAGES_VOLATILE] = { "/sys/kernel/mm/ksm/pages_volatile", 0ULL },
+ [PAGES_TO_SCAN] = { "/sys/kernel/mm/ksm/pages_to_scan", 0ULL },
};
int do_sys_kernel_mm_ksm(int update_every, unsigned long long dt) {
- static procfile *ff_pages_shared = NULL, *ff_pages_sharing = NULL, *ff_pages_unshared = NULL, *ff_pages_volatile = NULL, *ff_pages_to_scan = NULL;
- static long page_size = -1;
+ static procfile *ff_pages_shared = NULL, *ff_pages_sharing = NULL, *ff_pages_unshared = NULL, *ff_pages_volatile = NULL, *ff_pages_to_scan = NULL;
+ static long page_size = -1;
- if(dt) {};
-
- if(page_size == -1)
- page_size = sysconf(_SC_PAGESIZE);
-
- if(!ff_pages_shared) {
- snprintfz(values[PAGES_SHARED].filename, FILENAME_MAX, "%s%s", global_host_prefix, "/sys/kernel/mm/ksm/pages_shared");
- snprintfz(values[PAGES_SHARED].filename, FILENAME_MAX, "%s", config_get("plugin:proc:/sys/kernel/mm/ksm", "/sys/kernel/mm/ksm/pages_shared", values[PAGES_SHARED].filename));
- ff_pages_shared = procfile_open(values[PAGES_SHARED].filename, " \t:", PROCFILE_FLAG_DEFAULT);
- }
+ if(dt) {};
+
+ if(page_size == -1)
+ page_size = sysconf(_SC_PAGESIZE);
+
+ if(!ff_pages_shared) {
+ snprintfz(values[PAGES_SHARED].filename, FILENAME_MAX, "%s%s", global_host_prefix, "/sys/kernel/mm/ksm/pages_shared");
+ snprintfz(values[PAGES_SHARED].filename, FILENAME_MAX, "%s", config_get("plugin:proc:/sys/kernel/mm/ksm", "/sys/kernel/mm/ksm/pages_shared", values[PAGES_SHARED].filename));
+ ff_pages_shared = procfile_open(values[PAGES_SHARED].filename, " \t:", PROCFILE_FLAG_DEFAULT);
+ }
- if(!ff_pages_sharing) {
- snprintfz(values[PAGES_SHARING].filename, FILENAME_MAX, "%s%s", global_host_prefix, "/sys/kernel/mm/ksm/pages_sharing");
- snprintfz(values[PAGES_SHARING].filename, FILENAME_MAX, "%s", config_get("plugin:proc:/sys/kernel/mm/ksm", "/sys/kernel/mm/ksm/pages_sharing", values[PAGES_SHARING].filename));
- ff_pages_sharing = procfile_open(values[PAGES_SHARING].filename, " \t:", PROCFILE_FLAG_DEFAULT);
- }
+ if(!ff_pages_sharing) {
+ snprintfz(values[PAGES_SHARING].filename, FILENAME_MAX, "%s%s", global_host_prefix, "/sys/kernel/mm/ksm/pages_sharing");
+ snprintfz(values[PAGES_SHARING].filename, FILENAME_MAX, "%s", config_get("plugin:proc:/sys/kernel/mm/ksm", "/sys/kernel/mm/ksm/pages_sharing", values[PAGES_SHARING].filename));
+ ff_pages_sharing = procfile_open(values[PAGES_SHARING].filename, " \t:", PROCFILE_FLAG_DEFAULT);
+ }
- if(!ff_pages_unshared) {
- snprintfz(values[PAGES_UNSHARED].filename, FILENAME_MAX, "%s%s", global_host_prefix, "/sys/kernel/mm/ksm/pages_unshared");
- snprintfz(values[PAGES_UNSHARED].filename, FILENAME_MAX, "%s", config_get("plugin:proc:/sys/kernel/mm/ksm", "/sys/kernel/mm/ksm/pages_unshared", values[PAGES_UNSHARED].filename));
- ff_pages_unshared = procfile_open(values[PAGES_UNSHARED].filename, " \t:", PROCFILE_FLAG_DEFAULT);
- }
+ if(!ff_pages_unshared) {
+ snprintfz(values[PAGES_UNSHARED].filename, FILENAME_MAX, "%s%s", global_host_prefix, "/sys/kernel/mm/ksm/pages_unshared");
+ snprintfz(values[PAGES_UNSHARED].filename, FILENAME_MAX, "%s", config_get("plugin:proc:/sys/kernel/mm/ksm", "/sys/kernel/mm/ksm/pages_unshared", values[PAGES_UNSHARED].filename));
+ ff_pages_unshared = procfile_open(values[PAGES_UNSHARED].filename, " \t:", PROCFILE_FLAG_DEFAULT);
+ }
- if(!ff_pages_volatile) {
- snprintfz(values[PAGES_VOLATILE].filename, FILENAME_MAX, "%s%s", global_host_prefix, "/sys/kernel/mm/ksm/pages_volatile");
- snprintfz(values[PAGES_VOLATILE].filename, FILENAME_MAX, "%s", config_get("plugin:proc:/sys/kernel/mm/ksm", "/sys/kernel/mm/ksm/pages_volatile", values[PAGES_VOLATILE].filename));
- ff_pages_volatile = procfile_open(values[PAGES_VOLATILE].filename, " \t:", PROCFILE_FLAG_DEFAULT);
- }
+ if(!ff_pages_volatile) {
+ snprintfz(values[PAGES_VOLATILE].filename, FILENAME_MAX, "%s%s", global_host_prefix, "/sys/kernel/mm/ksm/pages_volatile");
+ snprintfz(values[PAGES_VOLATILE].filename, FILENAME_MAX, "%s", config_get("plugin:proc:/sys/kernel/mm/ksm", "/sys/kernel/mm/ksm/pages_volatile", values[PAGES_VOLATILE].filename));
+ ff_pages_volatile = procfile_open(values[PAGES_VOLATILE].filename, " \t:", PROCFILE_FLAG_DEFAULT);
+ }
- if(!ff_pages_to_scan) {
- snprintfz(values[PAGES_TO_SCAN].filename, FILENAME_MAX, "%s%s", global_host_prefix, "/sys/kernel/mm/ksm/pages_to_scan");
- snprintfz(values[PAGES_TO_SCAN].filename, FILENAME_MAX, "%s", config_get("plugin:proc:/sys/kernel/mm/ksm", "/sys/kernel/mm/ksm/pages_to_scan", values[PAGES_TO_SCAN].filename));
- ff_pages_to_scan = procfile_open(values[PAGES_TO_SCAN].filename, " \t:", PROCFILE_FLAG_DEFAULT);
- }
+ if(!ff_pages_to_scan) {
+ snprintfz(values[PAGES_TO_SCAN].filename, FILENAME_MAX, "%s%s", global_host_prefix, "/sys/kernel/mm/ksm/pages_to_scan");
+ snprintfz(values[PAGES_TO_SCAN].filename, FILENAME_MAX, "%s", config_get("plugin:proc:/sys/kernel/mm/ksm", "/sys/kernel/mm/ksm/pages_to_scan", values[PAGES_TO_SCAN].filename));
+ ff_pages_to_scan = procfile_open(values[PAGES_TO_SCAN].filename, " \t:", PROCFILE_FLAG_DEFAULT);
+ }
- if(!ff_pages_shared || !ff_pages_sharing || !ff_pages_unshared || !ff_pages_volatile || !ff_pages_to_scan) return 1;
+ if(!ff_pages_shared || !ff_pages_sharing || !ff_pages_unshared || !ff_pages_volatile || !ff_pages_to_scan) return 1;
- unsigned long long pages_shared = 0, pages_sharing = 0, pages_unshared = 0, pages_volatile = 0, pages_to_scan = 0, offered = 0, saved = 0;
+ unsigned long long pages_shared = 0, pages_sharing = 0, pages_unshared = 0, pages_volatile = 0, pages_to_scan = 0, offered = 0, saved = 0;
- ff_pages_shared = procfile_readall(ff_pages_shared);
- if(!ff_pages_shared) return 0; // we return 0, so that we will retry to open it next time
- pages_shared = strtoull(procfile_lineword(ff_pages_shared, 0, 0), NULL, 10);
+ ff_pages_shared = procfile_readall(ff_pages_shared);
+ if(!ff_pages_shared) return 0; // we return 0, so that we will retry to open it next time
+ pages_shared = strtoull(procfile_lineword(ff_pages_shared, 0, 0), NULL, 10);
- ff_pages_sharing = procfile_readall(ff_pages_sharing);
- if(!ff_pages_sharing) return 0; // we return 0, so that we will retry to open it next time
- pages_sharing = strtoull(procfile_lineword(ff_pages_sharing, 0, 0), NULL, 10);
+ ff_pages_sharing = procfile_readall(ff_pages_sharing);
+ if(!ff_pages_sharing) return 0; // we return 0, so that we will retry to open it next time
+ pages_sharing = strtoull(procfile_lineword(ff_pages_sharing, 0, 0), NULL, 10);
- ff_pages_unshared = procfile_readall(ff_pages_unshared);
- if(!ff_pages_unshared) return 0; // we return 0, so that we will retry to open it next time
- pages_unshared = strtoull(procfile_lineword(ff_pages_unshared, 0, 0), NULL, 10);
+ ff_pages_unshared = procfile_readall(ff_pages_unshared);
+ if(!ff_pages_unshared) return 0; // we return 0, so that we will retry to open it next time
+ pages_unshared = strtoull(procfile_lineword(ff_pages_unshared, 0, 0), NULL, 10);
- ff_pages_volatile = procfile_readall(ff_pages_volatile);
- if(!ff_pages_volatile) return 0; // we return 0, so that we will retry to open it next time
- pages_volatile = strtoull(procfile_lineword(ff_pages_volatile, 0, 0), NULL, 10);
+ ff_pages_volatile = procfile_readall(ff_pages_volatile);
+ if(!ff_pages_volatile) return 0; // we return 0, so that we will retry to open it next time
+ pages_volatile = strtoull(procfile_lineword(ff_pages_volatile, 0, 0), NULL, 10);
- ff_pages_to_scan = procfile_readall(ff_pages_to_scan);
- if(!ff_pages_to_scan) return 0; // we return 0, so that we will retry to open it next time
- pages_to_scan = strtoull(procfile_lineword(ff_pages_to_scan, 0, 0), NULL, 10);
+ ff_pages_to_scan = procfile_readall(ff_pages_to_scan);
+ if(!ff_pages_to_scan) return 0; // we return 0, so that we will retry to open it next time
+ pages_to_scan = strtoull(procfile_lineword(ff_pages_to_scan, 0, 0), NULL, 10);
- offered = pages_sharing + pages_unshared + pages_volatile;
- saved = pages_sharing - pages_shared;
+ offered = pages_sharing + pages_shared + pages_unshared + pages_volatile;
+ saved = pages_sharing - pages_shared;
- if(!offered || !pages_to_scan) return 0;
+ if(!offered || !pages_to_scan) return 0;
- RRDSET *st;
+ RRDSET *st;
- // --------------------------------------------------------------------
+ // --------------------------------------------------------------------
- st = rrdset_find("mem.ksm");
- if(!st) {
- st = rrdset_create("mem", "ksm", NULL, "ksm", NULL, "Kernel Same Page Merging", "MB", 5000, update_every, RRDSET_TYPE_AREA);
+ st = rrdset_find("mem.ksm");
+ if(!st) {
+ st = rrdset_create("mem", "ksm", NULL, "ksm", NULL, "Kernel Same Page Merging", "MB", 5000, update_every, RRDSET_TYPE_AREA);
- rrddim_add(st, "shared", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
- rrddim_add(st, "unshared", NULL, -1, 1024 * 1024, RRDDIM_ABSOLUTE);
- rrddim_add(st, "sharing", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
- rrddim_add(st, "volatile", NULL, -1, 1024 * 1024, RRDDIM_ABSOLUTE);
- rrddim_add(st, "to_scan", "to scan", -1, 1024 * 1024, RRDDIM_ABSOLUTE);
- }
- else rrdset_next(st);
+ rrddim_add(st, "shared", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
+ rrddim_add(st, "unshared", NULL, -1, 1024 * 1024, RRDDIM_ABSOLUTE);
+ rrddim_add(st, "sharing", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
+ rrddim_add(st, "volatile", NULL, -1, 1024 * 1024, RRDDIM_ABSOLUTE);
+ rrddim_add(st, "to_scan", "to scan", -1, 1024 * 1024, RRDDIM_ABSOLUTE);
+ }
+ else rrdset_next(st);
- rrddim_set(st, "shared", pages_shared * page_size);
- rrddim_set(st, "unshared", pages_unshared * page_size);
- rrddim_set(st, "sharing", pages_sharing * page_size);
- rrddim_set(st, "volatile", pages_volatile * page_size);
- rrddim_set(st, "to_scan", pages_to_scan * page_size);
- rrdset_done(st);
+ rrddim_set(st, "shared", pages_shared * page_size);
+ rrddim_set(st, "unshared", pages_unshared * page_size);
+ rrddim_set(st, "sharing", pages_sharing * page_size);
+ rrddim_set(st, "volatile", pages_volatile * page_size);
+ rrddim_set(st, "to_scan", pages_to_scan * page_size);
+ rrdset_done(st);
- st = rrdset_find("mem.ksm_savings");
- if(!st) {
- st = rrdset_create("mem", "ksm_savings", NULL, "ksm", NULL, "Kernel Same Page Merging Savings", "MB", 5001, update_every, RRDSET_TYPE_AREA);
+ st = rrdset_find("mem.ksm_savings");
+ if(!st) {
+ st = rrdset_create("mem", "ksm_savings", NULL, "ksm", NULL, "Kernel Same Page Merging Savings", "MB", 5001, update_every, RRDSET_TYPE_AREA);
- rrddim_add(st, "savings", NULL, -1, 1024 * 1024, RRDDIM_ABSOLUTE);
- rrddim_add(st, "offered", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
- }
- else rrdset_next(st);
+ rrddim_add(st, "savings", NULL, -1, 1024 * 1024, RRDDIM_ABSOLUTE);
+ rrddim_add(st, "offered", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
+ }
+ else rrdset_next(st);
- rrddim_set(st, "savings", saved * page_size);
- rrddim_set(st, "offered", offered * page_size);
- rrdset_done(st);
+ rrddim_set(st, "savings", saved * page_size);
+ rrddim_set(st, "offered", offered * page_size);
+ rrdset_done(st);
- st = rrdset_find("mem.ksm_ratios");
- if(!st) {
- st = rrdset_create("mem", "ksm_ratios", NULL, "ksm", NULL, "Kernel Same Page Merging Effectiveness", "percentage", 5002, update_every, RRDSET_TYPE_LINE);
-
- rrddim_add(st, "savings", NULL, 1, 10000, RRDDIM_ABSOLUTE);
- }
- else rrdset_next(st);
-
- rrddim_set(st, "savings", (saved * 1000000) / offered);
- rrdset_done(st);
+ st = rrdset_find("mem.ksm_ratios");
+ if(!st) {
+ st = rrdset_create("mem", "ksm_ratios", NULL, "ksm", NULL, "Kernel Same Page Merging Effectiveness", "percentage", 5002, update_every, RRDSET_TYPE_LINE);
+
+ rrddim_add(st, "savings", NULL, 1, 10000, RRDDIM_ABSOLUTE);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "savings", (saved * 1000000) / offered);
+ rrdset_done(st);
- return 0;
+ return 0;
}
diff --git a/src/unit_test.c b/src/unit_test.c
index 06b7afacb..a77fdb134 100644
--- a/src/unit_test.c
+++ b/src/unit_test.c
@@ -1,267 +1,258 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/resource.h>
-
#include "common.h"
-#include "storage_number.h"
-#include "rrd.h"
-#include "log.h"
-#include "web_buffer.h"
int check_storage_number(calculated_number n, int debug) {
- char buffer[100];
- uint32_t flags = SN_EXISTS;
-
- storage_number s = pack_storage_number(n, flags);
- calculated_number d = unpack_storage_number(s);
-
- if(!does_storage_number_exist(s)) {
- fprintf(stderr, "Exists flags missing for number " CALCULATED_NUMBER_FORMAT "!\n", n);
- return 5;
- }
-
- calculated_number ddiff = d - n;
- calculated_number dcdiff = ddiff * 100.0 / n;
-
- if(dcdiff < 0) dcdiff = -dcdiff;
-
- size_t len = print_calculated_number(buffer, d);
- calculated_number p = strtold(buffer, NULL);
- calculated_number pdiff = n - p;
- calculated_number pcdiff = pdiff * 100.0 / n;
- if(pcdiff < 0) pcdiff = -pcdiff;
-
- if(debug) {
- fprintf(stderr,
- CALCULATED_NUMBER_FORMAT " original\n"
- CALCULATED_NUMBER_FORMAT " packed and unpacked, (stored as 0x%08X, diff " CALCULATED_NUMBER_FORMAT ", " CALCULATED_NUMBER_FORMAT "%%)\n"
- "%s printed after unpacked (%zu bytes)\n"
- CALCULATED_NUMBER_FORMAT " re-parsed from printed (diff " CALCULATED_NUMBER_FORMAT ", " CALCULATED_NUMBER_FORMAT "%%)\n\n",
- n,
- d, s, ddiff, dcdiff,
- buffer,
- len, p, pdiff, pcdiff
- );
- if(len != strlen(buffer)) fprintf(stderr, "ERROR: printed number %s is reported to have length %zu but it has %zu\n", buffer, len, strlen(buffer));
- if(dcdiff > ACCURACY_LOSS) fprintf(stderr, "WARNING: packing number " CALCULATED_NUMBER_FORMAT " has accuracy loss %0.7Lf %%\n", n, dcdiff);
- if(pcdiff > ACCURACY_LOSS) fprintf(stderr, "WARNING: re-parsing the packed, unpacked and printed number " CALCULATED_NUMBER_FORMAT " has accuracy loss %0.7Lf %%\n", n, pcdiff);
- }
-
- if(len != strlen(buffer)) return 1;
- if(dcdiff > ACCURACY_LOSS) return 3;
- if(pcdiff > ACCURACY_LOSS) return 4;
- return 0;
+ char buffer[100];
+ uint32_t flags = SN_EXISTS;
+
+ storage_number s = pack_storage_number(n, flags);
+ calculated_number d = unpack_storage_number(s);
+
+ if(!does_storage_number_exist(s)) {
+ fprintf(stderr, "Exists flags missing for number " CALCULATED_NUMBER_FORMAT "!\n", n);
+ return 5;
+ }
+
+ calculated_number ddiff = d - n;
+ calculated_number dcdiff = ddiff * 100.0 / n;
+
+ if(dcdiff < 0) dcdiff = -dcdiff;
+
+ size_t len = print_calculated_number(buffer, d);
+ calculated_number p = strtold(buffer, NULL);
+ calculated_number pdiff = n - p;
+ calculated_number pcdiff = pdiff * 100.0 / n;
+ if(pcdiff < 0) pcdiff = -pcdiff;
+
+ if(debug) {
+ fprintf(stderr,
+ CALCULATED_NUMBER_FORMAT " original\n"
+ CALCULATED_NUMBER_FORMAT " packed and unpacked, (stored as 0x%08X, diff " CALCULATED_NUMBER_FORMAT ", " CALCULATED_NUMBER_FORMAT "%%)\n"
+ "%s printed after unpacked (%zu bytes)\n"
+ CALCULATED_NUMBER_FORMAT " re-parsed from printed (diff " CALCULATED_NUMBER_FORMAT ", " CALCULATED_NUMBER_FORMAT "%%)\n\n",
+ n,
+ d, s, ddiff, dcdiff,
+ buffer,
+ len, p, pdiff, pcdiff
+ );
+ if(len != strlen(buffer)) fprintf(stderr, "ERROR: printed number %s is reported to have length %zu but it has %zu\n", buffer, len, strlen(buffer));
+ if(dcdiff > ACCURACY_LOSS) fprintf(stderr, "WARNING: packing number " CALCULATED_NUMBER_FORMAT " has accuracy loss %0.7Lf %%\n", n, dcdiff);
+ if(pcdiff > ACCURACY_LOSS) fprintf(stderr, "WARNING: re-parsing the packed, unpacked and printed number " CALCULATED_NUMBER_FORMAT " has accuracy loss %0.7Lf %%\n", n, pcdiff);
+ }
+
+ if(len != strlen(buffer)) return 1;
+ if(dcdiff > ACCURACY_LOSS) return 3;
+ if(pcdiff > ACCURACY_LOSS) return 4;
+ return 0;
}
void benchmark_storage_number(int loop, int multiplier) {
- int i, j;
- calculated_number n, d;
- storage_number s;
- unsigned long long user, system, total, mine, their;
+ int i, j;
+ calculated_number n, d;
+ storage_number s;
+ unsigned long long user, system, total, mine, their;
- char buffer[100];
+ char buffer[100];
- struct rusage now, last;
+ struct rusage now, last;
- fprintf(stderr, "\n\nBenchmarking %d numbers, please wait...\n\n", loop);
+ fprintf(stderr, "\n\nBenchmarking %d numbers, please wait...\n\n", loop);
- // ------------------------------------------------------------------------
+ // ------------------------------------------------------------------------
- fprintf(stderr, "SYSTEM LONG DOUBLE SIZE: %zu bytes\n", sizeof(calculated_number));
- fprintf(stderr, "NETDATA FLOATING POINT SIZE: %zu bytes\n", sizeof(storage_number));
+ fprintf(stderr, "SYSTEM LONG DOUBLE SIZE: %zu bytes\n", sizeof(calculated_number));
+ fprintf(stderr, "NETDATA FLOATING POINT SIZE: %zu bytes\n", sizeof(storage_number));
- mine = (calculated_number)sizeof(storage_number) * (calculated_number)loop;
- their = (calculated_number)sizeof(calculated_number) * (calculated_number)loop;
+ mine = (calculated_number)sizeof(storage_number) * (calculated_number)loop;
+ their = (calculated_number)sizeof(calculated_number) * (calculated_number)loop;
- if(mine > their) {
- fprintf(stderr, "\nNETDATA NEEDS %0.2Lf TIMES MORE MEMORY. Sorry!\n", (long double)(mine / their));
- }
- else {
- fprintf(stderr, "\nNETDATA INTERNAL FLOATING POINT ARITHMETICS NEEDS %0.2Lf TIMES LESS MEMORY.\n", (long double)(their / mine));
- }
+ if(mine > their) {
+ fprintf(stderr, "\nNETDATA NEEDS %0.2Lf TIMES MORE MEMORY. Sorry!\n", (long double)(mine / their));
+ }
+ else {
+ fprintf(stderr, "\nNETDATA INTERNAL FLOATING POINT ARITHMETICS NEEDS %0.2Lf TIMES LESS MEMORY.\n", (long double)(their / mine));
+ }
- fprintf(stderr, "\nNETDATA FLOATING POINT\n");
- fprintf(stderr, "MIN POSITIVE VALUE " CALCULATED_NUMBER_FORMAT "\n", (calculated_number)STORAGE_NUMBER_POSITIVE_MIN);
- fprintf(stderr, "MAX POSITIVE VALUE " CALCULATED_NUMBER_FORMAT "\n", (calculated_number)STORAGE_NUMBER_POSITIVE_MAX);
- fprintf(stderr, "MIN NEGATIVE VALUE " CALCULATED_NUMBER_FORMAT "\n", (calculated_number)STORAGE_NUMBER_NEGATIVE_MIN);
- fprintf(stderr, "MAX NEGATIVE VALUE " CALCULATED_NUMBER_FORMAT "\n", (calculated_number)STORAGE_NUMBER_NEGATIVE_MAX);
- fprintf(stderr, "Maximum accuracy loss: " CALCULATED_NUMBER_FORMAT "%%\n\n\n", (calculated_number)ACCURACY_LOSS);
+ fprintf(stderr, "\nNETDATA FLOATING POINT\n");
+ fprintf(stderr, "MIN POSITIVE VALUE " CALCULATED_NUMBER_FORMAT "\n", (calculated_number)STORAGE_NUMBER_POSITIVE_MIN);
+ fprintf(stderr, "MAX POSITIVE VALUE " CALCULATED_NUMBER_FORMAT "\n", (calculated_number)STORAGE_NUMBER_POSITIVE_MAX);
+ fprintf(stderr, "MIN NEGATIVE VALUE " CALCULATED_NUMBER_FORMAT "\n", (calculated_number)STORAGE_NUMBER_NEGATIVE_MIN);
+ fprintf(stderr, "MAX NEGATIVE VALUE " CALCULATED_NUMBER_FORMAT "\n", (calculated_number)STORAGE_NUMBER_NEGATIVE_MAX);
+ fprintf(stderr, "Maximum accuracy loss: " CALCULATED_NUMBER_FORMAT "%%\n\n\n", (calculated_number)ACCURACY_LOSS);
- // ------------------------------------------------------------------------
+ // ------------------------------------------------------------------------
- fprintf(stderr, "INTERNAL LONG DOUBLE PRINTING: ");
- getrusage(RUSAGE_SELF, &last);
+ fprintf(stderr, "INTERNAL LONG DOUBLE PRINTING: ");
+ getrusage(RUSAGE_SELF, &last);
- // do the job
- for(j = 1; j < 11 ;j++) {
- n = STORAGE_NUMBER_POSITIVE_MIN * j;
+ // do the job
+ for(j = 1; j < 11 ;j++) {
+ n = STORAGE_NUMBER_POSITIVE_MIN * j;
- for(i = 0; i < loop ;i++) {
- n *= multiplier;
- if(n > STORAGE_NUMBER_POSITIVE_MAX) n = STORAGE_NUMBER_POSITIVE_MIN;
+ for(i = 0; i < loop ;i++) {
+ n *= multiplier;
+ if(n > STORAGE_NUMBER_POSITIVE_MAX) n = STORAGE_NUMBER_POSITIVE_MIN;
- print_calculated_number(buffer, n);
- }
- }
+ print_calculated_number(buffer, n);
+ }
+ }
- getrusage(RUSAGE_SELF, &now);
- user = now.ru_utime.tv_sec * 1000000ULL + now.ru_utime.tv_usec - last.ru_utime.tv_sec * 1000000ULL + last.ru_utime.tv_usec;
- system = now.ru_stime.tv_sec * 1000000ULL + now.ru_stime.tv_usec - last.ru_stime.tv_sec * 1000000ULL + last.ru_stime.tv_usec;
- total = user + system;
- mine = total;
+ getrusage(RUSAGE_SELF, &now);
+ user = now.ru_utime.tv_sec * 1000000ULL + now.ru_utime.tv_usec - last.ru_utime.tv_sec * 1000000ULL + last.ru_utime.tv_usec;
+ system = now.ru_stime.tv_sec * 1000000ULL + now.ru_stime.tv_usec - last.ru_stime.tv_sec * 1000000ULL + last.ru_stime.tv_usec;
+ total = user + system;
+ mine = total;
- fprintf(stderr, "user %0.5Lf, system %0.5Lf, total %0.5Lf\n", (long double)(user / 1000000.0), (long double)(system / 1000000.0), (long double)(total / 1000000.0));
+ fprintf(stderr, "user %0.5Lf, system %0.5Lf, total %0.5Lf\n", (long double)(user / 1000000.0), (long double)(system / 1000000.0), (long double)(total / 1000000.0));
- // ------------------------------------------------------------------------
+ // ------------------------------------------------------------------------
- fprintf(stderr, "SYSTEM LONG DOUBLE PRINTING: ");
- getrusage(RUSAGE_SELF, &last);
+ fprintf(stderr, "SYSTEM LONG DOUBLE PRINTING: ");
+ getrusage(RUSAGE_SELF, &last);
- // do the job
- for(j = 1; j < 11 ;j++) {
- n = STORAGE_NUMBER_POSITIVE_MIN * j;
+ // do the job
+ for(j = 1; j < 11 ;j++) {
+ n = STORAGE_NUMBER_POSITIVE_MIN * j;
- for(i = 0; i < loop ;i++) {
- n *= multiplier;
- if(n > STORAGE_NUMBER_POSITIVE_MAX) n = STORAGE_NUMBER_POSITIVE_MIN;
- snprintfz(buffer, 100, CALCULATED_NUMBER_FORMAT, n);
- }
- }
+ for(i = 0; i < loop ;i++) {
+ n *= multiplier;
+ if(n > STORAGE_NUMBER_POSITIVE_MAX) n = STORAGE_NUMBER_POSITIVE_MIN;
+ snprintfz(buffer, 100, CALCULATED_NUMBER_FORMAT, n);
+ }
+ }
- getrusage(RUSAGE_SELF, &now);
- user = now.ru_utime.tv_sec * 1000000ULL + now.ru_utime.tv_usec - last.ru_utime.tv_sec * 1000000ULL + last.ru_utime.tv_usec;
- system = now.ru_stime.tv_sec * 1000000ULL + now.ru_stime.tv_usec - last.ru_stime.tv_sec * 1000000ULL + last.ru_stime.tv_usec;
- total = user + system;
- their = total;
+ getrusage(RUSAGE_SELF, &now);
+ user = now.ru_utime.tv_sec * 1000000ULL + now.ru_utime.tv_usec - last.ru_utime.tv_sec * 1000000ULL + last.ru_utime.tv_usec;
+ system = now.ru_stime.tv_sec * 1000000ULL + now.ru_stime.tv_usec - last.ru_stime.tv_sec * 1000000ULL + last.ru_stime.tv_usec;
+ total = user + system;
+ their = total;
- fprintf(stderr, "user %0.5Lf, system %0.5Lf, total %0.5Lf\n", (long double)(user / 1000000.0), (long double)(system / 1000000.0), (long double)(total / 1000000.0));
+ fprintf(stderr, "user %0.5Lf, system %0.5Lf, total %0.5Lf\n", (long double)(user / 1000000.0), (long double)(system / 1000000.0), (long double)(total / 1000000.0));
- if(mine > total) {
- fprintf(stderr, "NETDATA CODE IS SLOWER %0.2Lf %%\n", (long double)(mine * 100.0 / their - 100.0));
- }
- else {
- fprintf(stderr, "NETDATA CODE IS F A S T E R %0.2Lf %%\n", (long double)(their * 100.0 / mine - 100.0));
- }
+ if(mine > total) {
+ fprintf(stderr, "NETDATA CODE IS SLOWER %0.2Lf %%\n", (long double)(mine * 100.0 / their - 100.0));
+ }
+ else {
+ fprintf(stderr, "NETDATA CODE IS F A S T E R %0.2Lf %%\n", (long double)(their * 100.0 / mine - 100.0));
+ }
- // ------------------------------------------------------------------------
+ // ------------------------------------------------------------------------
- fprintf(stderr, "\nINTERNAL LONG DOUBLE PRINTING WITH PACK / UNPACK: ");
- getrusage(RUSAGE_SELF, &last);
+ fprintf(stderr, "\nINTERNAL LONG DOUBLE PRINTING WITH PACK / UNPACK: ");
+ getrusage(RUSAGE_SELF, &last);
- // do the job
- for(j = 1; j < 11 ;j++) {
- n = STORAGE_NUMBER_POSITIVE_MIN * j;
+ // do the job
+ for(j = 1; j < 11 ;j++) {
+ n = STORAGE_NUMBER_POSITIVE_MIN * j;
- for(i = 0; i < loop ;i++) {
- n *= multiplier;
- if(n > STORAGE_NUMBER_POSITIVE_MAX) n = STORAGE_NUMBER_POSITIVE_MIN;
+ for(i = 0; i < loop ;i++) {
+ n *= multiplier;
+ if(n > STORAGE_NUMBER_POSITIVE_MAX) n = STORAGE_NUMBER_POSITIVE_MIN;
- s = pack_storage_number(n, 1);
- d = unpack_storage_number(s);
- print_calculated_number(buffer, d);
- }
- }
+ s = pack_storage_number(n, 1);
+ d = unpack_storage_number(s);
+ print_calculated_number(buffer, d);
+ }
+ }
- getrusage(RUSAGE_SELF, &now);
- user = now.ru_utime.tv_sec * 1000000ULL + now.ru_utime.tv_usec - last.ru_utime.tv_sec * 1000000ULL + last.ru_utime.tv_usec;
- system = now.ru_stime.tv_sec * 1000000ULL + now.ru_stime.tv_usec - last.ru_stime.tv_sec * 1000000ULL + last.ru_stime.tv_usec;
- total = user + system;
- mine = total;
-
- fprintf(stderr, "user %0.5Lf, system %0.5Lf, total %0.5Lf\n", (long double)(user / 1000000.0), (long double)(system / 1000000.0), (long double)(total / 1000000.0));
+ getrusage(RUSAGE_SELF, &now);
+ user = now.ru_utime.tv_sec * 1000000ULL + now.ru_utime.tv_usec - last.ru_utime.tv_sec * 1000000ULL + last.ru_utime.tv_usec;
+ system = now.ru_stime.tv_sec * 1000000ULL + now.ru_stime.tv_usec - last.ru_stime.tv_sec * 1000000ULL + last.ru_stime.tv_usec;
+ total = user + system;
+ mine = total;
+
+ fprintf(stderr, "user %0.5Lf, system %0.5Lf, total %0.5Lf\n", (long double)(user / 1000000.0), (long double)(system / 1000000.0), (long double)(total / 1000000.0));
- if(mine > their) {
- fprintf(stderr, "WITH PACKING UNPACKING NETDATA CODE IS SLOWER %0.2Lf %%\n", (long double)(mine * 100.0 / their - 100.0));
- }
- else {
- fprintf(stderr, "EVEN WITH PACKING AND UNPACKING, NETDATA CODE IS F A S T E R %0.2Lf %%\n", (long double)(their * 100.0 / mine - 100.0));
- }
+ if(mine > their) {
+ fprintf(stderr, "WITH PACKING UNPACKING NETDATA CODE IS SLOWER %0.2Lf %%\n", (long double)(mine * 100.0 / their - 100.0));
+ }
+ else {
+ fprintf(stderr, "EVEN WITH PACKING AND UNPACKING, NETDATA CODE IS F A S T E R %0.2Lf %%\n", (long double)(their * 100.0 / mine - 100.0));
+ }
- // ------------------------------------------------------------------------
+ // ------------------------------------------------------------------------
}
static int check_storage_number_exists() {
- uint32_t flags = SN_EXISTS;
-
-
- for(flags = 0; flags < 7 ; flags++) {
- if(get_storage_number_flags(flags << 24) != flags << 24) {
- fprintf(stderr, "Flag 0x%08x is not checked correctly. It became 0x%08x\n", flags << 24, get_storage_number_flags(flags << 24));
- return 1;
- }
- }
-
- flags = SN_EXISTS;
- calculated_number n = 0.0;
-
- storage_number s = pack_storage_number(n, flags);
- calculated_number d = unpack_storage_number(s);
- if(get_storage_number_flags(s) != flags) {
- fprintf(stderr, "Wrong flags. Given %08x, Got %08x!\n", flags, get_storage_number_flags(s));
- return 1;
- }
- if(n != d) {
- fprintf(stderr, "Wrong number returned. Expected " CALCULATED_NUMBER_FORMAT ", returned " CALCULATED_NUMBER_FORMAT "!\n", n, d);
- return 1;
- }
-
- return 0;
+ uint32_t flags = SN_EXISTS;
+
+
+ for(flags = 0; flags < 7 ; flags++) {
+ if(get_storage_number_flags(flags << 24) != flags << 24) {
+ fprintf(stderr, "Flag 0x%08x is not checked correctly. It became 0x%08x\n", flags << 24, get_storage_number_flags(flags << 24));
+ return 1;
+ }
+ }
+
+ flags = SN_EXISTS;
+ calculated_number n = 0.0;
+
+ storage_number s = pack_storage_number(n, flags);
+ calculated_number d = unpack_storage_number(s);
+ if(get_storage_number_flags(s) != flags) {
+ fprintf(stderr, "Wrong flags. Given %08x, Got %08x!\n", flags, get_storage_number_flags(s));
+ return 1;
+ }
+ if(n != d) {
+ fprintf(stderr, "Wrong number returned. Expected " CALCULATED_NUMBER_FORMAT ", returned " CALCULATED_NUMBER_FORMAT "!\n", n, d);
+ return 1;
+ }
+
+ return 0;
}
int unit_test_storage()
{
- if(check_storage_number_exists()) return 0;
+ if(check_storage_number_exists()) return 0;
- calculated_number c, a = 0;
- int i, j, g, r = 0;
+ calculated_number c, a = 0;
+ int i, j, g, r = 0;
- for(g = -1; g <= 1 ; g++) {
- a = 0;
+ for(g = -1; g <= 1 ; g++) {
+ a = 0;
- if(!g) continue;
+ if(!g) continue;
- for(j = 0; j < 9 ;j++) {
- a += 0.0000001;
- c = a * g;
- for(i = 0; i < 21 ;i++, c *= 10) {
- if(c > 0 && c < STORAGE_NUMBER_POSITIVE_MIN) continue;
- if(c < 0 && c > STORAGE_NUMBER_NEGATIVE_MAX) continue;
+ for(j = 0; j < 9 ;j++) {
+ a += 0.0000001;
+ c = a * g;
+ for(i = 0; i < 21 ;i++, c *= 10) {
+ if(c > 0 && c < STORAGE_NUMBER_POSITIVE_MIN) continue;
+ if(c < 0 && c > STORAGE_NUMBER_NEGATIVE_MAX) continue;
- if(check_storage_number(c, 1)) return 1;
- }
- }
- }
+ if(check_storage_number(c, 1)) return 1;
+ }
+ }
+ }
- benchmark_storage_number(1000000, 2);
- return r;
+ benchmark_storage_number(1000000, 2);
+ return r;
}
// --------------------------------------------------------------------------------------------------------------------
struct feed_values {
- unsigned long long microseconds;
- calculated_number value;
+ unsigned long long microseconds;
+ collected_number value;
};
struct test {
- char name[100];
- char description[1024];
+ char name[100];
+ char description[1024];
+
+ int update_every;
+ unsigned long long multiplier;
+ unsigned long long divisor;
+ int algorithm;
- int update_every;
- unsigned long long multiplier;
- unsigned long long divisor;
- int algorithm;
+ unsigned long feed_entries;
+ unsigned long result_entries;
+ struct feed_values *feed;
+ calculated_number *results;
- unsigned long feed_entries;
- unsigned long result_entries;
- struct feed_values *feed;
- calculated_number *results;
+ collected_number *feed2;
+ calculated_number *results2;
};
// --------------------------------------------------------------------------------------------------------------------
@@ -269,33 +260,35 @@ struct test {
// test absolute values stored
struct feed_values test1_feed[] = {
- { 0, 10 },
- { 1000000, 20 },
- { 1000000, 30 },
- { 1000000, 40 },
- { 1000000, 50 },
- { 1000000, 60 },
- { 1000000, 70 },
- { 1000000, 80 },
- { 1000000, 90 },
- { 1000000, 100 },
+ { 0, 10 },
+ { 1000000, 20 },
+ { 1000000, 30 },
+ { 1000000, 40 },
+ { 1000000, 50 },
+ { 1000000, 60 },
+ { 1000000, 70 },
+ { 1000000, 80 },
+ { 1000000, 90 },
+ { 1000000, 100 },
};
calculated_number test1_results[] = {
- 20, 30, 40, 50, 60, 70, 80, 90, 100
+ 20, 30, 40, 50, 60, 70, 80, 90, 100
};
struct test test1 = {
- "test1", // name
- "test absolute values stored at exactly second boundaries",
- 1, // update_every
- 1, // multiplier
- 1, // divisor
- RRDDIM_ABSOLUTE, // algorithm
- 10, // feed entries
- 9, // result entries
- test1_feed, // feed
- test1_results // results
+ "test1", // name
+ "test absolute values stored at exactly second boundaries",
+ 1, // update_every
+ 1, // multiplier
+ 1, // divisor
+ RRDDIM_ABSOLUTE, // algorithm
+ 10, // feed entries
+ 9, // result entries
+ test1_feed, // feed
+ test1_results, // results
+ NULL, // feed2
+ NULL // results2
};
// --------------------------------------------------------------------------------------------------------------------
@@ -303,497 +296,667 @@ struct test test1 = {
// test absolute values stored in the middle of second boundaries
struct feed_values test2_feed[] = {
- { 500000, 10 },
- { 1000000, 20 },
- { 1000000, 30 },
- { 1000000, 40 },
- { 1000000, 50 },
- { 1000000, 60 },
- { 1000000, 70 },
- { 1000000, 80 },
- { 1000000, 90 },
- { 1000000, 100 },
+ { 500000, 10 },
+ { 1000000, 20 },
+ { 1000000, 30 },
+ { 1000000, 40 },
+ { 1000000, 50 },
+ { 1000000, 60 },
+ { 1000000, 70 },
+ { 1000000, 80 },
+ { 1000000, 90 },
+ { 1000000, 100 },
};
calculated_number test2_results[] = {
- 20, 30, 40, 50, 60, 70, 80, 90, 100
+ 20, 30, 40, 50, 60, 70, 80, 90, 100
};
struct test test2 = {
- "test2", // name
- "test absolute values stored in the middle of second boundaries",
- 1, // update_every
- 1, // multiplier
- 1, // divisor
- RRDDIM_ABSOLUTE, // algorithm
- 10, // feed entries
- 9, // result entries
- test2_feed, // feed
- test2_results // results
+ "test2", // name
+ "test absolute values stored in the middle of second boundaries",
+ 1, // update_every
+ 1, // multiplier
+ 1, // divisor
+ RRDDIM_ABSOLUTE, // algorithm
+ 10, // feed entries
+ 9, // result entries
+ test2_feed, // feed
+ test2_results, // results
+ NULL, // feed2
+ NULL // results2
};
// --------------------------------------------------------------------------------------------------------------------
// test3
struct feed_values test3_feed[] = {
- { 0, 10 },
- { 1000000, 20 },
- { 1000000, 30 },
- { 1000000, 40 },
- { 1000000, 50 },
- { 1000000, 60 },
- { 1000000, 70 },
- { 1000000, 80 },
- { 1000000, 90 },
- { 1000000, 100 },
+ { 0, 10 },
+ { 1000000, 20 },
+ { 1000000, 30 },
+ { 1000000, 40 },
+ { 1000000, 50 },
+ { 1000000, 60 },
+ { 1000000, 70 },
+ { 1000000, 80 },
+ { 1000000, 90 },
+ { 1000000, 100 },
};
calculated_number test3_results[] = {
- 10, 10, 10, 10, 10, 10, 10, 10, 10
+ 10, 10, 10, 10, 10, 10, 10, 10, 10
};
struct test test3 = {
- "test3", // name
- "test incremental values stored at exactly second boundaries",
- 1, // update_every
- 1, // multiplier
- 1, // divisor
- RRDDIM_INCREMENTAL, // algorithm
- 10, // feed entries
- 9, // result entries
- test3_feed, // feed
- test3_results // results
+ "test3", // name
+ "test incremental values stored at exactly second boundaries",
+ 1, // update_every
+ 1, // multiplier
+ 1, // divisor
+ RRDDIM_INCREMENTAL, // algorithm
+ 10, // feed entries
+ 9, // result entries
+ test3_feed, // feed
+ test3_results, // results
+ NULL, // feed2
+ NULL // results2
};
// --------------------------------------------------------------------------------------------------------------------
// test4
struct feed_values test4_feed[] = {
- { 500000, 10 },
- { 1000000, 20 },
- { 1000000, 30 },
- { 1000000, 40 },
- { 1000000, 50 },
- { 1000000, 60 },
- { 1000000, 70 },
- { 1000000, 80 },
- { 1000000, 90 },
- { 1000000, 100 },
+ { 500000, 10 },
+ { 1000000, 20 },
+ { 1000000, 30 },
+ { 1000000, 40 },
+ { 1000000, 50 },
+ { 1000000, 60 },
+ { 1000000, 70 },
+ { 1000000, 80 },
+ { 1000000, 90 },
+ { 1000000, 100 },
};
calculated_number test4_results[] = {
- 5, 10, 10, 10, 10, 10, 10, 10, 10
+ 5, 10, 10, 10, 10, 10, 10, 10, 10
};
struct test test4 = {
- "test4", // name
- "test incremental values stored in the middle of second boundaries",
- 1, // update_every
- 1, // multiplier
- 1, // divisor
- RRDDIM_INCREMENTAL, // algorithm
- 10, // feed entries
- 9, // result entries
- test4_feed, // feed
- test4_results // results
+ "test4", // name
+ "test incremental values stored in the middle of second boundaries",
+ 1, // update_every
+ 1, // multiplier
+ 1, // divisor
+ RRDDIM_INCREMENTAL, // algorithm
+ 10, // feed entries
+ 9, // result entries
+ test4_feed, // feed
+ test4_results, // results
+ NULL, // feed2
+ NULL // results2
};
// --------------------------------------------------------------------------------------------------------------------
// test5
struct feed_values test5_feed[] = {
- { 500000, 1000 },
- { 1000000, 2000 },
- { 1000000, 2000 },
- { 1000000, 2000 },
- { 1000000, 3000 },
- { 1000000, 2000 },
- { 1000000, 2000 },
- { 1000000, 2000 },
- { 1000000, 2000 },
- { 1000000, 2000 },
+ { 500000, 1000 },
+ { 1000000, 2000 },
+ { 1000000, 2000 },
+ { 1000000, 2000 },
+ { 1000000, 3000 },
+ { 1000000, 2000 },
+ { 1000000, 2000 },
+ { 1000000, 2000 },
+ { 1000000, 2000 },
+ { 1000000, 2000 },
};
calculated_number test5_results[] = {
- 500, 500, 0, 500, 500, 0, 0, 0, 0
+ 500, 500, 0, 500, 500, 0, 0, 0, 0
};
struct test test5 = {
- "test5", // name
- "test incremental values ups and downs",
- 1, // update_every
- 1, // multiplier
- 1, // divisor
- RRDDIM_INCREMENTAL, // algorithm
- 10, // feed entries
- 9, // result entries
- test5_feed, // feed
- test5_results // results
+ "test5", // name
+ "test incremental values ups and downs",
+ 1, // update_every
+ 1, // multiplier
+ 1, // divisor
+ RRDDIM_INCREMENTAL, // algorithm
+ 10, // feed entries
+ 9, // result entries
+ test5_feed, // feed
+ test5_results, // results
+ NULL, // feed2
+ NULL // results2
};
// --------------------------------------------------------------------------------------------------------------------
// test6
struct feed_values test6_feed[] = {
- { 250000, 1000 },
- { 250000, 2000 },
- { 250000, 3000 },
- { 250000, 4000 },
- { 250000, 5000 },
- { 250000, 6000 },
- { 250000, 7000 },
- { 250000, 8000 },
- { 250000, 9000 },
- { 250000, 10000 },
- { 250000, 11000 },
- { 250000, 12000 },
- { 250000, 13000 },
- { 250000, 14000 },
- { 250000, 15000 },
- { 250000, 16000 },
+ { 250000, 1000 },
+ { 250000, 2000 },
+ { 250000, 3000 },
+ { 250000, 4000 },
+ { 250000, 5000 },
+ { 250000, 6000 },
+ { 250000, 7000 },
+ { 250000, 8000 },
+ { 250000, 9000 },
+ { 250000, 10000 },
+ { 250000, 11000 },
+ { 250000, 12000 },
+ { 250000, 13000 },
+ { 250000, 14000 },
+ { 250000, 15000 },
+ { 250000, 16000 },
};
calculated_number test6_results[] = {
- 3000, 4000, 4000, 4000
+ 3000, 4000, 4000, 4000
};
struct test test6 = {
- "test6", // name
- "test incremental values updated within the same second",
- 1, // update_every
- 1, // multiplier
- 1, // divisor
- RRDDIM_INCREMENTAL, // algorithm
- 16, // feed entries
- 4, // result entries
- test6_feed, // feed
- test6_results // results
+ "test6", // name
+ "test incremental values updated within the same second",
+ 1, // update_every
+ 1, // multiplier
+ 1, // divisor
+ RRDDIM_INCREMENTAL, // algorithm
+ 16, // feed entries
+ 4, // result entries
+ test6_feed, // feed
+ test6_results, // results
+ NULL, // feed2
+ NULL // results2
};
// --------------------------------------------------------------------------------------------------------------------
// test7
struct feed_values test7_feed[] = {
- { 500000, 1000 },
- { 2000000, 2000 },
- { 2000000, 3000 },
- { 2000000, 4000 },
- { 2000000, 5000 },
- { 2000000, 6000 },
- { 2000000, 7000 },
- { 2000000, 8000 },
- { 2000000, 9000 },
- { 2000000, 10000 },
+ { 500000, 1000 },
+ { 2000000, 2000 },
+ { 2000000, 3000 },
+ { 2000000, 4000 },
+ { 2000000, 5000 },
+ { 2000000, 6000 },
+ { 2000000, 7000 },
+ { 2000000, 8000 },
+ { 2000000, 9000 },
+ { 2000000, 10000 },
};
calculated_number test7_results[] = {
- 250, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500
+ 250, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500
};
struct test test7 = {
- "test7", // name
- "test incremental values updated in long durations",
- 1, // update_every
- 1, // multiplier
- 1, // divisor
- RRDDIM_INCREMENTAL, // algorithm
- 10, // feed entries
- 18, // result entries
- test7_feed, // feed
- test7_results // results
+ "test7", // name
+ "test incremental values updated in long durations",
+ 1, // update_every
+ 1, // multiplier
+ 1, // divisor
+ RRDDIM_INCREMENTAL, // algorithm
+ 10, // feed entries
+ 18, // result entries
+ test7_feed, // feed
+ test7_results, // results
+ NULL, // feed2
+ NULL // results2
};
// --------------------------------------------------------------------------------------------------------------------
// test8
struct feed_values test8_feed[] = {
- { 500000, 1000 },
- { 2000000, 2000 },
- { 2000000, 3000 },
- { 2000000, 4000 },
- { 2000000, 5000 },
- { 2000000, 6000 },
+ { 500000, 1000 },
+ { 2000000, 2000 },
+ { 2000000, 3000 },
+ { 2000000, 4000 },
+ { 2000000, 5000 },
+ { 2000000, 6000 },
};
calculated_number test8_results[] = {
- 1250, 2000, 2250, 3000, 3250, 4000, 4250, 5000, 5250, 6000
+ 1250, 2000, 2250, 3000, 3250, 4000, 4250, 5000, 5250, 6000
};
struct test test8 = {
- "test8", // name
- "test absolute values updated in long durations",
- 1, // update_every
- 1, // multiplier
- 1, // divisor
- RRDDIM_ABSOLUTE, // algorithm
- 6, // feed entries
- 10, // result entries
- test8_feed, // feed
- test8_results // results
+ "test8", // name
+ "test absolute values updated in long durations",
+ 1, // update_every
+ 1, // multiplier
+ 1, // divisor
+ RRDDIM_ABSOLUTE, // algorithm
+ 6, // feed entries
+ 10, // result entries
+ test8_feed, // feed
+ test8_results, // results
+ NULL, // feed2
+ NULL // results2
};
// --------------------------------------------------------------------------------------------------------------------
// test9
struct feed_values test9_feed[] = {
- { 250000, 1000 },
- { 250000, 2000 },
- { 250000, 3000 },
- { 250000, 4000 },
- { 250000, 5000 },
- { 250000, 6000 },
- { 250000, 7000 },
- { 250000, 8000 },
- { 250000, 9000 },
- { 250000, 10000 },
- { 250000, 11000 },
- { 250000, 12000 },
- { 250000, 13000 },
- { 250000, 14000 },
- { 250000, 15000 },
- { 250000, 16000 },
+ { 250000, 1000 },
+ { 250000, 2000 },
+ { 250000, 3000 },
+ { 250000, 4000 },
+ { 250000, 5000 },
+ { 250000, 6000 },
+ { 250000, 7000 },
+ { 250000, 8000 },
+ { 250000, 9000 },
+ { 250000, 10000 },
+ { 250000, 11000 },
+ { 250000, 12000 },
+ { 250000, 13000 },
+ { 250000, 14000 },
+ { 250000, 15000 },
+ { 250000, 16000 },
};
calculated_number test9_results[] = {
- 4000, 8000, 12000, 16000
+ 4000, 8000, 12000, 16000
};
struct test test9 = {
- "test9", // name
- "test absolute values updated within the same second",
- 1, // update_every
- 1, // multiplier
- 1, // divisor
- RRDDIM_ABSOLUTE, // algorithm
- 16, // feed entries
- 4, // result entries
- test9_feed, // feed
- test9_results // results
+ "test9", // name
+ "test absolute values updated within the same second",
+ 1, // update_every
+ 1, // multiplier
+ 1, // divisor
+ RRDDIM_ABSOLUTE, // algorithm
+ 16, // feed entries
+ 4, // result entries
+ test9_feed, // feed
+ test9_results, // results
+ NULL, // feed2
+ NULL // results2
};
// --------------------------------------------------------------------------------------------------------------------
// test10
struct feed_values test10_feed[] = {
- { 500000, 1000 },
- { 600000, 1000 + 600 },
- { 200000, 1600 + 200 },
- { 1000000, 1800 + 1000 },
- { 200000, 2800 + 200 },
- { 2000000, 3000 + 2000 },
- { 600000, 5000 + 600 },
- { 400000, 5600 + 400 },
- { 900000, 6000 + 900 },
- { 1000000, 6900 + 1000 },
+ { 500000, 1000 },
+ { 600000, 1000 + 600 },
+ { 200000, 1600 + 200 },
+ { 1000000, 1800 + 1000 },
+ { 200000, 2800 + 200 },
+ { 2000000, 3000 + 2000 },
+ { 600000, 5000 + 600 },
+ { 400000, 5600 + 400 },
+ { 900000, 6000 + 900 },
+ { 1000000, 6900 + 1000 },
};
calculated_number test10_results[] = {
- 500, 1000, 1000, 1000, 1000, 1000, 1000
+ 500, 1000, 1000, 1000, 1000, 1000, 1000
};
struct test test10 = {
- "test10", // name
- "test incremental values updated in short and long durations",
- 1, // update_every
- 1, // multiplier
- 1, // divisor
- RRDDIM_INCREMENTAL, // algorithm
- 10, // feed entries
- 7, // result entries
- test10_feed, // feed
- test10_results // results
+ "test10", // name
+ "test incremental values updated in short and long durations",
+ 1, // update_every
+ 1, // multiplier
+ 1, // divisor
+ RRDDIM_INCREMENTAL, // algorithm
+ 10, // feed entries
+ 7, // result entries
+ test10_feed, // feed
+ test10_results, // results
+ NULL, // feed2
+ NULL // results2
+};
+
+// --------------------------------------------------------------------------------------------------------------------
+// test11
+
+struct feed_values test11_feed[] = {
+ { 0, 10 },
+ { 1000000, 20 },
+ { 1000000, 30 },
+ { 1000000, 40 },
+ { 1000000, 50 },
+ { 1000000, 60 },
+ { 1000000, 70 },
+ { 1000000, 80 },
+ { 1000000, 90 },
+ { 1000000, 100 },
+};
+
+collected_number test11_feed2[] = {
+ 10, 20, 30, 40, 50, 60, 70, 80, 90, 100
+};
+
+calculated_number test11_results[] = {
+ 50, 50, 50, 50, 50, 50, 50, 50, 50
+};
+
+calculated_number test11_results2[] = {
+ 50, 50, 50, 50, 50, 50, 50, 50, 50
+};
+
+struct test test11 = {
+ "test11", // name
+ "test percentage-of-incremental-row with equal values",
+ 1, // update_every
+ 1, // multiplier
+ 1, // divisor
+ RRDDIM_PCENT_OVER_DIFF_TOTAL, // algorithm
+ 10, // feed entries
+ 9, // result entries
+ test11_feed, // feed
+ test11_results, // results
+ test11_feed2, // feed2
+ test11_results2 // results2
+};
+
+// --------------------------------------------------------------------------------------------------------------------
+// test12
+
+struct feed_values test12_feed[] = {
+ { 0, 10 },
+ { 1000000, 20 },
+ { 1000000, 30 },
+ { 1000000, 40 },
+ { 1000000, 50 },
+ { 1000000, 60 },
+ { 1000000, 70 },
+ { 1000000, 80 },
+ { 1000000, 90 },
+ { 1000000, 100 },
+};
+
+collected_number test12_feed2[] = {
+ 10*3, 20*3, 30*3, 40*3, 50*3, 60*3, 70*3, 80*3, 90*3, 100*3
+};
+
+calculated_number test12_results[] = {
+ 25, 25, 25, 25, 25, 25, 25, 25, 25
+};
+
+calculated_number test12_results2[] = {
+ 75, 75, 75, 75, 75, 75, 75, 75, 75
+};
+
+struct test test12 = {
+ "test12", // name
+ "test percentage-of-incremental-row with equal values",
+ 1, // update_every
+ 1, // multiplier
+ 1, // divisor
+ RRDDIM_PCENT_OVER_DIFF_TOTAL, // algorithm
+ 10, // feed entries
+ 9, // result entries
+ test12_feed, // feed
+ test12_results, // results
+ test12_feed2, // feed2
+ test12_results2 // results2
+};
+
+// --------------------------------------------------------------------------------------------------------------------
+// test13
+
+struct feed_values test13_feed[] = {
+ { 500000, 1000 },
+ { 600000, 1000 + 600 },
+ { 200000, 1600 + 200 },
+ { 1000000, 1800 + 1000 },
+ { 200000, 2800 + 200 },
+ { 2000000, 3000 + 2000 },
+ { 600000, 5000 + 600 },
+ { 400000, 5600 + 400 },
+ { 900000, 6000 + 900 },
+ { 1000000, 6900 + 1000 },
+};
+
+calculated_number test13_results[] = {
+ 83.3333300, 100, 100, 100, 100, 100, 100
+};
+
+struct test test13 = {
+ "test13", // name
+ "test incremental values updated in short and long durations",
+ 1, // update_every
+ 1, // multiplier
+ 1, // divisor
+ RRDDIM_PCENT_OVER_DIFF_TOTAL, // algorithm
+ 10, // feed entries
+ 7, // result entries
+ test13_feed, // feed
+ test13_results, // results
+ NULL, // feed2
+ NULL // results2
};
// --------------------------------------------------------------------------------------------------------------------
int run_test(struct test *test)
{
- fprintf(stderr, "\nRunning test '%s':\n%s\n", test->name, test->description);
-
- rrd_memory_mode = RRD_MEMORY_MODE_RAM;
- rrd_update_every = test->update_every;
-
- char name[101];
- snprintfz(name, 100, "unittest-%s", test->name);
-
- // create the chart
- RRDSET *st = rrdset_create("netdata", name, name, "netdata", NULL, "Unit Testing", "a value", 1, 1, RRDSET_TYPE_LINE);
- RRDDIM *rd = rrddim_add(st, "dimension", NULL, test->multiplier, test->divisor, test->algorithm);
- st->debug = 1;
-
- // feed it with the test data
- unsigned long c;
- for(c = 0; c < test->feed_entries; c++) {
- if(debug_flags) fprintf(stderr, "\n\n");
-
- if(c) {
- fprintf(stderr, " > %s: feeding position %lu, after %llu microseconds, with value " CALCULATED_NUMBER_FORMAT "\n", test->name, c+1, test->feed[c].microseconds, test->feed[c].value);
- rrdset_next_usec(st, test->feed[c].microseconds);
- }
- else {
- fprintf(stderr, " > %s: feeding position %lu with value " CALCULATED_NUMBER_FORMAT "\n", test->name, c+1, test->feed[c].value);
- }
-
- rrddim_set(st, "dimension", test->feed[c].value);
- rrdset_done(st);
-
- // align the first entry to second boundary
- if(!c) {
- fprintf(stderr, " > %s: fixing first collection time to be %llu microseconds to second boundary\n", test->name, test->feed[c].microseconds);
- rd->last_collected_time.tv_usec = st->last_collected_time.tv_usec = st->last_updated.tv_usec = test->feed[c].microseconds;
- }
- }
-
- // check the result
- int errors = 0;
-
- if(st->counter != test->result_entries) {
- fprintf(stderr, " %s stored %lu entries, but we were expecting %lu, ### E R R O R ###\n", test->name, st->counter, test->result_entries);
- errors++;
- }
-
- unsigned long max = (st->counter < test->result_entries)?st->counter:test->result_entries;
- for(c = 0 ; c < max ; c++) {
- calculated_number v = unpack_storage_number(rd->values[c]), n = test->results[c];
- fprintf(stderr, " %s: checking position %lu, expecting value " CALCULATED_NUMBER_FORMAT ", found " CALCULATED_NUMBER_FORMAT ", %s\n", test->name, c+1, n, v, (v == n)?"OK":"### E R R O R ###");
- if(v != n) errors++;
- }
-
- return errors;
+ fprintf(stderr, "\nRunning test '%s':\n%s\n", test->name, test->description);
+
+ rrd_memory_mode = RRD_MEMORY_MODE_RAM;
+ rrd_update_every = test->update_every;
+
+ char name[101];
+ snprintfz(name, 100, "unittest-%s", test->name);
+
+ // create the chart
+ RRDSET *st = rrdset_create("netdata", name, name, "netdata", NULL, "Unit Testing", "a value", 1, 1, RRDSET_TYPE_LINE);
+ RRDDIM *rd = rrddim_add(st, "dim1", NULL, test->multiplier, test->divisor, test->algorithm);
+
+ RRDDIM *rd2 = NULL;
+ if(test->feed2)
+ rd2 = rrddim_add(st, "dim2", NULL, test->multiplier, test->divisor, test->algorithm);
+
+ st->debug = 1;
+
+ // feed it with the test data
+ unsigned long c;
+ for(c = 0; c < test->feed_entries; c++) {
+ if(debug_flags) fprintf(stderr, "\n\n");
+
+ if(c) {
+ fprintf(stderr, " > %s: feeding position %lu, after %llu microseconds\n", test->name, c+1, test->feed[c].microseconds);
+ rrdset_next_usec(st, test->feed[c].microseconds);
+ }
+ else {
+ fprintf(stderr, " > %s: feeding position %lu\n", test->name, c+1);
+ }
+
+ fprintf(stderr, " >> %s with value " COLLECTED_NUMBER_FORMAT "\n", rd->name, test->feed[c].value);
+ rrddim_set(st, "dim1", test->feed[c].value);
+
+ if(rd2) {
+ fprintf(stderr, " >> %s with value " COLLECTED_NUMBER_FORMAT "\n", rd2->name, test->feed2[c]);
+ rrddim_set(st, "dim2", test->feed2[c]);
+ }
+
+ rrdset_done(st);
+
+ // align the first entry to second boundary
+ if(!c) {
+ fprintf(stderr, " > %s: fixing first collection time to be %llu microseconds to second boundary\n", test->name, test->feed[c].microseconds);
+ rd->last_collected_time.tv_usec = st->last_collected_time.tv_usec = st->last_updated.tv_usec = test->feed[c].microseconds;
+ }
+ }
+
+ // check the result
+ int errors = 0;
+
+ if(st->counter != test->result_entries) {
+ fprintf(stderr, " %s stored %lu entries, but we were expecting %lu, ### E R R O R ###\n", test->name, st->counter, test->result_entries);
+ errors++;
+ }
+
+ unsigned long max = (st->counter < test->result_entries)?st->counter:test->result_entries;
+ for(c = 0 ; c < max ; c++) {
+ calculated_number v = unpack_storage_number(rd->values[c]);
+ calculated_number n = test->results[c];
+ int same = (roundl(v * 10000000.0) == roundl(n * 10000000.0))?1:0;
+ fprintf(stderr, " %s/%s: checking position %lu, expecting value " CALCULATED_NUMBER_FORMAT ", found " CALCULATED_NUMBER_FORMAT ", %s\n", test->name, rd->name, c+1, n, v, (same)?"OK":"### E R R O R ###");
+ if(!same) errors++;
+
+ if(rd2) {
+ v = unpack_storage_number(rd2->values[c]);
+ n = test->results2[c];
+ same = (roundl(v * 10000000.0) == roundl(n * 10000000.0))?1:0;
+ fprintf(stderr, " %s/%s: checking position %lu, expecting value " CALCULATED_NUMBER_FORMAT ", found " CALCULATED_NUMBER_FORMAT ", %s\n", test->name, rd2->name, c+1, n, v, (same)?"OK":"### E R R O R ###");
+ if(!same) errors++;
+ }
+ }
+
+ return errors;
}
int run_all_mockup_tests(void)
{
- if(run_test(&test1))
- return 1;
+ if(run_test(&test1))
+ return 1;
+
+ if(run_test(&test2))
+ return 1;
+
+ if(run_test(&test3))
+ return 1;
+
+ if(run_test(&test4))
+ return 1;
- if(run_test(&test2))
- return 1;
+ if(run_test(&test5))
+ return 1;
- if(run_test(&test3))
- return 1;
+ if(run_test(&test6))
+ return 1;
- if(run_test(&test4))
- return 1;
+ if(run_test(&test7))
+ return 1;
- if(run_test(&test5))
- return 1;
+ if(run_test(&test8))
+ return 1;
- if(run_test(&test6))
- return 1;
+ if(run_test(&test9))
+ return 1;
- if(run_test(&test7))
- return 1;
+ if(run_test(&test10))
+ return 1;
- if(run_test(&test8))
- return 1;
+ if(run_test(&test11))
+ return 1;
- if(run_test(&test9))
- return 1;
+ if(run_test(&test12))
+ return 1;
- if(run_test(&test10))
- return 1;
+ if(run_test(&test13))
+ return 1;
- return 0;
+ return 0;
}
int unit_test(long delay, long shift)
{
- static int repeat = 0;
- repeat++;
-
- char name[101];
- snprintfz(name, 100, "unittest-%d-%ld-%ld", repeat, delay, shift);
-
- //debug_flags = 0xffffffff;
- rrd_memory_mode = RRD_MEMORY_MODE_RAM;
- rrd_update_every = 1;
-
- int do_abs = 1;
- int do_inc = 1;
- int do_abst = 0;
- int do_absi = 0;
-
- RRDSET *st = rrdset_create("netdata", name, name, "netdata", NULL, "Unit Testing", "a value", 1, 1, RRDSET_TYPE_LINE);
- st->debug = 1;
-
- RRDDIM *rdabs = NULL;
- RRDDIM *rdinc = NULL;
- RRDDIM *rdabst = NULL;
- RRDDIM *rdabsi = NULL;
-
- if(do_abs) rdabs = rrddim_add(st, "absolute", "absolute", 1, 1, RRDDIM_ABSOLUTE);
- if(do_inc) rdinc = rrddim_add(st, "incremental", "incremental", 1, 1, RRDDIM_INCREMENTAL);
- if(do_abst) rdabst = rrddim_add(st, "percentage-of-absolute-row", "percentage-of-absolute-row", 1, 1, RRDDIM_PCENT_OVER_ROW_TOTAL);
- if(do_absi) rdabsi = rrddim_add(st, "percentage-of-incremental-row", "percentage-of-incremental-row", 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-
- long increment = 1000;
- collected_number i = 0;
-
- unsigned long c, dimensions = 0;
- RRDDIM *rd;
- for(rd = st->dimensions ; rd ; rd = rd->next) dimensions++;
-
- for(c = 0; c < 20 ;c++) {
- i += increment;
-
- fprintf(stderr, "\n\nLOOP = %lu, DELAY = %ld, VALUE = " COLLECTED_NUMBER_FORMAT "\n", c, delay, i);
- if(c) {
- rrdset_next_usec(st, delay);
- }
- if(do_abs) rrddim_set(st, "absolute", i);
- if(do_inc) rrddim_set(st, "incremental", i);
- if(do_abst) rrddim_set(st, "percentage-of-absolute-row", i);
- if(do_absi) rrddim_set(st, "percentage-of-incremental-row", i);
-
- if(!c) {
- gettimeofday(&st->last_collected_time, NULL);
- st->last_collected_time.tv_usec = shift;
- }
-
- // prevent it from deleting the dimensions
- for(rd = st->dimensions ; rd ; rd = rd->next)
- rd->last_collected_time.tv_sec = st->last_collected_time.tv_sec;
-
- rrdset_done(st);
- }
-
- unsigned long oincrement = increment;
- increment = increment * st->update_every * 1000000 / delay;
- fprintf(stderr, "\n\nORIGINAL INCREMENT: %lu, INCREMENT %lu, DELAY %lu, SHIFT %lu\n", oincrement * 10, increment * 10, delay, shift);
-
- int ret = 0;
- storage_number sn;
- calculated_number cn, v;
- for(c = 0 ; c < st->counter ; c++) {
- fprintf(stderr, "\nPOSITION: c = %lu, EXPECTED VALUE %lu\n", c, (oincrement + c * increment + increment * (1000000 - shift) / 1000000 )* 10);
-
- for(rd = st->dimensions ; rd ; rd = rd->next) {
- sn = rd->values[c];
- cn = unpack_storage_number(sn);
- fprintf(stderr, "\t %s " CALCULATED_NUMBER_FORMAT " (PACKED AS " STORAGE_NUMBER_FORMAT ") -> ", rd->id, cn, sn);
-
- if(rd == rdabs) v =
- ( oincrement
- // + (increment * (1000000 - shift) / 1000000)
- + (c + 1) * increment
- );
-
- else if(rd == rdinc) v = (c?(increment):(increment * (1000000 - shift) / 1000000));
- else if(rd == rdabst) v = oincrement / dimensions / 10;
- else if(rd == rdabsi) v = oincrement / dimensions / 10;
- else v = 0;
-
- if(v == cn) fprintf(stderr, "passed.\n");
- else {
- fprintf(stderr, "ERROR! (expected " CALCULATED_NUMBER_FORMAT ")\n", v);
- ret = 1;
- }
- }
- }
-
- if(ret)
- fprintf(stderr, "\n\nUNIT TEST(%ld, %ld) FAILED\n\n", delay, shift);
-
- return ret;
+ static int repeat = 0;
+ repeat++;
+
+ char name[101];
+ snprintfz(name, 100, "unittest-%d-%ld-%ld", repeat, delay, shift);
+
+ //debug_flags = 0xffffffff;
+ rrd_memory_mode = RRD_MEMORY_MODE_RAM;
+ rrd_update_every = 1;
+
+ int do_abs = 1;
+ int do_inc = 1;
+ int do_abst = 0;
+ int do_absi = 0;
+
+ RRDSET *st = rrdset_create("netdata", name, name, "netdata", NULL, "Unit Testing", "a value", 1, 1, RRDSET_TYPE_LINE);
+ st->debug = 1;
+
+ RRDDIM *rdabs = NULL;
+ RRDDIM *rdinc = NULL;
+ RRDDIM *rdabst = NULL;
+ RRDDIM *rdabsi = NULL;
+
+ if(do_abs) rdabs = rrddim_add(st, "absolute", "absolute", 1, 1, RRDDIM_ABSOLUTE);
+ if(do_inc) rdinc = rrddim_add(st, "incremental", "incremental", 1, 1, RRDDIM_INCREMENTAL);
+ if(do_abst) rdabst = rrddim_add(st, "percentage-of-absolute-row", "percentage-of-absolute-row", 1, 1, RRDDIM_PCENT_OVER_ROW_TOTAL);
+ if(do_absi) rdabsi = rrddim_add(st, "percentage-of-incremental-row", "percentage-of-incremental-row", 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
+
+ long increment = 1000;
+ collected_number i = 0;
+
+ unsigned long c, dimensions = 0;
+ RRDDIM *rd;
+ for(rd = st->dimensions ; rd ; rd = rd->next) dimensions++;
+
+ for(c = 0; c < 20 ;c++) {
+ i += increment;
+
+ fprintf(stderr, "\n\nLOOP = %lu, DELAY = %ld, VALUE = " COLLECTED_NUMBER_FORMAT "\n", c, delay, i);
+ if(c) {
+ rrdset_next_usec(st, delay);
+ }
+ if(do_abs) rrddim_set(st, "absolute", i);
+ if(do_inc) rrddim_set(st, "incremental", i);
+ if(do_abst) rrddim_set(st, "percentage-of-absolute-row", i);
+ if(do_absi) rrddim_set(st, "percentage-of-incremental-row", i);
+
+ if(!c) {
+ gettimeofday(&st->last_collected_time, NULL);
+ st->last_collected_time.tv_usec = shift;
+ }
+
+ // prevent it from deleting the dimensions
+ for(rd = st->dimensions ; rd ; rd = rd->next)
+ rd->last_collected_time.tv_sec = st->last_collected_time.tv_sec;
+
+ rrdset_done(st);
+ }
+
+ unsigned long oincrement = increment;
+ increment = increment * st->update_every * 1000000 / delay;
+ fprintf(stderr, "\n\nORIGINAL INCREMENT: %lu, INCREMENT %ld, DELAY %ld, SHIFT %ld\n", oincrement * 10, increment * 10, delay, shift);
+
+ int ret = 0;
+ storage_number sn;
+ calculated_number cn, v;
+ for(c = 0 ; c < st->counter ; c++) {
+ fprintf(stderr, "\nPOSITION: c = %lu, EXPECTED VALUE %lu\n", c, (oincrement + c * increment + increment * (1000000 - shift) / 1000000 )* 10);
+
+ for(rd = st->dimensions ; rd ; rd = rd->next) {
+ sn = rd->values[c];
+ cn = unpack_storage_number(sn);
+ fprintf(stderr, "\t %s " CALCULATED_NUMBER_FORMAT " (PACKED AS " STORAGE_NUMBER_FORMAT ") -> ", rd->id, cn, sn);
+
+ if(rd == rdabs) v =
+ ( oincrement
+ // + (increment * (1000000 - shift) / 1000000)
+ + (c + 1) * increment
+ );
+
+ else if(rd == rdinc) v = (c?(increment):(increment * (1000000 - shift) / 1000000));
+ else if(rd == rdabst) v = oincrement / dimensions / 10;
+ else if(rd == rdabsi) v = oincrement / dimensions / 10;
+ else v = 0;
+
+ if(v == cn) fprintf(stderr, "passed.\n");
+ else {
+ fprintf(stderr, "ERROR! (expected " CALCULATED_NUMBER_FORMAT ")\n", v);
+ ret = 1;
+ }
+ }
+ }
+
+ if(ret)
+ fprintf(stderr, "\n\nUNIT TEST(%ld, %ld) FAILED\n\n", delay, shift);
+
+ return ret;
}
diff --git a/src/url.c b/src/url.c
index 010b07ddd..6be4d9648 100644
--- a/src/url.c
+++ b/src/url.c
@@ -1,13 +1,4 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-
#include "common.h"
-#include "log.h"
-#include "url.h"
// ----------------------------------------------------------------------------
// URL encode / decode
@@ -15,79 +6,72 @@
/* Converts a hex character to its integer value */
char from_hex(char ch) {
- return (char)(isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10);
+ return (char)(isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10);
}
/* Converts an integer value to its hex character*/
char to_hex(char code) {
- static char hex[] = "0123456789abcdef";
- return hex[code & 15];
+ static char hex[] = "0123456789abcdef";
+ return hex[code & 15];
}
/* Returns a url-encoded version of str */
/* IMPORTANT: be sure to free() the returned string after use */
char *url_encode(char *str) {
- char *buf, *pbuf;
-
- pbuf = buf = malloc(strlen(str) * 3 + 1);
-
- if(!buf)
- fatal("Cannot allocate memory.");
+ char *buf, *pbuf;
- while (*str) {
- if (isalnum(*str) || *str == '-' || *str == '_' || *str == '.' || *str == '~')
- *pbuf++ = *str;
+ pbuf = buf = mallocz(strlen(str) * 3 + 1);
- else if (*str == ' ')
- *pbuf++ = '+';
+ while (*str) {
+ if (isalnum(*str) || *str == '-' || *str == '_' || *str == '.' || *str == '~')
+ *pbuf++ = *str;
- else
- *pbuf++ = '%', *pbuf++ = to_hex(*str >> 4), *pbuf++ = to_hex(*str & 15);
+ else if (*str == ' ')
+ *pbuf++ = '+';
- str++;
- }
- *pbuf = '\0';
+ else
+ *pbuf++ = '%', *pbuf++ = to_hex(*str >> 4), *pbuf++ = to_hex(*str & 15);
- // FIX: I think this is prudent. URLs can be as long as 2 KiB or more.
- // We allocated 3 times more space to accomodate %NN encoding of
- // non ASCII chars. If URL has none of these kind of chars we will
- // end up with a big unused buffer.
- //
- // Try to shrink the buffer...
- if (!!(pbuf = (char *)realloc(buf, strlen(buf)+1)))
- buf = pbuf;
+ str++;
+ }
+ *pbuf = '\0';
- return buf;
+ pbuf = strdupz(buf);
+ freez(buf);
+ return pbuf;
}
/* Returns a url-decoded version of str */
/* IMPORTANT: be sure to free() the returned string after use */
char *url_decode(char *str) {
- char *pstr = str,
- *buf = malloc(strlen(str) + 1),
- *pbuf = buf;
+ size_t size = strlen(str) + 1;
- if(!buf)
- fatal("Cannot allocate memory.");
+ char *buf = mallocz(size);
+ return url_decode_r(buf, str, size);
+}
- while (*pstr) {
- if (*pstr == '%') {
- if (pstr[1] && pstr[2]) {
- *pbuf++ = from_hex(pstr[1]) << 4 | from_hex(pstr[2]);
- pstr += 2;
- }
- }
- else if (*pstr == '+')
- *pbuf++ = ' ';
+char *url_decode_r(char *to, char *url, size_t size) {
+ char *s = url, // source
+ *d = to, // destination
+ *e = &to[size - 1]; // destination end
- else
- *pbuf++ = *pstr;
+ while(*s && d < e) {
+ if(unlikely(*s == '%')) {
+ if(likely(s[1] && s[2])) {
+ *d++ = from_hex(s[1]) << 4 | from_hex(s[2]);
+ s += 2;
+ }
+ }
+ else if(unlikely(*s == '+'))
+ *d++ = ' ';
- pstr++;
- }
+ else
+ *d++ = *s;
- *pbuf = '\0';
+ s++;
+ }
- return buf;
-}
+ *d = '\0';
+ return to;
+}
diff --git a/src/url.h b/src/url.h
index f79a20ea0..fa44d49a8 100644
--- a/src/url.h
+++ b/src/url.h
@@ -19,4 +19,6 @@ extern char *url_encode(char *str);
/* IMPORTANT: be sure to free() the returned string after use */
extern char *url_decode(char *str);
+extern char *url_decode_r(char *to, char *url, size_t size);
+
#endif /* NETDATA_URL_H */
diff --git a/src/web_buffer.c b/src/web_buffer.c
index a0f153721..01a97ddc9 100644
--- a/src/web_buffer.c
+++ b/src/web_buffer.c
@@ -1,23 +1,11 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdlib.h>
-#include <string.h>
-
-#ifdef STORAGE_WITH_MATH
-#include <math.h>
-#endif
-
#include "common.h"
-#include "web_buffer.h"
-#include "log.h"
#define BUFFER_OVERFLOW_EOF "EOF"
static inline void buffer_overflow_init(BUFFER *b)
{
- b->buffer[b->size] = '\0';
- strcpy(&b->buffer[b->size + 1], BUFFER_OVERFLOW_EOF);
+ b->buffer[b->size] = '\0';
+ strcpy(&b->buffer[b->size + 1], BUFFER_OVERFLOW_EOF);
}
#ifdef NETDATA_INTERNAL_CHECKS
@@ -28,174 +16,224 @@ static inline void buffer_overflow_init(BUFFER *b)
static inline void _buffer_overflow_check(BUFFER *b, const char *file, const char *function, const unsigned long line)
{
- if(b->len > b->size) {
- error("BUFFER: length %ld is above size %ld, at line %lu, at function %s() of file '%s'.", b->len, b->size, line, function, file);
- b->len = b->size;
- }
-
- if(b->buffer[b->size] != '\0' || strcmp(&b->buffer[b->size + 1], BUFFER_OVERFLOW_EOF)) {
- error("BUFFER: detected overflow at line %lu, at function %s() of file '%s'.", line, function, file);
- buffer_overflow_init(b);
- }
+ if(b->len > b->size) {
+ error("BUFFER: length %zu is above size %zu, at line %lu, at function %s() of file '%s'.", b->len, b->size, line, function, file);
+ b->len = b->size;
+ }
+
+ if(b->buffer[b->size] != '\0' || strcmp(&b->buffer[b->size + 1], BUFFER_OVERFLOW_EOF)) {
+ error("BUFFER: detected overflow at line %lu, at function %s() of file '%s'.", line, function, file);
+ buffer_overflow_init(b);
+ }
}
void buffer_reset(BUFFER *wb)
{
- buffer_flush(wb);
+ buffer_flush(wb);
- wb->contenttype = CT_TEXT_PLAIN;
- wb->options = 0;
- wb->date = 0;
+ wb->contenttype = CT_TEXT_PLAIN;
+ wb->options = 0;
+ wb->date = 0;
- buffer_overflow_check(wb);
+ buffer_overflow_check(wb);
}
const char *buffer_tostring(BUFFER *wb)
{
- buffer_need_bytes(wb, 1);
- wb->buffer[wb->len] = '\0';
+ buffer_need_bytes(wb, 1);
+ wb->buffer[wb->len] = '\0';
- buffer_overflow_check(wb);
+ buffer_overflow_check(wb);
- return(wb->buffer);
+ return(wb->buffer);
}
void buffer_char_replace(BUFFER *wb, char from, char to)
{
- char *s = wb->buffer, *end = &wb->buffer[wb->len];
+ char *s = wb->buffer, *end = &wb->buffer[wb->len];
- while(s != end) {
- if(*s == from) *s = to;
- s++;
- }
+ while(s != end) {
+ if(*s == from) *s = to;
+ s++;
+ }
- buffer_overflow_check(wb);
+ buffer_overflow_check(wb);
}
+// This trick seems to give an 80% speed increase in 32bit systems
+// print_calculated_number_llu_r() will just print the digits up to the
+// point the remaining value fits in 32 bits, and then calls
+// print_calculated_number_lu_r() to print the rest with 32 bit arithmetic.
+
+inline char *print_number_lu_r(char *str, unsigned long uvalue) {
+ char *wstr = str;
+
+ // print each digit
+ do *wstr++ = (char)('0' + (uvalue % 10)); while(uvalue /= 10);
+ return wstr;
+}
+
+inline char *print_number_llu_r(char *str, unsigned long long uvalue) {
+ char *wstr = str;
+
+ // print each digit
+ do *wstr++ = (char)('0' + (uvalue % 10)); while((uvalue /= 10) && uvalue > (unsigned long long)0xffffffff);
+ if(uvalue) return print_number_lu_r(wstr, uvalue);
+ return wstr;
+}
+
+void buffer_print_llu(BUFFER *wb, unsigned long long uvalue)
+{
+ buffer_need_bytes(wb, 50);
+
+ char *str = &wb->buffer[wb->len];
+ char *wstr = str;
+
+#ifdef ENVIRONMENT32
+ if(uvalue > (unsigned long long)0xffffffff)
+ wstr = print_number_llu_r(wstr, uvalue);
+ else
+ wstr = print_number_lu_r(wstr, uvalue);
+#else
+ do *wstr++ = (char)('0' + (uvalue % 10)); while(uvalue /= 10);
+#endif
+
+ // terminate it
+ *wstr = '\0';
+
+ // reverse it
+ char *begin = str, *end = wstr - 1, aux;
+ while (end > begin) aux = *end, *end-- = *begin, *begin++ = aux;
+
+ // return the buffer length
+ wb->len += wstr - str;
+}
void buffer_strcat(BUFFER *wb, const char *txt)
{
- if(unlikely(!txt || !*txt)) return;
-
- buffer_need_bytes(wb, 1);
-
- char *s = &wb->buffer[wb->len], *start, *end = &wb->buffer[wb->size];
- long len = wb->len;
-
- start = s;
- while(*txt && s != end)
- *s++ = *txt++;
-
- len += s - start;
-
- wb->len = len;
- buffer_overflow_check(wb);
-
- if(*txt) {
- debug(D_WEB_BUFFER, "strcat(): increasing web_buffer at position %ld, size = %ld\n", wb->len, wb->size);
- len = strlen(txt);
- buffer_increase(wb, len);
- buffer_strcat(wb, txt);
- }
- else {
- // terminate the string
- // without increasing the length
- buffer_need_bytes(wb, (size_t)1);
- wb->buffer[wb->len] = '\0';
- }
+ if(unlikely(!txt || !*txt)) return;
+
+ buffer_need_bytes(wb, 1);
+
+ char *s = &wb->buffer[wb->len], *start, *end = &wb->buffer[wb->size];
+ size_t len = wb->len;
+
+ start = s;
+ while(*txt && s != end)
+ *s++ = *txt++;
+
+ len += s - start;
+
+ wb->len = len;
+ buffer_overflow_check(wb);
+
+ if(*txt) {
+ debug(D_WEB_BUFFER, "strcat(): increasing web_buffer at position %zu, size = %zu\n", wb->len, wb->size);
+ len = strlen(txt);
+ buffer_increase(wb, len);
+ buffer_strcat(wb, txt);
+ }
+ else {
+ // terminate the string
+ // without increasing the length
+ buffer_need_bytes(wb, (size_t)1);
+ wb->buffer[wb->len] = '\0';
+ }
}
void buffer_snprintf(BUFFER *wb, size_t len, const char *fmt, ...)
{
- if(unlikely(!fmt || !*fmt)) return;
+ if(unlikely(!fmt || !*fmt)) return;
- buffer_need_bytes(wb, len + 1);
+ buffer_need_bytes(wb, len + 1);
- va_list args;
- va_start(args, fmt);
- wb->len += vsnprintfz(&wb->buffer[wb->len], len, fmt, args);
- va_end(args);
+ va_list args;
+ va_start(args, fmt);
+ wb->len += vsnprintfz(&wb->buffer[wb->len], len, fmt, args);
+ va_end(args);
- buffer_overflow_check(wb);
+ buffer_overflow_check(wb);
- // the buffer is \0 terminated by vsnprintfz
+ // the buffer is \0 terminated by vsnprintfz
}
void buffer_vsprintf(BUFFER *wb, const char *fmt, va_list args)
{
- if(unlikely(!fmt || !*fmt)) return;
+ if(unlikely(!fmt || !*fmt)) return;
- buffer_need_bytes(wb, 2);
+ buffer_need_bytes(wb, 2);
- size_t len = wb->size - wb->len - 1;
+ size_t len = wb->size - wb->len - 1;
- wb->len += vsnprintfz(&wb->buffer[wb->len], len, fmt, args);
+ wb->len += vsnprintfz(&wb->buffer[wb->len], len, fmt, args);
- buffer_overflow_check(wb);
+ buffer_overflow_check(wb);
- // the buffer is \0 terminated by vsnprintfz
+ // the buffer is \0 terminated by vsnprintfz
}
void buffer_sprintf(BUFFER *wb, const char *fmt, ...)
{
- if(unlikely(!fmt || !*fmt)) return;
+ if(unlikely(!fmt || !*fmt)) return;
- buffer_need_bytes(wb, 2);
+ buffer_need_bytes(wb, 2);
- size_t len = wb->size - wb->len - 1;
- size_t wrote;
+ size_t len = wb->size - wb->len - 1;
+ size_t wrote;
- va_list args;
- va_start(args, fmt);
- wrote = (size_t) vsnprintfz(&wb->buffer[wb->len], len, fmt, args);
- va_end(args);
+ va_list args;
+ va_start(args, fmt);
+ wrote = (size_t) vsnprintfz(&wb->buffer[wb->len], len, fmt, args);
+ va_end(args);
- if(unlikely(wrote >= len)) {
- // there is bug in vsnprintf() and it returns
- // a number higher to len, but it does not
- // overflow the buffer.
- // our buffer overflow detector will log it
- // if it does.
- buffer_overflow_check(wb);
+ if(unlikely(wrote >= len)) {
+ // truncated
+ buffer_overflow_check(wb);
- debug(D_WEB_BUFFER, "web_buffer_sprintf(): increasing web_buffer at position %ld, size = %ld\n", wb->len, wb->size);
- buffer_need_bytes(wb, len + WEB_DATA_LENGTH_INCREASE_STEP);
+ debug(D_WEB_BUFFER, "web_buffer_sprintf(): increasing web_buffer at position %zu, size = %zu\n", wb->len, wb->size);
+ buffer_need_bytes(wb, len + WEB_DATA_LENGTH_INCREASE_STEP);
- va_start(args, fmt);
- buffer_vsprintf(wb, fmt, args);
- va_end(args);
- }
- else
- wb->len += wrote;
+ va_start(args, fmt);
+ buffer_vsprintf(wb, fmt, args);
+ va_end(args);
+ }
+ else
+ wb->len += wrote;
- // the buffer is \0 terminated by vsnprintf
+ // the buffer is \0 terminated by vsnprintf
}
void buffer_rrd_value(BUFFER *wb, calculated_number value)
{
- buffer_need_bytes(wb, 50);
- wb->len += print_calculated_number(&wb->buffer[wb->len], value);
+ buffer_need_bytes(wb, 50);
+
+ if(isnan(value) || isinf(value)) {
+ buffer_strcat(wb, "null");
+ return;
+ }
+ else
+ wb->len += print_calculated_number(&wb->buffer[wb->len], value);
- // terminate it
- buffer_need_bytes(wb, 1);
- wb->buffer[wb->len] = '\0';
+ // terminate it
+ buffer_need_bytes(wb, 1);
+ wb->buffer[wb->len] = '\0';
- buffer_overflow_check(wb);
+ buffer_overflow_check(wb);
}
// generate a javascript date, the fastest possible way...
void buffer_jsdate(BUFFER *wb, int year, int month, int day, int hours, int minutes, int seconds)
{
// 10 20 30 = 35
- // 01234567890123456789012345678901234
- // Date(2014,04,01,03,28,20)
+ // 01234567890123456789012345678901234
+ // Date(2014,04,01,03,28,20)
- buffer_need_bytes(wb, 30);
+ buffer_need_bytes(wb, 30);
- char *b = &wb->buffer[wb->len], *p;
+ char *b = &wb->buffer[wb->len], *p;
unsigned int *q = (unsigned int *)b;
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
@@ -234,23 +272,23 @@ void buffer_jsdate(BUFFER *wb, int year, int month, int day, int hours, int minu
*r++ = 0x2900; // ")\0"
#endif
- wb->len += (size_t)((char *)r - b - 1);
+ wb->len += (size_t)((char *)r - b - 1);
- // terminate it
- wb->buffer[wb->len] = '\0';
- buffer_overflow_check(wb);
+ // terminate it
+ wb->buffer[wb->len] = '\0';
+ buffer_overflow_check(wb);
}
// generate a date, the fastest possible way...
void buffer_date(BUFFER *wb, int year, int month, int day, int hours, int minutes, int seconds)
{
- // 10 20 30 = 35
- // 01234567890123456789012345678901234
- // 2014-04-01 03:28:20
+ // 10 20 30 = 35
+ // 01234567890123456789012345678901234
+ // 2014-04-01 03:28:20
- buffer_need_bytes(wb, 36);
+ buffer_need_bytes(wb, 36);
- char *b = &wb->buffer[wb->len];
+ char *b = &wb->buffer[wb->len];
char *p = b;
*p++ = '0' + year / 1000; year %= 1000;
@@ -274,68 +312,56 @@ void buffer_date(BUFFER *wb, int year, int month, int day, int hours, int minute
*p++ = '0' + seconds % 10;
*p = '\0';
- wb->len += (size_t)(p - b);
+ wb->len += (size_t)(p - b);
- // terminate it
- wb->buffer[wb->len] = '\0';
- buffer_overflow_check(wb);
+ // terminate it
+ wb->buffer[wb->len] = '\0';
+ buffer_overflow_check(wb);
}
-BUFFER *buffer_create(long size)
+BUFFER *buffer_create(size_t size)
{
- BUFFER *b;
-
- debug(D_WEB_BUFFER, "Creating new web buffer of size %d.", size);
-
- b = calloc(1, sizeof(BUFFER));
- if(!b) {
- error("Cannot allocate a web_buffer.");
- return NULL;
- }
-
- b->buffer = malloc(size + sizeof(BUFFER_OVERFLOW_EOF) + 2);
- if(!b->buffer) {
- error("Cannot allocate a buffer of size %u.", size + sizeof(BUFFER_OVERFLOW_EOF) + 2);
- free(b);
- return NULL;
- }
- b->buffer[0] = '\0';
- b->size = size;
- b->contenttype = CT_TEXT_PLAIN;
- buffer_overflow_init(b);
- buffer_overflow_check(b);
-
- return(b);
+ BUFFER *b;
+
+ debug(D_WEB_BUFFER, "Creating new web buffer of size %zu.", size);
+
+ b = callocz(1, sizeof(BUFFER));
+ b->buffer = mallocz(size + sizeof(BUFFER_OVERFLOW_EOF) + 2);
+ b->buffer[0] = '\0';
+ b->size = size;
+ b->contenttype = CT_TEXT_PLAIN;
+ buffer_overflow_init(b);
+ buffer_overflow_check(b);
+
+ return(b);
}
void buffer_free(BUFFER *b)
{
- buffer_overflow_check(b);
+ buffer_overflow_check(b);
- debug(D_WEB_BUFFER, "Freeing web buffer of size %d.", b->size);
+ debug(D_WEB_BUFFER, "Freeing web buffer of size %zu.", b->size);
- if(b->buffer) free(b->buffer);
- free(b);
+ freez(b->buffer);
+ freez(b);
}
void buffer_increase(BUFFER *b, size_t free_size_required)
{
- buffer_overflow_check(b);
-
- size_t left = b->size - b->len;
+ buffer_overflow_check(b);
- if(left >= free_size_required) return;
+ size_t left = b->size - b->len;
- size_t increase = free_size_required - left;
- if(increase < WEB_DATA_LENGTH_INCREASE_STEP) increase = WEB_DATA_LENGTH_INCREASE_STEP;
+ if(left >= free_size_required) return;
- debug(D_WEB_BUFFER, "Increasing data buffer from size %d to %d.", b->size, b->size + increase);
+ size_t increase = free_size_required - left;
+ if(increase < WEB_DATA_LENGTH_INCREASE_STEP) increase = WEB_DATA_LENGTH_INCREASE_STEP;
- b->buffer = realloc(b->buffer, b->size + increase + sizeof(BUFFER_OVERFLOW_EOF) + 2);
- if(!b->buffer) fatal("Failed to increase data buffer from size %d to %d.", b->size + sizeof(BUFFER_OVERFLOW_EOF) + 2, b->size + increase + sizeof(BUFFER_OVERFLOW_EOF) + 2);
+ debug(D_WEB_BUFFER, "Increasing data buffer from size %zu to %zu.", b->size, b->size + increase);
- b->size += increase;
+ b->buffer = reallocz(b->buffer, b->size + increase + sizeof(BUFFER_OVERFLOW_EOF) + 2);
+ b->size += increase;
- buffer_overflow_init(b);
- buffer_overflow_check(b);
+ buffer_overflow_init(b);
+ buffer_overflow_check(b);
}
diff --git a/src/web_buffer.h b/src/web_buffer.h
index 73533f499..c4cd05632 100644
--- a/src/web_buffer.h
+++ b/src/web_buffer.h
@@ -1,47 +1,43 @@
-#include <stdarg.h>
-#include <time.h>
-#include "storage_number.h"
-
#ifndef NETDATA_WEB_BUFFER_H
#define NETDATA_WEB_BUFFER_H 1
-#define WEB_DATA_LENGTH_INCREASE_STEP 16384
+#define WEB_DATA_LENGTH_INCREASE_STEP 1024
typedef struct web_buffer {
- size_t size; // allocation size of buffer
- size_t len; // current data length in buffer
- char *buffer; // the buffer
- uint8_t contenttype;
- uint8_t options;
- time_t date; // the date this content has been generated
+ size_t size; // allocation size of buffer
+ size_t len; // current data length in buffer
+ char *buffer; // the buffer
+ uint8_t contenttype;
+ uint8_t options;
+ time_t date; // the date this content has been generated
} BUFFER;
// options
-#define WB_CONTENT_CACHEABLE 1
-#define WB_CONTENT_NO_CACHEABLE 2
+#define WB_CONTENT_CACHEABLE 1
+#define WB_CONTENT_NO_CACHEABLE 2
// content-types
-#define CT_APPLICATION_JSON 1
-#define CT_TEXT_PLAIN 2
-#define CT_TEXT_HTML 3
-#define CT_APPLICATION_X_JAVASCRIPT 4
-#define CT_TEXT_CSS 5
-#define CT_TEXT_XML 6
-#define CT_APPLICATION_XML 7
-#define CT_TEXT_XSL 8
-#define CT_APPLICATION_OCTET_STREAM 9
-#define CT_APPLICATION_X_FONT_TRUETYPE 10
-#define CT_APPLICATION_X_FONT_OPENTYPE 11
-#define CT_APPLICATION_FONT_WOFF 12
-#define CT_APPLICATION_FONT_WOFF2 13
-#define CT_APPLICATION_VND_MS_FONTOBJ 14
-#define CT_IMAGE_SVG_XML 15
-#define CT_IMAGE_PNG 16
-#define CT_IMAGE_JPG 17
-#define CT_IMAGE_GIF 18
-#define CT_IMAGE_XICON 19
-#define CT_IMAGE_ICNS 20
-#define CT_IMAGE_BMP 21
+#define CT_APPLICATION_JSON 1
+#define CT_TEXT_PLAIN 2
+#define CT_TEXT_HTML 3
+#define CT_APPLICATION_X_JAVASCRIPT 4
+#define CT_TEXT_CSS 5
+#define CT_TEXT_XML 6
+#define CT_APPLICATION_XML 7
+#define CT_TEXT_XSL 8
+#define CT_APPLICATION_OCTET_STREAM 9
+#define CT_APPLICATION_X_FONT_TRUETYPE 10
+#define CT_APPLICATION_X_FONT_OPENTYPE 11
+#define CT_APPLICATION_FONT_WOFF 12
+#define CT_APPLICATION_FONT_WOFF2 13
+#define CT_APPLICATION_VND_MS_FONTOBJ 14
+#define CT_IMAGE_SVG_XML 15
+#define CT_IMAGE_PNG 16
+#define CT_IMAGE_JPG 17
+#define CT_IMAGE_GIF 18
+#define CT_IMAGE_XICON 19
+#define CT_IMAGE_ICNS 20
+#define CT_IMAGE_BMP 21
#define buffer_strlen(wb) ((wb)->len)
extern const char *buffer_tostring(BUFFER *wb);
@@ -57,14 +53,19 @@ extern void buffer_rrd_value(BUFFER *wb, calculated_number value);
extern void buffer_date(BUFFER *wb, int year, int month, int day, int hours, int minutes, int seconds);
extern void buffer_jsdate(BUFFER *wb, int year, int month, int day, int hours, int minutes, int seconds);
-extern BUFFER *buffer_create(long size);
+extern BUFFER *buffer_create(size_t size);
extern void buffer_free(BUFFER *b);
extern void buffer_increase(BUFFER *b, size_t free_size_required);
-extern void buffer_snprintf(BUFFER *wb, size_t len, const char *fmt, ...);
+extern void buffer_snprintf(BUFFER *wb, size_t len, const char *fmt, ...) __attribute__ (( format (printf, 3, 4)));
extern void buffer_vsprintf(BUFFER *wb, const char *fmt, va_list args);
-extern void buffer_sprintf(BUFFER *wb, const char *fmt, ...);
+extern void buffer_sprintf(BUFFER *wb, const char *fmt, ...) __attribute__ (( format (printf, 2, 3)));
extern void buffer_char_replace(BUFFER *wb, char from, char to);
+extern char *print_number_lu_r(char *str, unsigned long uvalue);
+extern char *print_number_llu_r(char *str, unsigned long long uvalue);
+
+extern void buffer_print_llu(BUFFER *wb, unsigned long long uvalue);
+
#endif /* NETDATA_WEB_BUFFER_H */
diff --git a/src/web_buffer_svg.c b/src/web_buffer_svg.c
new file mode 100644
index 000000000..2f7627cc5
--- /dev/null
+++ b/src/web_buffer_svg.c
@@ -0,0 +1,616 @@
+#include "common.h"
+
+#define BADGE_HORIZONTAL_PADDING 4
+#define VERDANA_KERNING 0.2
+#define VERDANA_PADDING 1.0
+
+/*
+ * verdana11_widths[] has been generated with this method:
+ * https://github.com/badges/shields/blob/master/measure-text.js
+*/
+
+double verdana11_widths[256] = {
+ [0] = 0.0,
+ [1] = 0.0,
+ [2] = 0.0,
+ [3] = 0.0,
+ [4] = 0.0,
+ [5] = 0.0,
+ [6] = 0.0,
+ [7] = 0.0,
+ [8] = 0.0,
+ [9] = 0.0,
+ [10] = 0.0,
+ [11] = 0.0,
+ [12] = 0.0,
+ [13] = 0.0,
+ [14] = 0.0,
+ [15] = 0.0,
+ [16] = 0.0,
+ [17] = 0.0,
+ [18] = 0.0,
+ [19] = 0.0,
+ [20] = 0.0,
+ [21] = 0.0,
+ [22] = 0.0,
+ [23] = 0.0,
+ [24] = 0.0,
+ [25] = 0.0,
+ [26] = 0.0,
+ [27] = 0.0,
+ [28] = 0.0,
+ [29] = 0.0,
+ [30] = 0.0,
+ [31] = 0.0,
+ [32] = 3.8671874999999996, //
+ [33] = 4.3291015625, // !
+ [34] = 5.048828125, // "
+ [35] = 9.001953125, // #
+ [36] = 6.9931640625, // $
+ [37] = 11.837890625, // %
+ [38] = 7.992187499999999, // &
+ [39] = 2.9541015625, // '
+ [40] = 4.9951171875, // (
+ [41] = 4.9951171875, // )
+ [42] = 6.9931640625, // *
+ [43] = 9.001953125, // +
+ [44] = 4.00146484375, // ,
+ [45] = 4.9951171875, // -
+ [46] = 4.00146484375, // .
+ [47] = 4.9951171875, // /
+ [48] = 6.9931640625, // 0
+ [49] = 6.9931640625, // 1
+ [50] = 6.9931640625, // 2
+ [51] = 6.9931640625, // 3
+ [52] = 6.9931640625, // 4
+ [53] = 6.9931640625, // 5
+ [54] = 6.9931640625, // 6
+ [55] = 6.9931640625, // 7
+ [56] = 6.9931640625, // 8
+ [57] = 6.9931640625, // 9
+ [58] = 4.9951171875, // :
+ [59] = 4.9951171875, // ;
+ [60] = 9.001953125, // <
+ [61] = 9.001953125, // =
+ [62] = 9.001953125, // >
+ [63] = 5.99951171875, // ?
+ [64] = 11.0, // @
+ [65] = 7.51953125, // A
+ [66] = 7.541015625, // B
+ [67] = 7.680664062499999, // C
+ [68] = 8.4755859375, // D
+ [69] = 6.95556640625, // E
+ [70] = 6.32177734375, // F
+ [71] = 8.529296875, // G
+ [72] = 8.26611328125, // H
+ [73] = 4.6298828125, // I
+ [74] = 5.00048828125, // J
+ [75] = 7.62158203125, // K
+ [76] = 6.123046875, // L
+ [77] = 9.2705078125, // M
+ [78] = 8.228515625, // N
+ [79] = 8.658203125, // O
+ [80] = 6.63330078125, // P
+ [81] = 8.658203125, // Q
+ [82] = 7.6484375, // R
+ [83] = 7.51953125, // S
+ [84] = 6.7783203125, // T
+ [85] = 8.05126953125, // U
+ [86] = 7.51953125, // V
+ [87] = 10.87646484375, // W
+ [88] = 7.53564453125, // X
+ [89] = 6.767578125, // Y
+ [90] = 7.53564453125, // Z
+ [91] = 4.9951171875, // [
+ [92] = 4.9951171875, // backslash
+ [93] = 4.9951171875, // ]
+ [94] = 9.001953125, // ^
+ [95] = 6.9931640625, // _
+ [96] = 6.9931640625, // `
+ [97] = 6.6064453125, // a
+ [98] = 6.853515625, // b
+ [99] = 5.73095703125, // c
+ [100] = 6.853515625, // d
+ [101] = 6.552734375, // e
+ [102] = 3.8671874999999996, // f
+ [103] = 6.853515625, // g
+ [104] = 6.9609375, // h
+ [105] = 3.0185546875, // i
+ [106] = 3.78662109375, // j
+ [107] = 6.509765625, // k
+ [108] = 3.0185546875, // l
+ [109] = 10.69921875, // m
+ [110] = 6.9609375, // n
+ [111] = 6.67626953125, // o
+ [112] = 6.853515625, // p
+ [113] = 6.853515625, // q
+ [114] = 4.6943359375, // r
+ [115] = 5.73095703125, // s
+ [116] = 4.33447265625, // t
+ [117] = 6.9609375, // u
+ [118] = 6.509765625, // v
+ [119] = 9.001953125, // w
+ [120] = 6.509765625, // x
+ [121] = 6.509765625, // y
+ [122] = 5.779296875, // z
+ [123] = 6.982421875, // {
+ [124] = 4.9951171875, // |
+ [125] = 6.982421875, // }
+ [126] = 9.001953125, // ~
+ [127] = 0.0,
+ [128] = 0.0,
+ [129] = 0.0,
+ [130] = 0.0,
+ [131] = 0.0,
+ [132] = 0.0,
+ [133] = 0.0,
+ [134] = 0.0,
+ [135] = 0.0,
+ [136] = 0.0,
+ [137] = 0.0,
+ [138] = 0.0,
+ [139] = 0.0,
+ [140] = 0.0,
+ [141] = 0.0,
+ [142] = 0.0,
+ [143] = 0.0,
+ [144] = 0.0,
+ [145] = 0.0,
+ [146] = 0.0,
+ [147] = 0.0,
+ [148] = 0.0,
+ [149] = 0.0,
+ [150] = 0.0,
+ [151] = 0.0,
+ [152] = 0.0,
+ [153] = 0.0,
+ [154] = 0.0,
+ [155] = 0.0,
+ [156] = 0.0,
+ [157] = 0.0,
+ [158] = 0.0,
+ [159] = 0.0,
+ [160] = 0.0,
+ [161] = 0.0,
+ [162] = 0.0,
+ [163] = 0.0,
+ [164] = 0.0,
+ [165] = 0.0,
+ [166] = 0.0,
+ [167] = 0.0,
+ [168] = 0.0,
+ [169] = 0.0,
+ [170] = 0.0,
+ [171] = 0.0,
+ [172] = 0.0,
+ [173] = 0.0,
+ [174] = 0.0,
+ [175] = 0.0,
+ [176] = 0.0,
+ [177] = 0.0,
+ [178] = 0.0,
+ [179] = 0.0,
+ [180] = 0.0,
+ [181] = 0.0,
+ [182] = 0.0,
+ [183] = 0.0,
+ [184] = 0.0,
+ [185] = 0.0,
+ [186] = 0.0,
+ [187] = 0.0,
+ [188] = 0.0,
+ [189] = 0.0,
+ [190] = 0.0,
+ [191] = 0.0,
+ [192] = 0.0,
+ [193] = 0.0,
+ [194] = 0.0,
+ [195] = 0.0,
+ [196] = 0.0,
+ [197] = 0.0,
+ [198] = 0.0,
+ [199] = 0.0,
+ [200] = 0.0,
+ [201] = 0.0,
+ [202] = 0.0,
+ [203] = 0.0,
+ [204] = 0.0,
+ [205] = 0.0,
+ [206] = 0.0,
+ [207] = 0.0,
+ [208] = 0.0,
+ [209] = 0.0,
+ [210] = 0.0,
+ [211] = 0.0,
+ [212] = 0.0,
+ [213] = 0.0,
+ [214] = 0.0,
+ [215] = 0.0,
+ [216] = 0.0,
+ [217] = 0.0,
+ [218] = 0.0,
+ [219] = 0.0,
+ [220] = 0.0,
+ [221] = 0.0,
+ [222] = 0.0,
+ [223] = 0.0,
+ [224] = 0.0,
+ [225] = 0.0,
+ [226] = 0.0,
+ [227] = 0.0,
+ [228] = 0.0,
+ [229] = 0.0,
+ [230] = 0.0,
+ [231] = 0.0,
+ [232] = 0.0,
+ [233] = 0.0,
+ [234] = 0.0,
+ [235] = 0.0,
+ [236] = 0.0,
+ [237] = 0.0,
+ [238] = 0.0,
+ [239] = 0.0,
+ [240] = 0.0,
+ [241] = 0.0,
+ [242] = 0.0,
+ [243] = 0.0,
+ [244] = 0.0,
+ [245] = 0.0,
+ [246] = 0.0,
+ [247] = 0.0,
+ [248] = 0.0,
+ [249] = 0.0,
+ [250] = 0.0,
+ [251] = 0.0,
+ [252] = 0.0,
+ [253] = 0.0,
+ [254] = 0.0,
+ [255] = 0.0
+};
+
+// find the width of the string using the verdana 11points font
+// re-write the string in place, skiping zero-length characters
+static inline int verdana11_width(char *s) {
+ double w = 0.0;
+ char *d = s;
+
+ while(*s) {
+ double t = verdana11_widths[(unsigned char)*s];
+ if(t == 0.0)
+ s++;
+ else {
+ w += t + VERDANA_KERNING;
+ if(d != s)
+ *d++ = *s++;
+ else
+ d = ++s;
+ }
+ }
+
+ *d = '\0';
+ w -= VERDANA_KERNING;
+ w += VERDANA_PADDING;
+ return ceil(w);
+}
+
+static inline size_t escape_xmlz(char *dst, const char *src, size_t len) {
+ size_t i = len;
+
+ // required escapes from
+ // https://github.com/badges/shields/blob/master/badge.js
+ while(*src && i) {
+ switch(*src) {
+ case '\\':
+ *dst++ = '/';
+ src++;
+ i--;
+ break;
+
+ case '&':
+ if(i > 5) {
+ strcpy(dst, "&amp;");
+ i -= 5;
+ dst += 5;
+ src++;
+ }
+ else goto cleanup;
+ break;
+
+ case '<':
+ if(i > 4) {
+ strcpy(dst, "&lt;");
+ i -= 4;
+ dst += 4;
+ src++;
+ }
+ else goto cleanup;
+ break;
+
+ case '>':
+ if(i > 4) {
+ strcpy(dst, "&gt;");
+ i -= 4;
+ dst += 4;
+ src++;
+ }
+ else goto cleanup;
+ break;
+
+ case '"':
+ if(i > 6) {
+ strcpy(dst, "&quot;");
+ i -= 6;
+ dst += 6;
+ src++;
+ }
+ else goto cleanup;
+ break;
+
+ case '\'':
+ if(i > 6) {
+ strcpy(dst, "&apos;");
+ i -= 6;
+ dst += 6;
+ src++;
+ }
+ else goto cleanup;
+ break;
+
+ default:
+ i--;
+ *dst++ = *src++;
+ break;
+ }
+ }
+
+cleanup:
+ *dst = '\0';
+ return len - i;
+}
+
+static inline const char *fix_units(const char *units) {
+ if(!units || !*units || !strcmp(units, "empty") || !strcmp(units, "null")) return "";
+ if(!strcmp(units, "percentage") || !strcmp(units, "percent") || !strcmp(units, "pcent")) return "%";
+ return units;
+}
+
+static inline const char *color_map(const char *color) {
+ // colors from:
+ // https://github.com/badges/shields/blob/master/colorscheme.json
+ if(!strcmp(color, "brightgreen")) return "#4c1";
+ else if(!strcmp(color, "green")) return "#97CA00";
+ else if(!strcmp(color, "yellow")) return "#dfb317";
+ else if(!strcmp(color, "yellowgreen")) return "#a4a61d";
+ else if(!strcmp(color, "orange")) return "#fe7d37";
+ else if(!strcmp(color, "red")) return "#e05d44";
+ else if(!strcmp(color, "blue")) return "#007ec6";
+ else if(!strcmp(color, "grey")) return "#555";
+ else if(!strcmp(color, "gray")) return "#555";
+ else if(!strcmp(color, "lightgrey")) return "#9f9f9f";
+ else if(!strcmp(color, "lightgray")) return "#9f9f9f";
+ return color;
+}
+
+static inline void calc_colorz(const char *color, char *final, size_t len, calculated_number value, int value_is_null) {
+ char color_buffer[256 + 1] = "";
+ char value_buffer[256 + 1] = "";
+ char comparison = '>';
+
+ // example input:
+ // color<max|color>min|color:null...
+
+ const char *c = color;
+ while(*c) {
+ char *dc = color_buffer, *dv = NULL;
+ size_t ci = 0, vi = 0;
+
+ const char *t = c;
+
+ while(*t && *t != '|') {
+ switch(*t) {
+ case ':':
+ comparison = '=';
+ dv = value_buffer;
+ break;
+
+ case '}':
+ case ')':
+ case '>':
+ if(t[1] == '=') {
+ comparison = ')';
+ t++;
+ }
+ else
+ comparison = '>';
+ dv = value_buffer;
+ break;
+
+ case '{':
+ case '(':
+ case '<':
+ if(t[1] == '=') {
+ comparison = '(';
+ t++;
+ }
+ else
+ comparison = '<';
+ dv = value_buffer;
+ break;
+
+ default:
+ if(dv) {
+ if(vi < 256) {
+ vi++;
+ *dv++ = *t;
+ }
+ }
+ else {
+ if(ci < 256) {
+ ci++;
+ *dc++ = *t;
+ }
+ }
+ break;
+ }
+
+ t++;
+ }
+
+ // prepare for next iteration
+ if(*t == '|') t++;
+ c = t;
+
+ // do the math
+ *dc = '\0';
+ if(dv) {
+ *dv = '\0';
+
+ if(value_is_null) {
+ if(!*value_buffer || !strcmp(value_buffer, "null"))
+ break;
+ }
+ else {
+ calculated_number v = strtold(value_buffer, NULL);
+
+ if(comparison == '<' && value < v) break;
+ else if(comparison == '(' && value <= v) break;
+ else if(comparison == '>' && value > v) break;
+ else if(comparison == ')' && value >= v) break;
+ else if(comparison == '=' && value == v) break;
+ }
+ }
+ else
+ break;
+ }
+
+ const char *b;
+ if(color_buffer[0])
+ b = color_buffer;
+ else
+ b = color;
+
+ strncpyz(final, b, len);
+}
+
+// value + units
+#define VALUE_STRING_SIZE 100
+
+// label
+#define LABEL_STRING_SIZE 200
+
+// colors
+#define COLOR_STRING_SIZE 100
+
+void buffer_svg(BUFFER *wb, const char *label, calculated_number value, const char *units, const char *label_color, const char *value_color, int value_is_null, int precision) {
+ char label_buffer[LABEL_STRING_SIZE + 1]
+ , value_color_buffer[COLOR_STRING_SIZE + 1]
+ , value_string[VALUE_STRING_SIZE + 1]
+ , label_escaped[LABEL_STRING_SIZE + 1]
+ , value_escaped[VALUE_STRING_SIZE + 1]
+ , label_color_escaped[COLOR_STRING_SIZE + 1]
+ , value_color_escaped[COLOR_STRING_SIZE + 1];
+
+ int label_width, value_width, total_width;
+
+ if(unlikely(!label_color || !*label_color))
+ label_color = "#555";
+
+ if(unlikely(!value_color || !*value_color))
+ value_color = (value_is_null)?"#999":"#4c1";
+
+ units = fix_units(units);
+ calc_colorz(value_color, value_color_buffer, COLOR_STRING_SIZE, value, value_is_null);
+
+ char *separator = "";
+ if(unlikely(isalnum(*units)))
+ separator = " ";
+
+ if(unlikely(value_is_null))
+ strcpy(value_string, "-");
+
+ else if(precision < 0) {
+ int len, l, lstop = 0;
+
+ calculated_number abs = value;
+ if(isless(value, 0)) {
+ lstop = 1;
+ abs = -value;
+ }
+
+ if(isgreaterequal(abs, 1000)) len = snprintfz(value_string, VALUE_STRING_SIZE, "%0.0Lf", (long double)value);
+ else if(isgreaterequal(abs, 100)) len = snprintfz(value_string, VALUE_STRING_SIZE, "%0.1Lf", (long double)value);
+ else if(isgreaterequal(abs, 1)) len = snprintfz(value_string, VALUE_STRING_SIZE, "%0.2Lf", (long double)value);
+ else if(isgreaterequal(abs, 0.1)) len = snprintfz(value_string, VALUE_STRING_SIZE, "%0.3Lf", (long double)value);
+ else len = snprintfz(value_string, VALUE_STRING_SIZE, "%0.4Lf", (long double)value);
+
+ // remove trailing zeros
+ for(l = len - 1; l > lstop ; l--) {
+ if(likely(value_string[l] == '0')) {
+ value_string[l] = '\0';
+ len--;
+ }
+
+ else if(unlikely(value_string[l] == '.')) {
+ value_string[l] = '\0';
+ len--;
+ break;
+ }
+
+ else
+ break;
+ }
+
+ if(len >= 0)
+ snprintfz(&value_string[len], VALUE_STRING_SIZE - len, "%s%s", separator, units);
+ }
+ else {
+ if(precision > 50) precision = 50;
+ snprintfz(value_string, VALUE_STRING_SIZE, "%0.*Lf%s%s", precision, (long double)value, separator, units);
+ }
+
+ // we need to copy the label, since verdana11_width may write to it
+ strncpyz(label_buffer, label, LABEL_STRING_SIZE);
+
+ label_width = verdana11_width(label_buffer) + (BADGE_HORIZONTAL_PADDING * 2);
+ value_width = verdana11_width(value_string) + (BADGE_HORIZONTAL_PADDING * 2);
+ total_width = label_width + value_width;
+
+ escape_xmlz(label_escaped, label_buffer, LABEL_STRING_SIZE);
+ escape_xmlz(value_escaped, value_string, VALUE_STRING_SIZE);
+ escape_xmlz(label_color_escaped, color_map(label_color), COLOR_STRING_SIZE);
+ escape_xmlz(value_color_escaped, color_map(value_color_buffer), COLOR_STRING_SIZE);
+
+ wb->contenttype = CT_IMAGE_SVG_XML;
+
+ // svg template from:
+ // https://raw.githubusercontent.com/badges/shields/master/templates/flat-template.svg
+ buffer_sprintf(wb,
+ "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"%d\" height=\"20\">"
+ "<linearGradient id=\"smooth\" x2=\"0\" y2=\"100%%\">"
+ "<stop offset=\"0\" stop-color=\"#bbb\" stop-opacity=\".1\"/>"
+ "<stop offset=\"1\" stop-opacity=\".1\"/>"
+ "</linearGradient>"
+ "<mask id=\"round\">"
+ "<rect width=\"%d\" height=\"20\" rx=\"3\" fill=\"#fff\"/>"
+ "</mask>"
+ "<g mask=\"url(#round)\">"
+ "<rect width=\"%d\" height=\"20\" fill=\"%s\"/>"
+ "<rect x=\"%d\" width=\"%d\" height=\"20\" fill=\"%s\"/>"
+ "<rect width=\"%d\" height=\"20\" fill=\"url(#smooth)\"/>"
+ "</g>"
+ "<g fill=\"#fff\" text-anchor=\"middle\" font-family=\"DejaVu Sans,Verdana,Geneva,sans-serif\" font-size=\"11\">"
+ "<text x=\"%d\" y=\"15\" fill=\"#010101\" fill-opacity=\".3\">%s</text>"
+ "<text x=\"%d\" y=\"14\">%s</text>"
+ "<text x=\"%d\" y=\"15\" fill=\"#010101\" fill-opacity=\".3\">%s</text>"
+ "<text x=\"%d\" y=\"14\">%s</text>"
+ "</g>"
+ "</svg>",
+ total_width, total_width,
+ label_width, label_color_escaped,
+ label_width, value_width, value_color_escaped,
+ total_width,
+ label_width / 2, label_escaped,
+ label_width / 2, label_escaped,
+ label_width + value_width / 2 -1, value_escaped,
+ label_width + value_width / 2 -1, value_escaped);
+}
diff --git a/src/web_buffer_svg.h b/src/web_buffer_svg.h
new file mode 100644
index 000000000..1281847eb
--- /dev/null
+++ b/src/web_buffer_svg.h
@@ -0,0 +1,6 @@
+#ifndef NETDATA_WEB_BUFFER_SVG_H
+#define NETDATA_WEB_BUFFER_SVG_H 1
+
+extern void buffer_svg(BUFFER *wb, const char *label, calculated_number value, const char *units, const char *label_color, const char *value_color, int value_is_null, int precision);
+
+#endif /* NETDATA_WEB_BUFFER_SVG_H */
diff --git a/src/web_client.c b/src/web_client.c
index 601dda083..4036d4c81 100644
--- a/src/web_client.c
+++ b/src/web_client.c
@@ -1,2032 +1,2537 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <unistd.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <errno.h>
-#include <pthread.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <netinet/tcp.h>
-#include <malloc.h>
-#include <pwd.h>
-#include <grp.h>
-#include <ctype.h>
-
#include "common.h"
-#include "log.h"
-#include "appconfig.h"
-#include "url.h"
-#include "web_buffer.h"
-#include "web_server.h"
-#include "global_statistics.h"
-#include "rrd.h"
-#include "rrd2json.h"
-#include "registry.h"
-
-#include "web_client.h"
-#include "../config.h"
#define INITIAL_WEB_DATA_LENGTH 16384
#define WEB_REQUEST_LENGTH 16384
#define TOO_BIG_REQUEST 16384
int web_client_timeout = DEFAULT_DISCONNECT_IDLE_WEB_CLIENTS_AFTER_SECONDS;
-int web_enable_gzip = 1;
+int web_donotrack_comply = 0;
-extern int netdata_exit;
+#ifdef NETDATA_WITH_ZLIB
+int web_enable_gzip = 1, web_gzip_level = 3, web_gzip_strategy = Z_DEFAULT_STRATEGY;
+#endif /* NETDATA_WITH_ZLIB */
struct web_client *web_clients = NULL;
unsigned long long web_clients_count = 0;
+inline int web_client_crock_socket(struct web_client *w) {
+#ifdef TCP_CORK
+ if(likely(!w->tcp_cork && w->ofd != -1)) {
+ w->tcp_cork = 1;
+ if(unlikely(setsockopt(w->ofd, IPPROTO_TCP, TCP_CORK, (char *) &w->tcp_cork, sizeof(int)) != 0)) {
+ error("%llu: failed to enable TCP_CORK on socket.", w->id);
+ w->tcp_cork = 0;
+ return -1;
+ }
+ }
+#endif /* TCP_CORK */
+
+ return 0;
+}
+
+inline int web_client_uncrock_socket(struct web_client *w) {
+#ifdef TCP_CORK
+ if(likely(w->tcp_cork && w->ofd != -1)) {
+ w->tcp_cork = 0;
+ if(unlikely(setsockopt(w->ofd, IPPROTO_TCP, TCP_CORK, (char *) &w->tcp_cork, sizeof(int)) != 0)) {
+ error("%llu: failed to disable TCP_CORK on socket.", w->id);
+ w->tcp_cork = 1;
+ return -1;
+ }
+ }
+#endif /* TCP_CORK */
+
+ return 0;
+}
+
struct web_client *web_client_create(int listener)
{
- struct web_client *w;
-
- w = calloc(1, sizeof(struct web_client));
- if(!w) {
- error("Cannot allocate new web_client memory.");
- return NULL;
- }
-
- w->id = ++web_clients_count;
- w->mode = WEB_CLIENT_MODE_NORMAL;
-
- {
- struct sockaddr *sadr;
- socklen_t addrlen;
-
- sadr = (struct sockaddr*) &w->clientaddr;
- addrlen = sizeof(w->clientaddr);
-
- w->ifd = accept(listener, sadr, &addrlen);
- if (w->ifd == -1) {
- error("%llu: Cannot accept new incoming connection.", w->id);
- free(w);
- return NULL;
- }
- w->ofd = w->ifd;
-
- if(getnameinfo(sadr, addrlen, w->client_ip, NI_MAXHOST, w->client_port, NI_MAXSERV, NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
- error("Cannot getnameinfo() on received client connection.");
- strncpyz(w->client_ip, "UNKNOWN", NI_MAXHOST);
- strncpyz(w->client_port, "UNKNOWN", NI_MAXSERV);
- }
- w->client_ip[NI_MAXHOST] = '\0';
- w->client_port[NI_MAXSERV] = '\0';
-
- switch(sadr->sa_family) {
-
- case AF_INET:
- debug(D_WEB_CLIENT_ACCESS, "%llu: New IPv4 web client from %s port %s on socket %d.", w->id, w->client_ip, w->client_port, w->ifd);
- break;
-
- case AF_INET6:
- if(strncmp(w->client_ip, "::ffff:", 7) == 0) {
- strcpy(w->client_ip, &w->client_ip[7]);
- debug(D_WEB_CLIENT_ACCESS, "%llu: New IPv4 web client from %s port %s on socket %d.", w->id, w->client_ip, w->client_port, w->ifd);
- }
- debug(D_WEB_CLIENT_ACCESS, "%llu: New IPv6 web client from %s port %s on socket %d.", w->id, w->client_ip, w->client_port, w->ifd);
- break;
-
- default:
- debug(D_WEB_CLIENT_ACCESS, "%llu: New UNKNOWN web client from %s port %s on socket %d.", w->id, w->client_ip, w->client_port, w->ifd);
- break;
- }
-
- int flag = 1;
- if(setsockopt(w->ifd, SOL_SOCKET, SO_KEEPALIVE, (char *) &flag, sizeof(int)) != 0) error("%llu: Cannot set SO_KEEPALIVE on socket.", w->id);
- }
-
- w->response.data = buffer_create(INITIAL_WEB_DATA_LENGTH);
- if(unlikely(!w->response.data)) {
- // no need for error log - web_buffer_create already logged the error
- close(w->ifd);
- free(w);
- return NULL;
- }
-
- w->response.header = buffer_create(HTTP_RESPONSE_HEADER_SIZE);
- if(unlikely(!w->response.header)) {
- // no need for error log - web_buffer_create already logged the error
- buffer_free(w->response.data);
- close(w->ifd);
- free(w);
- return NULL;
- }
-
- w->response.header_output = buffer_create(HTTP_RESPONSE_HEADER_SIZE);
- if(unlikely(!w->response.header_output)) {
- // no need for error log - web_buffer_create already logged the error
- buffer_free(w->response.header);
- buffer_free(w->response.data);
- close(w->ifd);
- free(w);
- return NULL;
- }
-
- w->origin[0] = '*';
- w->wait_receive = 1;
-
- if(web_clients) web_clients->prev = w;
- w->next = web_clients;
- web_clients = w;
-
- global_statistics.connected_clients++;
-
- return(w);
+ struct web_client *w;
+
+ w = callocz(1, sizeof(struct web_client));
+ w->id = ++web_clients_count;
+ w->mode = WEB_CLIENT_MODE_NORMAL;
+
+ {
+ struct sockaddr *sadr;
+ socklen_t addrlen;
+
+ sadr = (struct sockaddr*) &w->clientaddr;
+ addrlen = sizeof(w->clientaddr);
+
+ w->ifd = accept4(listener, sadr, &addrlen, SOCK_NONBLOCK);
+ if (w->ifd == -1) {
+ error("%llu: Cannot accept new incoming connection.", w->id);
+ freez(w);
+ return NULL;
+ }
+ w->ofd = w->ifd;
+
+ if(getnameinfo(sadr, addrlen, w->client_ip, NI_MAXHOST, w->client_port, NI_MAXSERV, NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
+ error("Cannot getnameinfo() on received client connection.");
+ strncpyz(w->client_ip, "UNKNOWN", NI_MAXHOST);
+ strncpyz(w->client_port, "UNKNOWN", NI_MAXSERV);
+ }
+ w->client_ip[NI_MAXHOST] = '\0';
+ w->client_port[NI_MAXSERV] = '\0';
+
+ switch(sadr->sa_family) {
+ case AF_INET:
+ debug(D_WEB_CLIENT_ACCESS, "%llu: New IPv4 web client from %s port %s on socket %d.", w->id, w->client_ip, w->client_port, w->ifd);
+ break;
+
+ case AF_INET6:
+ if(strncmp(w->client_ip, "::ffff:", 7) == 0) {
+ memmove(w->client_ip, &w->client_ip[7], strlen(&w->client_ip[7]) + 1);
+ debug(D_WEB_CLIENT_ACCESS, "%llu: New IPv4 web client from %s port %s on socket %d.", w->id, w->client_ip, w->client_port, w->ifd);
+ }
+ else
+ debug(D_WEB_CLIENT_ACCESS, "%llu: New IPv6 web client from %s port %s on socket %d.", w->id, w->client_ip, w->client_port, w->ifd);
+ break;
+
+ default:
+ debug(D_WEB_CLIENT_ACCESS, "%llu: New UNKNOWN web client from %s port %s on socket %d.", w->id, w->client_ip, w->client_port, w->ifd);
+ break;
+ }
+
+ int flag = 1;
+ if(setsockopt(w->ofd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)) != 0)
+ error("%llu: failed to enable TCP_NODELAY on socket.", w->id);
+
+ flag = 1;
+ if(setsockopt(w->ifd, SOL_SOCKET, SO_KEEPALIVE, (char *) &flag, sizeof(int)) != 0)
+ error("%llu: Cannot set SO_KEEPALIVE on socket.", w->id);
+ }
+
+ w->response.data = buffer_create(INITIAL_WEB_DATA_LENGTH);
+ w->response.header = buffer_create(HTTP_RESPONSE_HEADER_SIZE);
+ w->response.header_output = buffer_create(HTTP_RESPONSE_HEADER_SIZE);
+ w->origin[0] = '*';
+ w->wait_receive = 1;
+
+ if(web_clients) web_clients->prev = w;
+ w->next = web_clients;
+ web_clients = w;
+
+ web_client_connected();
+
+ return(w);
}
-void web_client_reset(struct web_client *w)
-{
- struct timeval tv;
- gettimeofday(&tv, NULL);
+void web_client_reset(struct web_client *w) {
+ web_client_uncrock_socket(w);
+
+ debug(D_WEB_CLIENT, "%llu: Reseting client.", w->id);
- long sent = (w->mode == WEB_CLIENT_MODE_FILECOPY)?w->response.rlen:w->response.data->len;
+ if(likely(w->last_url[0])) {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ size_t size = (w->mode == WEB_CLIENT_MODE_FILECOPY)?w->response.rlen:w->response.data->len;
+ size_t sent = size;
#ifdef NETDATA_WITH_ZLIB
- if(likely(w->response.zoutput)) sent = (long)w->response.zstream.total_out;
+ if(likely(w->response.zoutput)) sent = (size_t)w->response.zstream.total_out;
#endif
- long size = (w->mode == WEB_CLIENT_MODE_FILECOPY)?w->response.rlen:w->response.data->len;
-
- if(likely(w->last_url[0]))
- log_access("%llu: (sent/all = %ld/%ld bytes %0.0f%%, prep/sent/total = %0.2f/%0.2f/%0.2f ms) %s: %d '%s'",
- w->id,
- sent, size, -((size>0)?((float)(size-sent)/(float)size * 100.0):0.0),
- (float)usecdiff(&w->tv_ready, &w->tv_in) / 1000.0,
- (float)usecdiff(&tv, &w->tv_ready) / 1000.0,
- (float)usecdiff(&tv, &w->tv_in) / 1000.0,
- (w->mode == WEB_CLIENT_MODE_FILECOPY)?"filecopy":((w->mode == WEB_CLIENT_MODE_OPTIONS)?"options":"data"),
- w->response.code,
- w->last_url
- );
-
- debug(D_WEB_CLIENT, "%llu: Reseting client.", w->id);
-
- if(unlikely(w->mode == WEB_CLIENT_MODE_FILECOPY)) {
- debug(D_WEB_CLIENT, "%llu: Closing filecopy input file.", w->id);
- close(w->ifd);
- w->ifd = w->ofd;
- }
-
- w->last_url[0] = '\0';
- w->cookie1[0] = '\0';
- w->cookie2[0] = '\0';
- w->origin[0] = '*';
- w->origin[1] = '\0';
-
- w->mode = WEB_CLIENT_MODE_NORMAL;
- w->enable_gzip = 0;
- w->keepalive = 0;
- if(w->decoded_url) {
- free(w->decoded_url);
- w->decoded_url = NULL;
- }
-
- buffer_reset(w->response.header_output);
- buffer_reset(w->response.header);
- buffer_reset(w->response.data);
- w->response.rlen = 0;
- w->response.sent = 0;
- w->response.code = 0;
-
- w->wait_receive = 1;
- w->wait_send = 0;
-
- w->response.zoutput = 0;
-
- // if we had enabled compression, release it
+ // --------------------------------------------------------------------
+ // global statistics
+
+ finished_web_request_statistics(usec_dt(&tv, &w->tv_in),
+ w->stats_received_bytes,
+ w->stats_sent_bytes,
+ size,
+ sent);
+
+ w->stats_received_bytes = 0;
+ w->stats_sent_bytes = 0;
+
+
+ // --------------------------------------------------------------------
+ // access log
+
+ log_access("%llu: (sent/all = %zu/%zu bytes %0.0f%%, prep/sent/total = %0.2f/%0.2f/%0.2f ms) %s: %d '%s'",
+ w->id,
+ sent, size, -((size > 0) ? ((size - sent) / (double) size * 100.0) : 0.0),
+ usec_dt(&w->tv_ready, &w->tv_in) / 1000.0,
+ usec_dt(&tv, &w->tv_ready) / 1000.0,
+ usec_dt(&tv, &w->tv_in) / 1000.0,
+ (w->mode == WEB_CLIENT_MODE_FILECOPY) ? "filecopy" : ((w->mode == WEB_CLIENT_MODE_OPTIONS)
+ ? "options" : "data"),
+ w->response.code,
+ w->last_url
+ );
+ }
+
+ if(unlikely(w->mode == WEB_CLIENT_MODE_FILECOPY)) {
+ if(w->ifd != w->ofd) {
+ debug(D_WEB_CLIENT, "%llu: Closing filecopy input file descriptor %d.", w->id, w->ifd);
+ if(w->ifd != -1) close(w->ifd);
+ w->ifd = w->ofd;
+ }
+ }
+
+ w->last_url[0] = '\0';
+ w->cookie1[0] = '\0';
+ w->cookie2[0] = '\0';
+ w->origin[0] = '*';
+ w->origin[1] = '\0';
+
+ w->mode = WEB_CLIENT_MODE_NORMAL;
+
+ w->tcp_cork = 0;
+ w->donottrack = 0;
+ w->tracking_required = 0;
+ w->keepalive = 0;
+ w->decoded_url[0] = '\0';
+
+ buffer_reset(w->response.header_output);
+ buffer_reset(w->response.header);
+ buffer_reset(w->response.data);
+ w->response.rlen = 0;
+ w->response.sent = 0;
+ w->response.code = 0;
+
+ w->wait_receive = 1;
+ w->wait_send = 0;
+
+ w->response.zoutput = 0;
+
+ // if we had enabled compression, release it
#ifdef NETDATA_WITH_ZLIB
- if(w->response.zinitialized) {
- debug(D_DEFLATE, "%llu: Reseting compression.", w->id);
- deflateEnd(&w->response.zstream);
- w->response.zsent = 0;
- w->response.zhave = 0;
- w->response.zstream.avail_in = 0;
- w->response.zstream.avail_out = 0;
- w->response.zstream.total_in = 0;
- w->response.zstream.total_out = 0;
- w->response.zinitialized = 0;
- }
+ if(w->response.zinitialized) {
+ debug(D_DEFLATE, "%llu: Freeing compression resources.", w->id);
+ deflateEnd(&w->response.zstream);
+ w->response.zsent = 0;
+ w->response.zhave = 0;
+ w->response.zstream.avail_in = 0;
+ w->response.zstream.avail_out = 0;
+ w->response.zstream.total_in = 0;
+ w->response.zstream.total_out = 0;
+ w->response.zinitialized = 0;
+ }
#endif // NETDATA_WITH_ZLIB
}
-struct web_client *web_client_free(struct web_client *w)
-{
- struct web_client *n = w->next;
-
- debug(D_WEB_CLIENT_ACCESS, "%llu: Closing web client from %s port %s.", w->id, w->client_ip, w->client_port);
+struct web_client *web_client_free(struct web_client *w) {
+ web_client_reset(w);
- if(w->prev) w->prev->next = w->next;
- if(w->next) w->next->prev = w->prev;
+ struct web_client *n = w->next;
+ if(w == web_clients) web_clients = n;
- if(w == web_clients) web_clients = w->next;
+ debug(D_WEB_CLIENT_ACCESS, "%llu: Closing web client from %s port %s.", w->id, w->client_ip, w->client_port);
- if(w->response.header_output) buffer_free(w->response.header_output);
- if(w->response.header) buffer_free(w->response.header);
- if(w->response.data) buffer_free(w->response.data);
- close(w->ifd);
- if(w->ofd != w->ifd) close(w->ofd);
- free(w);
+ if(w->prev) w->prev->next = w->next;
+ if(w->next) w->next->prev = w->prev;
+ if(w->response.header_output) buffer_free(w->response.header_output);
+ if(w->response.header) buffer_free(w->response.header);
+ if(w->response.data) buffer_free(w->response.data);
+ if(w->ifd != -1) close(w->ifd);
+ if(w->ofd != -1 && w->ofd != w->ifd) close(w->ofd);
+ freez(w);
- global_statistics.connected_clients--;
+ web_client_disconnected();
- return(n);
+ return(n);
}
-uid_t web_files_uid(void)
-{
- static char *web_owner = NULL;
- static uid_t owner_uid = 0;
-
- if(unlikely(!web_owner)) {
- web_owner = config_get("global", "web files owner", config_get("global", "run as user", ""));
- if(!web_owner || !*web_owner)
- owner_uid = geteuid();
- else {
- // getpwnam() is not thread safe,
- // but we have called this function once
- // while single threaded
- struct passwd *pw = getpwnam(web_owner);
- if(!pw) {
- error("User %s is not present. Ignoring option.", web_owner);
- owner_uid = geteuid();
- }
- else {
- debug(D_WEB_CLIENT, "Web files owner set to %s.\n", web_owner);
- owner_uid = pw->pw_uid;
- }
- }
- }
-
- return(owner_uid);
+uid_t web_files_uid(void) {
+ static char *web_owner = NULL;
+ static uid_t owner_uid = 0;
+
+ if(unlikely(!web_owner)) {
+ web_owner = config_get("global", "web files owner", config_get("global", "run as user", ""));
+ if(!web_owner || !*web_owner)
+ owner_uid = geteuid();
+ else {
+ // getpwnam() is not thread safe,
+ // but we have called this function once
+ // while single threaded
+ struct passwd *pw = getpwnam(web_owner);
+ if(!pw) {
+ error("User '%s' is not present. Ignoring option.", web_owner);
+ owner_uid = geteuid();
+ }
+ else {
+ debug(D_WEB_CLIENT, "Web files owner set to %s.", web_owner);
+ owner_uid = pw->pw_uid;
+ }
+ }
+ }
+
+ return(owner_uid);
}
-gid_t web_files_gid(void)
-{
- static char *web_group = NULL;
- static gid_t owner_gid = 0;
-
- if(unlikely(!web_group)) {
- web_group = config_get("global", "web files group", config_get("global", "web files owner", ""));
- if(!web_group || !*web_group)
- owner_gid = getegid();
- else {
- // getgrnam() is not thread safe,
- // but we have called this function once
- // while single threaded
- struct group *gr = getgrnam(web_group);
- if(!gr) {
- error("Group %s is not present. Ignoring option.", web_group);
- owner_gid = getegid();
- }
- else {
- debug(D_WEB_CLIENT, "Web files group set to %s.\n", web_group);
- owner_gid = gr->gr_gid;
- }
- }
- }
-
- return(owner_gid);
+gid_t web_files_gid(void) {
+ static char *web_group = NULL;
+ static gid_t owner_gid = 0;
+
+ if(unlikely(!web_group)) {
+ web_group = config_get("global", "web files group", config_get("global", "web files owner", ""));
+ if(!web_group || !*web_group)
+ owner_gid = getegid();
+ else {
+ // getgrnam() is not thread safe,
+ // but we have called this function once
+ // while single threaded
+ struct group *gr = getgrnam(web_group);
+ if(!gr) {
+ error("Group '%s' is not present. Ignoring option.", web_group);
+ owner_gid = getegid();
+ }
+ else {
+ debug(D_WEB_CLIENT, "Web files group set to %s.", web_group);
+ owner_gid = gr->gr_gid;
+ }
+ }
+ }
+
+ return(owner_gid);
}
int mysendfile(struct web_client *w, char *filename)
{
- static char *web_dir = NULL;
-
- // initialize our static data
- if(unlikely(!web_dir)) web_dir = config_get("global", "web files directory", WEB_DIR);
-
- debug(D_WEB_CLIENT, "%llu: Looking for file '%s/%s'", w->id, web_dir, filename);
-
- // skip leading slashes
- while (*filename == '/') filename++;
-
- // if the filename contain known paths, skip them
- if(strncmp(filename, WEB_PATH_FILE "/", strlen(WEB_PATH_FILE) + 1) == 0) filename = &filename[strlen(WEB_PATH_FILE) + 1];
-
- char *s;
- for(s = filename; *s ;s++) {
- if( !isalnum(*s) && *s != '/' && *s != '.' && *s != '-' && *s != '_') {
- debug(D_WEB_CLIENT_ACCESS, "%llu: File '%s' is not acceptable.", w->id, filename);
- buffer_sprintf(w->response.data, "File '%s' cannot be served. Filename contains invalid character '%c'", filename, *s);
- return 400;
- }
- }
-
- // if the filename contains a .. refuse to serve it
- if(strstr(filename, "..") != 0) {
- debug(D_WEB_CLIENT_ACCESS, "%llu: File '%s' is not acceptable.", w->id, filename);
- buffer_sprintf(w->response.data, "File '%s' cannot be served. Relative filenames with '..' in them are not supported.", filename);
- return 400;
- }
-
- // access the file
- char webfilename[FILENAME_MAX + 1];
- snprintfz(webfilename, FILENAME_MAX, "%s/%s", web_dir, filename);
-
- // check if the file exists
- struct stat stat;
- if(lstat(webfilename, &stat) != 0) {
- debug(D_WEB_CLIENT_ACCESS, "%llu: File '%s' is not found.", w->id, webfilename);
- buffer_sprintf(w->response.data, "File '%s' does not exist, or is not accessible.", webfilename);
- return 404;
- }
-
- // check if the file is owned by expected user
- if(stat.st_uid != web_files_uid()) {
- error("%llu: File '%s' is owned by user %d (expected user %d). Access Denied.", w->id, webfilename, stat.st_uid, web_files_uid());
- buffer_sprintf(w->response.data, "Access to file '%s' is not permitted.", webfilename);
- return 403;
- }
-
- // check if the file is owned by expected group
- if(stat.st_gid != web_files_gid()) {
- error("%llu: File '%s' is owned by group %d (expected group %d). Access Denied.", w->id, webfilename, stat.st_gid, web_files_gid());
- buffer_sprintf(w->response.data, "Access to file '%s' is not permitted.", webfilename);
- return 403;
- }
-
- if((stat.st_mode & S_IFMT) == S_IFDIR) {
- snprintfz(webfilename, FILENAME_MAX, "%s/index.html", filename);
- return mysendfile(w, webfilename);
- }
-
- if((stat.st_mode & S_IFMT) != S_IFREG) {
- error("%llu: File '%s' is not a regular file. Access Denied.", w->id, webfilename);
- buffer_sprintf(w->response.data, "Access to file '%s' is not permitted.", webfilename);
- return 403;
- }
-
- // open the file
- w->ifd = open(webfilename, O_NONBLOCK, O_RDONLY);
- if(w->ifd == -1) {
- w->ifd = w->ofd;
-
- if(errno == EBUSY || errno == EAGAIN) {
- error("%llu: File '%s' is busy, sending 307 Moved Temporarily to force retry.", w->id, webfilename);
- buffer_sprintf(w->response.header, "Location: /" WEB_PATH_FILE "/%s\r\n", filename);
- buffer_sprintf(w->response.data, "The file '%s' is currently busy. Please try again later.", webfilename);
- return 307;
- }
- else {
- error("%llu: Cannot open file '%s'.", w->id, webfilename);
- buffer_sprintf(w->response.data, "Cannot open file '%s'.", webfilename);
- return 404;
- }
- }
-
- // pick a Content-Type for the file
- if(strstr(filename, ".html") != NULL) w->response.data->contenttype = CT_TEXT_HTML;
- else if(strstr(filename, ".js") != NULL) w->response.data->contenttype = CT_APPLICATION_X_JAVASCRIPT;
- else if(strstr(filename, ".css") != NULL) w->response.data->contenttype = CT_TEXT_CSS;
- else if(strstr(filename, ".xml") != NULL) w->response.data->contenttype = CT_TEXT_XML;
- else if(strstr(filename, ".xsl") != NULL) w->response.data->contenttype = CT_TEXT_XSL;
- else if(strstr(filename, ".txt") != NULL) w->response.data->contenttype = CT_TEXT_PLAIN;
- else if(strstr(filename, ".svg") != NULL) w->response.data->contenttype = CT_IMAGE_SVG_XML;
- else if(strstr(filename, ".ttf") != NULL) w->response.data->contenttype = CT_APPLICATION_X_FONT_TRUETYPE;
- else if(strstr(filename, ".otf") != NULL) w->response.data->contenttype = CT_APPLICATION_X_FONT_OPENTYPE;
- else if(strstr(filename, ".woff2")!= NULL) w->response.data->contenttype = CT_APPLICATION_FONT_WOFF2;
- else if(strstr(filename, ".woff") != NULL) w->response.data->contenttype = CT_APPLICATION_FONT_WOFF;
- else if(strstr(filename, ".eot") != NULL) w->response.data->contenttype = CT_APPLICATION_VND_MS_FONTOBJ;
- else if(strstr(filename, ".png") != NULL) w->response.data->contenttype = CT_IMAGE_PNG;
- else if(strstr(filename, ".jpg") != NULL) w->response.data->contenttype = CT_IMAGE_JPG;
- else if(strstr(filename, ".jpeg") != NULL) w->response.data->contenttype = CT_IMAGE_JPG;
- else if(strstr(filename, ".gif") != NULL) w->response.data->contenttype = CT_IMAGE_GIF;
- else if(strstr(filename, ".bmp") != NULL) w->response.data->contenttype = CT_IMAGE_BMP;
- else if(strstr(filename, ".ico") != NULL) w->response.data->contenttype = CT_IMAGE_XICON;
- else if(strstr(filename, ".icns") != NULL) w->response.data->contenttype = CT_IMAGE_ICNS;
- else w->response.data->contenttype = CT_APPLICATION_OCTET_STREAM;
-
- debug(D_WEB_CLIENT_ACCESS, "%llu: Sending file '%s' (%ld bytes, ifd %d, ofd %d).", w->id, webfilename, stat.st_size, w->ifd, w->ofd);
-
- w->mode = WEB_CLIENT_MODE_FILECOPY;
- w->wait_receive = 1;
- w->wait_send = 0;
- buffer_flush(w->response.data);
- w->response.rlen = stat.st_size;
- w->response.data->date = stat.st_mtim.tv_sec;
-
- return 200;
+ static char *web_dir = NULL;
+
+ // initialize our static data
+ if(unlikely(!web_dir)) web_dir = config_get("global", "web files directory", WEB_DIR);
+
+ debug(D_WEB_CLIENT, "%llu: Looking for file '%s/%s'", w->id, web_dir, filename);
+
+ // skip leading slashes
+ while (*filename == '/') filename++;
+
+ // if the filename contain known paths, skip them
+ if(strncmp(filename, WEB_PATH_FILE "/", strlen(WEB_PATH_FILE) + 1) == 0)
+ filename = &filename[strlen(WEB_PATH_FILE) + 1];
+
+ char *s;
+ for(s = filename; *s ;s++) {
+ if( !isalnum(*s) && *s != '/' && *s != '.' && *s != '-' && *s != '_') {
+ debug(D_WEB_CLIENT_ACCESS, "%llu: File '%s' is not acceptable.", w->id, filename);
+ buffer_sprintf(w->response.data, "File '%s' cannot be served. Filename contains invalid character '%c'", filename, *s);
+ return 400;
+ }
+ }
+
+ // if the filename contains a .. refuse to serve it
+ if(strstr(filename, "..") != 0) {
+ debug(D_WEB_CLIENT_ACCESS, "%llu: File '%s' is not acceptable.", w->id, filename);
+ buffer_sprintf(w->response.data, "File '%s' cannot be served. Relative filenames with '..' in them are not supported.", filename);
+ return 400;
+ }
+
+ // access the file
+ char webfilename[FILENAME_MAX + 1];
+ snprintfz(webfilename, FILENAME_MAX, "%s/%s", web_dir, filename);
+
+ // check if the file exists
+ struct stat stat;
+ if(lstat(webfilename, &stat) != 0) {
+ debug(D_WEB_CLIENT_ACCESS, "%llu: File '%s' is not found.", w->id, webfilename);
+ buffer_sprintf(w->response.data, "File '%s' does not exist, or is not accessible.", webfilename);
+ return 404;
+ }
+
+ // check if the file is owned by expected user
+ if(stat.st_uid != web_files_uid()) {
+ error("%llu: File '%s' is owned by user %u (expected user %u). Access Denied.", w->id, webfilename, stat.st_uid, web_files_uid());
+ buffer_sprintf(w->response.data, "Access to file '%s' is not permitted.", webfilename);
+ return 403;
+ }
+
+ // check if the file is owned by expected group
+ if(stat.st_gid != web_files_gid()) {
+ error("%llu: File '%s' is owned by group %u (expected group %u). Access Denied.", w->id, webfilename, stat.st_gid, web_files_gid());
+ buffer_sprintf(w->response.data, "Access to file '%s' is not permitted.", webfilename);
+ return 403;
+ }
+
+ if((stat.st_mode & S_IFMT) == S_IFDIR) {
+ snprintfz(webfilename, FILENAME_MAX, "%s/index.html", filename);
+ return mysendfile(w, webfilename);
+ }
+
+ if((stat.st_mode & S_IFMT) != S_IFREG) {
+ error("%llu: File '%s' is not a regular file. Access Denied.", w->id, webfilename);
+ buffer_sprintf(w->response.data, "Access to file '%s' is not permitted.", webfilename);
+ return 403;
+ }
+
+ // open the file
+ w->ifd = open(webfilename, O_NONBLOCK, O_RDONLY);
+ if(w->ifd == -1) {
+ w->ifd = w->ofd;
+
+ if(errno == EBUSY || errno == EAGAIN) {
+ error("%llu: File '%s' is busy, sending 307 Moved Temporarily to force retry.", w->id, webfilename);
+ buffer_sprintf(w->response.header, "Location: /" WEB_PATH_FILE "/%s\r\n", filename);
+ buffer_sprintf(w->response.data, "The file '%s' is currently busy. Please try again later.", webfilename);
+ return 307;
+ }
+ else {
+ error("%llu: Cannot open file '%s'.", w->id, webfilename);
+ buffer_sprintf(w->response.data, "Cannot open file '%s'.", webfilename);
+ return 404;
+ }
+ }
+ if(fcntl(w->ifd, F_SETFL, O_NONBLOCK) < 0)
+ error("%llu: Cannot set O_NONBLOCK on file '%s'.", w->id, webfilename);
+
+ // pick a Content-Type for the file
+ if(strstr(filename, ".html") != NULL) w->response.data->contenttype = CT_TEXT_HTML;
+ else if(strstr(filename, ".js") != NULL) w->response.data->contenttype = CT_APPLICATION_X_JAVASCRIPT;
+ else if(strstr(filename, ".css") != NULL) w->response.data->contenttype = CT_TEXT_CSS;
+ else if(strstr(filename, ".xml") != NULL) w->response.data->contenttype = CT_TEXT_XML;
+ else if(strstr(filename, ".xsl") != NULL) w->response.data->contenttype = CT_TEXT_XSL;
+ else if(strstr(filename, ".txt") != NULL) w->response.data->contenttype = CT_TEXT_PLAIN;
+ else if(strstr(filename, ".svg") != NULL) w->response.data->contenttype = CT_IMAGE_SVG_XML;
+ else if(strstr(filename, ".ttf") != NULL) w->response.data->contenttype = CT_APPLICATION_X_FONT_TRUETYPE;
+ else if(strstr(filename, ".otf") != NULL) w->response.data->contenttype = CT_APPLICATION_X_FONT_OPENTYPE;
+ else if(strstr(filename, ".woff2")!= NULL) w->response.data->contenttype = CT_APPLICATION_FONT_WOFF2;
+ else if(strstr(filename, ".woff") != NULL) w->response.data->contenttype = CT_APPLICATION_FONT_WOFF;
+ else if(strstr(filename, ".eot") != NULL) w->response.data->contenttype = CT_APPLICATION_VND_MS_FONTOBJ;
+ else if(strstr(filename, ".png") != NULL) w->response.data->contenttype = CT_IMAGE_PNG;
+ else if(strstr(filename, ".jpg") != NULL) w->response.data->contenttype = CT_IMAGE_JPG;
+ else if(strstr(filename, ".jpeg") != NULL) w->response.data->contenttype = CT_IMAGE_JPG;
+ else if(strstr(filename, ".gif") != NULL) w->response.data->contenttype = CT_IMAGE_GIF;
+ else if(strstr(filename, ".bmp") != NULL) w->response.data->contenttype = CT_IMAGE_BMP;
+ else if(strstr(filename, ".ico") != NULL) w->response.data->contenttype = CT_IMAGE_XICON;
+ else if(strstr(filename, ".icns") != NULL) w->response.data->contenttype = CT_IMAGE_ICNS;
+ else w->response.data->contenttype = CT_APPLICATION_OCTET_STREAM;
+
+ debug(D_WEB_CLIENT_ACCESS, "%llu: Sending file '%s' (%ld bytes, ifd %d, ofd %d).", w->id, webfilename, stat.st_size, w->ifd, w->ofd);
+
+ w->mode = WEB_CLIENT_MODE_FILECOPY;
+ w->wait_receive = 1;
+ w->wait_send = 0;
+ buffer_flush(w->response.data);
+ w->response.rlen = stat.st_size;
+ w->response.data->date = stat.st_mtim.tv_sec;
+
+ return 200;
}
#ifdef NETDATA_WITH_ZLIB
-void web_client_enable_deflate(struct web_client *w) {
- if(w->response.zinitialized == 1) {
- error("%llu: Compression has already be initialized for this client.", w->id);
- return;
- }
-
- if(w->response.sent) {
- error("%llu: Cannot enable compression in the middle of a conversation.", w->id);
- return;
- }
-
- w->response.zstream.zalloc = Z_NULL;
- w->response.zstream.zfree = Z_NULL;
- w->response.zstream.opaque = Z_NULL;
-
- w->response.zstream.next_in = (Bytef *)w->response.data->buffer;
- w->response.zstream.avail_in = 0;
- w->response.zstream.total_in = 0;
-
- w->response.zstream.next_out = w->response.zbuffer;
- w->response.zstream.avail_out = 0;
- w->response.zstream.total_out = 0;
-
- w->response.zstream.zalloc = Z_NULL;
- w->response.zstream.zfree = Z_NULL;
- w->response.zstream.opaque = Z_NULL;
-
-// if(deflateInit(&w->response.zstream, Z_DEFAULT_COMPRESSION) != Z_OK) {
-// error("%llu: Failed to initialize zlib. Proceeding without compression.", w->id);
-// return;
-// }
-
- // Select GZIP compression: windowbits = 15 + 16 = 31
- if(deflateInit2(&w->response.zstream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY) != Z_OK) {
- error("%llu: Failed to initialize zlib. Proceeding without compression.", w->id);
- return;
- }
-
- w->response.zsent = 0;
- w->response.zoutput = 1;
- w->response.zinitialized = 1;
-
- debug(D_DEFLATE, "%llu: Initialized compression.", w->id);
+void web_client_enable_deflate(struct web_client *w, int gzip) {
+ if(unlikely(w->response.zinitialized)) {
+ error("%llu: Compression has already be initialized for this client.", w->id);
+ return;
+ }
+
+ if(unlikely(w->response.sent)) {
+ error("%llu: Cannot enable compression in the middle of a conversation.", w->id);
+ return;
+ }
+
+ w->response.zstream.zalloc = Z_NULL;
+ w->response.zstream.zfree = Z_NULL;
+ w->response.zstream.opaque = Z_NULL;
+
+ w->response.zstream.next_in = (Bytef *)w->response.data->buffer;
+ w->response.zstream.avail_in = 0;
+ w->response.zstream.total_in = 0;
+
+ w->response.zstream.next_out = w->response.zbuffer;
+ w->response.zstream.avail_out = 0;
+ w->response.zstream.total_out = 0;
+
+ w->response.zstream.zalloc = Z_NULL;
+ w->response.zstream.zfree = Z_NULL;
+ w->response.zstream.opaque = Z_NULL;
+
+// if(deflateInit(&w->response.zstream, Z_DEFAULT_COMPRESSION) != Z_OK) {
+// error("%llu: Failed to initialize zlib. Proceeding without compression.", w->id);
+// return;
+// }
+
+ // Select GZIP compression: windowbits = 15 + 16 = 31
+ if(deflateInit2(&w->response.zstream, web_gzip_level, Z_DEFLATED, 15 + ((gzip)?16:0), 8, web_gzip_strategy) != Z_OK) {
+ error("%llu: Failed to initialize zlib. Proceeding without compression.", w->id);
+ return;
+ }
+
+ w->response.zsent = 0;
+ w->response.zoutput = 1;
+ w->response.zinitialized = 1;
+
+ debug(D_DEFLATE, "%llu: Initialized compression.", w->id);
}
#endif // NETDATA_WITH_ZLIB
+void buffer_data_options2string(BUFFER *wb, uint32_t options) {
+ int count = 0;
+
+ if(options & RRDR_OPTION_NONZERO) {
+ if(count++) buffer_strcat(wb, " ");
+ buffer_strcat(wb, "nonzero");
+ }
+
+ if(options & RRDR_OPTION_REVERSED) {
+ if(count++) buffer_strcat(wb, " ");
+ buffer_strcat(wb, "flip");
+ }
+
+ if(options & RRDR_OPTION_JSON_WRAP) {
+ if(count++) buffer_strcat(wb, " ");
+ buffer_strcat(wb, "jsonwrap");
+ }
+
+ if(options & RRDR_OPTION_MIN2MAX) {
+ if(count++) buffer_strcat(wb, " ");
+ buffer_strcat(wb, "min2max");
+ }
+
+ if(options & RRDR_OPTION_MILLISECONDS) {
+ if(count++) buffer_strcat(wb, " ");
+ buffer_strcat(wb, "ms");
+ }
+
+ if(options & RRDR_OPTION_ABSOLUTE) {
+ if(count++) buffer_strcat(wb, " ");
+ buffer_strcat(wb, "abs");
+ }
+
+ if(options & RRDR_OPTION_SECONDS) {
+ if(count++) buffer_strcat(wb, " ");
+ buffer_strcat(wb, "seconds");
+ }
+
+ if(options & RRDR_OPTION_NULL2ZERO) {
+ if(count++) buffer_strcat(wb, " ");
+ buffer_strcat(wb, "null2zero");
+ }
+
+ if(options & RRDR_OPTION_OBJECTSROWS) {
+ if(count++) buffer_strcat(wb, " ");
+ buffer_strcat(wb, "objectrows");
+ }
+
+ if(options & RRDR_OPTION_GOOGLE_JSON) {
+ if(count++) buffer_strcat(wb, " ");
+ buffer_strcat(wb, "google_json");
+ }
+
+ if(options & RRDR_OPTION_PERCENTAGE) {
+ if(count++) buffer_strcat(wb, " ");
+ buffer_strcat(wb, "percentage");
+ }
+
+ if(options & RRDR_OPTION_NOT_ALIGNED) {
+ if(count++) buffer_strcat(wb, " ");
+ buffer_strcat(wb, "unaligned");
+ }
+}
+
uint32_t web_client_api_request_v1_data_options(char *o)
{
- uint32_t ret = 0x00000000;
- char *tok;
-
- while(o && *o && (tok = mystrsep(&o, ", |"))) {
- if(!*tok) continue;
-
- if(!strcmp(tok, "nonzero"))
- ret |= RRDR_OPTION_NONZERO;
- else if(!strcmp(tok, "flip") || !strcmp(tok, "reversed") || !strcmp(tok, "reverse"))
- ret |= RRDR_OPTION_REVERSED;
- else if(!strcmp(tok, "jsonwrap"))
- ret |= RRDR_OPTION_JSON_WRAP;
- else if(!strcmp(tok, "min2max"))
- ret |= RRDR_OPTION_MIN2MAX;
- else if(!strcmp(tok, "ms") || !strcmp(tok, "milliseconds"))
- ret |= RRDR_OPTION_MILLISECONDS;
- else if(!strcmp(tok, "abs") || !strcmp(tok, "absolute") || !strcmp(tok, "absolute_sum") || !strcmp(tok, "absolute-sum"))
- ret |= RRDR_OPTION_ABSOLUTE;
- else if(!strcmp(tok, "seconds"))
- ret |= RRDR_OPTION_SECONDS;
- else if(!strcmp(tok, "null2zero"))
- ret |= RRDR_OPTION_NULL2ZERO;
- else if(!strcmp(tok, "objectrows"))
- ret |= RRDR_OPTION_OBJECTSROWS;
- else if(!strcmp(tok, "google_json"))
- ret |= RRDR_OPTION_GOOGLE_JSON;
- else if(!strcmp(tok, "percentage"))
- ret |= RRDR_OPTION_PERCENTAGE;
- }
-
- return ret;
+ uint32_t ret = 0x00000000;
+ char *tok;
+
+ while(o && *o && (tok = mystrsep(&o, ", |"))) {
+ if(!*tok) continue;
+
+ if(!strcmp(tok, "nonzero"))
+ ret |= RRDR_OPTION_NONZERO;
+ else if(!strcmp(tok, "flip") || !strcmp(tok, "reversed") || !strcmp(tok, "reverse"))
+ ret |= RRDR_OPTION_REVERSED;
+ else if(!strcmp(tok, "jsonwrap"))
+ ret |= RRDR_OPTION_JSON_WRAP;
+ else if(!strcmp(tok, "min2max"))
+ ret |= RRDR_OPTION_MIN2MAX;
+ else if(!strcmp(tok, "ms") || !strcmp(tok, "milliseconds"))
+ ret |= RRDR_OPTION_MILLISECONDS;
+ else if(!strcmp(tok, "abs") || !strcmp(tok, "absolute") || !strcmp(tok, "absolute_sum") || !strcmp(tok, "absolute-sum"))
+ ret |= RRDR_OPTION_ABSOLUTE;
+ else if(!strcmp(tok, "seconds"))
+ ret |= RRDR_OPTION_SECONDS;
+ else if(!strcmp(tok, "null2zero"))
+ ret |= RRDR_OPTION_NULL2ZERO;
+ else if(!strcmp(tok, "objectrows"))
+ ret |= RRDR_OPTION_OBJECTSROWS;
+ else if(!strcmp(tok, "google_json"))
+ ret |= RRDR_OPTION_GOOGLE_JSON;
+ else if(!strcmp(tok, "percentage"))
+ ret |= RRDR_OPTION_PERCENTAGE;
+ else if(!strcmp(tok, "unaligned"))
+ ret |= RRDR_OPTION_NOT_ALIGNED;
+ }
+
+ return ret;
}
uint32_t web_client_api_request_v1_data_format(char *name)
{
- if(!strcmp(name, DATASOURCE_FORMAT_DATATABLE_JSON)) // datatable
- return DATASOURCE_DATATABLE_JSON;
+ if(!strcmp(name, DATASOURCE_FORMAT_DATATABLE_JSON)) // datatable
+ return DATASOURCE_DATATABLE_JSON;
- else if(!strcmp(name, DATASOURCE_FORMAT_DATATABLE_JSONP)) // datasource
- return DATASOURCE_DATATABLE_JSONP;
+ else if(!strcmp(name, DATASOURCE_FORMAT_DATATABLE_JSONP)) // datasource
+ return DATASOURCE_DATATABLE_JSONP;
- else if(!strcmp(name, DATASOURCE_FORMAT_JSON)) // json
- return DATASOURCE_JSON;
+ else if(!strcmp(name, DATASOURCE_FORMAT_JSON)) // json
+ return DATASOURCE_JSON;
- else if(!strcmp(name, DATASOURCE_FORMAT_JSONP)) // jsonp
- return DATASOURCE_JSONP;
+ else if(!strcmp(name, DATASOURCE_FORMAT_JSONP)) // jsonp
+ return DATASOURCE_JSONP;
- else if(!strcmp(name, DATASOURCE_FORMAT_SSV)) // ssv
- return DATASOURCE_SSV;
+ else if(!strcmp(name, DATASOURCE_FORMAT_SSV)) // ssv
+ return DATASOURCE_SSV;
- else if(!strcmp(name, DATASOURCE_FORMAT_CSV)) // csv
- return DATASOURCE_CSV;
+ else if(!strcmp(name, DATASOURCE_FORMAT_CSV)) // csv
+ return DATASOURCE_CSV;
- else if(!strcmp(name, DATASOURCE_FORMAT_TSV) || !strcmp(name, "tsv-excel")) // tsv
- return DATASOURCE_TSV;
+ else if(!strcmp(name, DATASOURCE_FORMAT_TSV) || !strcmp(name, "tsv-excel")) // tsv
+ return DATASOURCE_TSV;
- else if(!strcmp(name, DATASOURCE_FORMAT_HTML)) // html
- return DATASOURCE_HTML;
+ else if(!strcmp(name, DATASOURCE_FORMAT_HTML)) // html
+ return DATASOURCE_HTML;
- else if(!strcmp(name, DATASOURCE_FORMAT_JS_ARRAY)) // array
- return DATASOURCE_JS_ARRAY;
+ else if(!strcmp(name, DATASOURCE_FORMAT_JS_ARRAY)) // array
+ return DATASOURCE_JS_ARRAY;
- else if(!strcmp(name, DATASOURCE_FORMAT_SSV_COMMA)) // ssvcomma
- return DATASOURCE_SSV_COMMA;
+ else if(!strcmp(name, DATASOURCE_FORMAT_SSV_COMMA)) // ssvcomma
+ return DATASOURCE_SSV_COMMA;
- else if(!strcmp(name, DATASOURCE_FORMAT_CSV_JSON_ARRAY)) // csvjsonarray
- return DATASOURCE_CSV_JSON_ARRAY;
+ else if(!strcmp(name, DATASOURCE_FORMAT_CSV_JSON_ARRAY)) // csvjsonarray
+ return DATASOURCE_CSV_JSON_ARRAY;
- return DATASOURCE_JSON;
+ return DATASOURCE_JSON;
}
uint32_t web_client_api_request_v1_data_google_format(char *name)
{
- if(!strcmp(name, "json"))
- return DATASOURCE_DATATABLE_JSONP;
+ if(!strcmp(name, "json"))
+ return DATASOURCE_DATATABLE_JSONP;
- else if(!strcmp(name, "html"))
- return DATASOURCE_HTML;
+ else if(!strcmp(name, "html"))
+ return DATASOURCE_HTML;
- else if(!strcmp(name, "csv"))
- return DATASOURCE_CSV;
+ else if(!strcmp(name, "csv"))
+ return DATASOURCE_CSV;
- else if(!strcmp(name, "tsv-excel"))
- return DATASOURCE_TSV;
+ else if(!strcmp(name, "tsv-excel"))
+ return DATASOURCE_TSV;
- return DATASOURCE_JSON;
+ return DATASOURCE_JSON;
}
-int web_client_api_request_v1_data_group(char *name)
+const char *group_method2string(int group) {
+ switch(group) {
+ case GROUP_UNDEFINED:
+ return "";
+
+ case GROUP_AVERAGE:
+ return "average";
+
+ case GROUP_MIN:
+ return "min";
+
+ case GROUP_MAX:
+ return "max";
+
+ case GROUP_SUM:
+ return "sum";
+
+ case GROUP_INCREMENTAL_SUM:
+ return "incremental-sum";
+
+ default:
+ return "unknown-group-method";
+ }
+}
+
+int web_client_api_request_v1_data_group(char *name, int def)
{
- if(!strcmp(name, "max"))
- return GROUP_MAX;
+ if(!strcmp(name, "average"))
+ return GROUP_AVERAGE;
+
+ else if(!strcmp(name, "min"))
+ return GROUP_MIN;
+
+ else if(!strcmp(name, "max"))
+ return GROUP_MAX;
+
+ else if(!strcmp(name, "sum"))
+ return GROUP_SUM;
- else if(!strcmp(name, "average"))
- return GROUP_AVERAGE;
+ else if(!strcmp(name, "incremental-sum"))
+ return GROUP_INCREMENTAL_SUM;
- return GROUP_MAX;
+ return def;
+}
+
+int web_client_api_request_v1_alarms(struct web_client *w, char *url)
+{
+ int all = 0;
+
+ while(url) {
+ char *value = mystrsep(&url, "?&[]");
+ if (!value || !*value) continue;
+
+ if(!strcmp(value, "all")) all = 1;
+ else if(!strcmp(value, "active")) all = 0;
+ }
+
+ buffer_flush(w->response.data);
+ w->response.data->contenttype = CT_APPLICATION_JSON;
+ health_alarms2json(&localhost, w->response.data, all);
+ return 200;
+}
+
+int web_client_api_request_v1_alarm_log(struct web_client *w, char *url)
+{
+ (void)url;
+
+ buffer_flush(w->response.data);
+ w->response.data->contenttype = CT_APPLICATION_JSON;
+ health_alarm_log2json(&localhost, w->response.data);
+ return 200;
}
int web_client_api_request_v1_charts(struct web_client *w, char *url)
{
- if(url) { ; }
+ if(url) { ; }
- buffer_flush(w->response.data);
- w->response.data->contenttype = CT_APPLICATION_JSON;
- rrd_stats_api_v1_charts(w->response.data);
- return 200;
+ buffer_flush(w->response.data);
+ w->response.data->contenttype = CT_APPLICATION_JSON;
+ rrd_stats_api_v1_charts(w->response.data);
+ return 200;
}
int web_client_api_request_v1_chart(struct web_client *w, char *url)
{
- int ret = 400;
- char *chart = NULL;
-
- buffer_flush(w->response.data);
-
- while(url) {
- char *value = mystrsep(&url, "?&[]");
- if(!value || !*value) continue;
-
- char *name = mystrsep(&value, "=");
- if(!name || !*name) continue;
- if(!value || !*value) continue;
-
- // name and value are now the parameters
- // they are not null and not empty
-
- if(!strcmp(name, "chart")) chart = value;
- //else {
- /// buffer_sprintf(w->response.data, "Unknown parameter '%s' in request.", name);
- // goto cleanup;
- //}
- }
-
- if(!chart || !*chart) {
- buffer_sprintf(w->response.data, "No chart id is given at the request.");
- goto cleanup;
- }
-
- RRDSET *st = rrdset_find(chart);
- if(!st) st = rrdset_find_byname(chart);
- if(!st) {
- buffer_sprintf(w->response.data, "Chart '%s' is not found.", chart);
- ret = 404;
- goto cleanup;
- }
-
- w->response.data->contenttype = CT_APPLICATION_JSON;
- rrd_stats_api_v1_chart(st, w->response.data);
- return 200;
+ int ret = 400;
+ char *chart = NULL;
+
+ buffer_flush(w->response.data);
+
+ while(url) {
+ char *value = mystrsep(&url, "?&[]");
+ if(!value || !*value) continue;
+
+ char *name = mystrsep(&value, "=");
+ if(!name || !*name) continue;
+ if(!value || !*value) continue;
+
+ // name and value are now the parameters
+ // they are not null and not empty
+
+ if(!strcmp(name, "chart")) chart = value;
+ //else {
+ /// buffer_sprintf(w->response.data, "Unknown parameter '%s' in request.", name);
+ // goto cleanup;
+ //}
+ }
+
+ if(!chart || !*chart) {
+ buffer_sprintf(w->response.data, "No chart id is given at the request.");
+ goto cleanup;
+ }
+
+ RRDSET *st = rrdset_find(chart);
+ if(!st) st = rrdset_find_byname(chart);
+ if(!st) {
+ buffer_sprintf(w->response.data, "Chart '%s' is not found.", chart);
+ ret = 404;
+ goto cleanup;
+ }
+
+ w->response.data->contenttype = CT_APPLICATION_JSON;
+ rrd_stats_api_v1_chart(st, w->response.data);
+ return 200;
cleanup:
- return ret;
+ return ret;
+}
+
+int web_client_api_request_v1_badge(struct web_client *w, char *url) {
+ int ret = 400;
+ buffer_flush(w->response.data);
+
+ BUFFER *dimensions = NULL;
+
+ const char *chart = NULL
+ , *before_str = NULL
+ , *after_str = NULL
+ , *points_str = NULL
+ , *multiply_str = NULL
+ , *divide_str = NULL
+ , *label = NULL
+ , *units = NULL
+ , *label_color = NULL
+ , *value_color = NULL
+ , *refresh_str = NULL
+ , *precision_str = NULL
+ , *alarm = NULL;
+
+ int group = GROUP_AVERAGE;
+ uint32_t options = 0x00000000;
+
+ while(url) {
+ char *value = mystrsep(&url, "/?&[]");
+ if(!value || !*value) continue;
+
+ char *name = mystrsep(&value, "=");
+ if(!name || !*name) continue;
+ if(!value || !*value) continue;
+
+ debug(D_WEB_CLIENT, "%llu: API v1 badge.svg query param '%s' with value '%s'", w->id, name, value);
+
+ // name and value are now the parameters
+ // they are not null and not empty
+
+ if(!strcmp(name, "chart")) chart = value;
+ else if(!strcmp(name, "dimension") || !strcmp(name, "dim") || !strcmp(name, "dimensions") || !strcmp(name, "dims")) {
+ if(!dimensions)
+ dimensions = buffer_create(100);
+
+ buffer_strcat(dimensions, "|");
+ buffer_strcat(dimensions, value);
+ }
+ else if(!strcmp(name, "after")) after_str = value;
+ else if(!strcmp(name, "before")) before_str = value;
+ else if(!strcmp(name, "points")) points_str = value;
+ else if(!strcmp(name, "group")) {
+ group = web_client_api_request_v1_data_group(value, GROUP_AVERAGE);
+ }
+ else if(!strcmp(name, "options")) {
+ options |= web_client_api_request_v1_data_options(value);
+ }
+ else if(!strcmp(name, "label")) label = value;
+ else if(!strcmp(name, "units")) units = value;
+ else if(!strcmp(name, "label_color")) label_color = value;
+ else if(!strcmp(name, "value_color")) value_color = value;
+ else if(!strcmp(name, "multiply")) multiply_str = value;
+ else if(!strcmp(name, "divide")) divide_str = value;
+ else if(!strcmp(name, "refresh")) refresh_str = value;
+ else if(!strcmp(name, "precision")) precision_str = value;
+ else if(!strcmp(name, "alarm")) alarm = value;
+ }
+
+ if(!chart || !*chart) {
+ buffer_sprintf(w->response.data, "No chart id is given at the request.");
+ goto cleanup;
+ }
+
+ RRDSET *st = rrdset_find(chart);
+ if(!st) st = rrdset_find_byname(chart);
+ if(!st) {
+ buffer_svg(w->response.data, "chart not found", 0, "", NULL, NULL, 1, -1);
+ ret = 200;
+ goto cleanup;
+ }
+
+ RRDCALC *rc = NULL;
+ if(alarm) {
+ rc = rrdcalc_find(st, alarm);
+ if (!rc) {
+ buffer_svg(w->response.data, "alarm not found", 0, "", NULL, NULL, 1, -1);
+ ret = 200;
+ goto cleanup;
+ }
+ }
+
+ long long multiply = (multiply_str && *multiply_str )?atol(multiply_str):1;
+ long long divide = (divide_str && *divide_str )?atol(divide_str):1;
+ long long before = (before_str && *before_str )?atol(before_str):0;
+ long long after = (after_str && *after_str )?atol(after_str):-st->update_every;
+ int points = (points_str && *points_str )?atoi(points_str):1;
+ int precision = (precision_str && *precision_str)?atoi(precision_str):-1;
+
+ if(!multiply) multiply = 1;
+ if(!divide) divide = 1;
+
+ int refresh = 0;
+ if(refresh_str && *refresh_str) {
+ if(!strcmp(refresh_str, "auto")) {
+ if(rc) refresh = rc->update_every;
+ else if(options & RRDR_OPTION_NOT_ALIGNED)
+ refresh = st->update_every;
+ else {
+ refresh = (before - after);
+ if(refresh < 0) refresh = -refresh;
+ }
+ }
+ else {
+ refresh = atoi(refresh_str);
+ if(refresh < 0) refresh = -refresh;
+ }
+ }
+
+ if(!label) {
+ if(alarm) {
+ char *s = (char *)alarm;
+ while(*s) {
+ if(*s == '_') *s = ' ';
+ s++;
+ }
+ label = alarm;
+ }
+ else if(dimensions) {
+ const char *dim = buffer_tostring(dimensions);
+ if(*dim == '|') dim++;
+ label = dim;
+ }
+ else
+ label = st->name;
+ }
+ if(!units) {
+ if(alarm) {
+ if(rc->units)
+ units = rc->units;
+ else
+ units = "";
+ }
+ else if(options & RRDR_OPTION_PERCENTAGE)
+ units = "%";
+ else
+ units = st->units;
+ }
+
+ debug(D_WEB_CLIENT, "%llu: API command 'badge.svg' for chart '%s', alarm '%s', dimensions '%s', after '%lld', before '%lld', points '%d', group '%d', options '0x%08x'"
+ , w->id
+ , chart
+ , alarm?alarm:""
+ , (dimensions)?buffer_tostring(dimensions):""
+ , after
+ , before
+ , points
+ , group
+ , options
+ );
+
+ if(rc) {
+ calculated_number n = rc->value;
+ if(isnan(n) || isinf(n)) n = 0;
+
+ if (refresh > 0)
+ buffer_sprintf(w->response.header, "Refresh: %d\r\n", refresh);
+
+ if(!value_color) {
+ switch(rc->status) {
+ case RRDCALC_STATUS_CRITICAL:
+ value_color = "red";
+ break;
+
+ case RRDCALC_STATUS_WARNING:
+ value_color = "orange";
+ break;
+
+ case RRDCALC_STATUS_CLEAR:
+ value_color = "brightgreen";
+ break;
+
+ case RRDCALC_STATUS_UNDEFINED:
+ value_color = "lightgrey";
+ break;
+
+ case RRDCALC_STATUS_UNINITIALIZED:
+ value_color = "#000";
+ break;
+
+ default:
+ value_color = "grey";
+ break;
+ }
+ }
+
+ buffer_svg(w->response.data, label, rc->value * multiply / divide, units, label_color, value_color, 0, precision);
+ ret = 200;
+ }
+ else {
+ time_t latest_timestamp = 0;
+ int value_is_null = 1;
+ calculated_number n = 0;
+ ret = 500;
+
+ // if the collected value is too old, don't calculate its value
+ if (rrdset_last_entry_t(st) >= (time(NULL) - (st->update_every * st->gap_when_lost_iterations_above)))
+ ret = rrd2value(st, w->response.data, &n, (dimensions) ? buffer_tostring(dimensions) : NULL, points, after,
+ before, group, options, NULL, &latest_timestamp, &value_is_null);
+
+ // if the value cannot be calculated, show empty badge
+ if (ret != 200) {
+ value_is_null = 1;
+ n = 0;
+ ret = 200;
+ }
+ else if (refresh > 0)
+ buffer_sprintf(w->response.header, "Refresh: %d\r\n", refresh);
+
+ // render the badge
+ buffer_svg(w->response.data, label, n * multiply / divide, units, label_color, value_color, value_is_null,
+ precision);
+ }
+
+cleanup:
+ if(dimensions)
+ buffer_free(dimensions);
+ return ret;
}
// returns the HTTP code
int web_client_api_request_v1_data(struct web_client *w, char *url)
{
- debug(D_WEB_CLIENT, "%llu: API v1 data with URL '%s'", w->id, url);
-
- int ret = 400;
- BUFFER *dimensions = NULL;
-
- buffer_flush(w->response.data);
-
- char *google_version = "0.6",
- *google_reqId = "0",
- *google_sig = "0",
- *google_out = "json",
- *responseHandler = NULL,
- *outFileName = NULL;
-
- time_t last_timestamp_in_data = 0, google_timestamp = 0;
-
- char *chart = NULL
- , *before_str = NULL
- , *after_str = NULL
- , *points_str = NULL;
-
- int group = GROUP_MAX;
- uint32_t format = DATASOURCE_JSON;
- uint32_t options = 0x00000000;
-
- while(url) {
- char *value = mystrsep(&url, "?&[]");
- if(!value || !*value) continue;
-
- char *name = mystrsep(&value, "=");
- if(!name || !*name) continue;
- if(!value || !*value) continue;
-
- debug(D_WEB_CLIENT, "%llu: API v1 data query param '%s' with value '%s'", w->id, name, value);
-
- // name and value are now the parameters
- // they are not null and not empty
-
- if(!strcmp(name, "chart")) chart = value;
- else if(!strcmp(name, "dimension") || !strcmp(name, "dim") || !strcmp(name, "dimensions") || !strcmp(name, "dims")) {
- if(!dimensions) dimensions = buffer_create(strlen(value));
- if(dimensions) {
- buffer_strcat(dimensions, "|");
- buffer_strcat(dimensions, value);
- }
- }
- else if(!strcmp(name, "after")) after_str = value;
- else if(!strcmp(name, "before")) before_str = value;
- else if(!strcmp(name, "points")) points_str = value;
- else if(!strcmp(name, "group")) {
- group = web_client_api_request_v1_data_group(value);
- }
- else if(!strcmp(name, "format")) {
- format = web_client_api_request_v1_data_format(value);
- }
- else if(!strcmp(name, "options")) {
- options |= web_client_api_request_v1_data_options(value);
- }
- else if(!strcmp(name, "callback")) {
- responseHandler = value;
- }
- else if(!strcmp(name, "filename")) {
- outFileName = value;
- }
- else if(!strcmp(name, "tqx")) {
- // parse Google Visualization API options
- // https://developers.google.com/chart/interactive/docs/dev/implementing_data_source
- char *tqx_name, *tqx_value;
-
- while(value) {
- tqx_value = mystrsep(&value, ";");
- if(!tqx_value || !*tqx_value) continue;
-
- tqx_name = mystrsep(&tqx_value, ":");
- if(!tqx_name || !*tqx_name) continue;
- if(!tqx_value || !*tqx_value) continue;
-
- if(!strcmp(tqx_name, "version"))
- google_version = tqx_value;
- else if(!strcmp(tqx_name, "reqId"))
- google_reqId = tqx_value;
- else if(!strcmp(tqx_name, "sig")) {
- google_sig = tqx_value;
- google_timestamp = strtoul(google_sig, NULL, 0);
- }
- else if(!strcmp(tqx_name, "out")) {
- google_out = tqx_value;
- format = web_client_api_request_v1_data_google_format(google_out);
- }
- else if(!strcmp(tqx_name, "responseHandler"))
- responseHandler = tqx_value;
- else if(!strcmp(tqx_name, "outFileName"))
- outFileName = tqx_value;
- }
- }
- }
-
- if(!chart || !*chart) {
- buffer_sprintf(w->response.data, "No chart id is given at the request.");
- goto cleanup;
- }
-
- RRDSET *st = rrdset_find(chart);
- if(!st) st = rrdset_find_byname(chart);
- if(!st) {
- buffer_sprintf(w->response.data, "Chart '%s' is not found.", chart);
- ret = 404;
- goto cleanup;
- }
-
- long long before = (before_str && *before_str)?atol(before_str):0;
- long long after = (after_str && *after_str) ?atol(after_str):0;
- int points = (points_str && *points_str)?atoi(points_str):0;
-
- debug(D_WEB_CLIENT, "%llu: API command 'data' for chart '%s', dimensions '%s', after '%lld', before '%lld', points '%d', group '%u', format '%u', options '0x%08x'"
- , w->id
- , chart
- , (dimensions)?buffer_tostring(dimensions):""
- , after
- , before
- , points
- , group
- , format
- , options
- );
-
- if(outFileName && *outFileName) {
- buffer_sprintf(w->response.header, "Content-Disposition: attachment; filename=\"%s\"\r\n", outFileName);
- error("generating outfilename header: '%s'", outFileName);
- }
-
- if(format == DATASOURCE_DATATABLE_JSONP) {
- if(responseHandler == NULL)
- responseHandler = "google.visualization.Query.setResponse";
-
- debug(D_WEB_CLIENT_ACCESS, "%llu: GOOGLE JSON/JSONP: version = '%s', reqId = '%s', sig = '%s', out = '%s', responseHandler = '%s', outFileName = '%s'",
- w->id, google_version, google_reqId, google_sig, google_out, responseHandler, outFileName
- );
-
- buffer_sprintf(w->response.data,
- "%s({version:'%s',reqId:'%s',status:'ok',sig:'%lu',table:",
- responseHandler, google_version, google_reqId, st->last_updated.tv_sec);
- }
- else if(format == DATASOURCE_JSONP) {
- if(responseHandler == NULL)
- responseHandler = "callback";
-
- buffer_strcat(w->response.data, responseHandler);
- buffer_strcat(w->response.data, "(");
- }
-
- ret = rrd2format(st, w->response.data, dimensions, format, points, after, before, group, options, &last_timestamp_in_data);
-
- if(format == DATASOURCE_DATATABLE_JSONP) {
- if(google_timestamp < last_timestamp_in_data)
- buffer_strcat(w->response.data, "});");
-
- else {
- // the client already has the latest data
- buffer_flush(w->response.data);
- buffer_sprintf(w->response.data,
- "%s({version:'%s',reqId:'%s',status:'error',errors:[{reason:'not_modified',message:'Data not modified'}]});",
- responseHandler, google_version, google_reqId);
- }
- }
- else if(format == DATASOURCE_JSONP)
- buffer_strcat(w->response.data, ");");
+ debug(D_WEB_CLIENT, "%llu: API v1 data with URL '%s'", w->id, url);
+
+ int ret = 400;
+ BUFFER *dimensions = NULL;
+
+ buffer_flush(w->response.data);
+
+ char *google_version = "0.6",
+ *google_reqId = "0",
+ *google_sig = "0",
+ *google_out = "json",
+ *responseHandler = NULL,
+ *outFileName = NULL;
+
+ time_t last_timestamp_in_data = 0, google_timestamp = 0;
+
+ char *chart = NULL
+ , *before_str = NULL
+ , *after_str = NULL
+ , *points_str = NULL;
+
+ int group = GROUP_AVERAGE;
+ uint32_t format = DATASOURCE_JSON;
+ uint32_t options = 0x00000000;
+
+ while(url) {
+ char *value = mystrsep(&url, "?&[]");
+ if(!value || !*value) continue;
+
+ char *name = mystrsep(&value, "=");
+ if(!name || !*name) continue;
+ if(!value || !*value) continue;
+
+ debug(D_WEB_CLIENT, "%llu: API v1 data query param '%s' with value '%s'", w->id, name, value);
+
+ // name and value are now the parameters
+ // they are not null and not empty
+
+ if(!strcmp(name, "chart")) chart = value;
+ else if(!strcmp(name, "dimension") || !strcmp(name, "dim") || !strcmp(name, "dimensions") || !strcmp(name, "dims")) {
+ if(!dimensions) dimensions = buffer_create(100);
+ buffer_strcat(dimensions, "|");
+ buffer_strcat(dimensions, value);
+ }
+ else if(!strcmp(name, "after")) after_str = value;
+ else if(!strcmp(name, "before")) before_str = value;
+ else if(!strcmp(name, "points")) points_str = value;
+ else if(!strcmp(name, "group")) {
+ group = web_client_api_request_v1_data_group(value, GROUP_AVERAGE);
+ }
+ else if(!strcmp(name, "format")) {
+ format = web_client_api_request_v1_data_format(value);
+ }
+ else if(!strcmp(name, "options")) {
+ options |= web_client_api_request_v1_data_options(value);
+ }
+ else if(!strcmp(name, "callback")) {
+ responseHandler = value;
+ }
+ else if(!strcmp(name, "filename")) {
+ outFileName = value;
+ }
+ else if(!strcmp(name, "tqx")) {
+ // parse Google Visualization API options
+ // https://developers.google.com/chart/interactive/docs/dev/implementing_data_source
+ char *tqx_name, *tqx_value;
+
+ while(value) {
+ tqx_value = mystrsep(&value, ";");
+ if(!tqx_value || !*tqx_value) continue;
+
+ tqx_name = mystrsep(&tqx_value, ":");
+ if(!tqx_name || !*tqx_name) continue;
+ if(!tqx_value || !*tqx_value) continue;
+
+ if(!strcmp(tqx_name, "version"))
+ google_version = tqx_value;
+ else if(!strcmp(tqx_name, "reqId"))
+ google_reqId = tqx_value;
+ else if(!strcmp(tqx_name, "sig")) {
+ google_sig = tqx_value;
+ google_timestamp = strtoul(google_sig, NULL, 0);
+ }
+ else if(!strcmp(tqx_name, "out")) {
+ google_out = tqx_value;
+ format = web_client_api_request_v1_data_google_format(google_out);
+ }
+ else if(!strcmp(tqx_name, "responseHandler"))
+ responseHandler = tqx_value;
+ else if(!strcmp(tqx_name, "outFileName"))
+ outFileName = tqx_value;
+ }
+ }
+ }
+
+ if(!chart || !*chart) {
+ buffer_sprintf(w->response.data, "No chart id is given at the request.");
+ goto cleanup;
+ }
+
+ RRDSET *st = rrdset_find(chart);
+ if(!st) st = rrdset_find_byname(chart);
+ if(!st) {
+ buffer_sprintf(w->response.data, "Chart '%s' is not found.", chart);
+ ret = 404;
+ goto cleanup;
+ }
+
+ long long before = (before_str && *before_str)?atol(before_str):0;
+ long long after = (after_str && *after_str) ?atol(after_str):0;
+ int points = (points_str && *points_str)?atoi(points_str):0;
+
+ debug(D_WEB_CLIENT, "%llu: API command 'data' for chart '%s', dimensions '%s', after '%lld', before '%lld', points '%d', group '%d', format '%u', options '0x%08x'"
+ , w->id
+ , chart
+ , (dimensions)?buffer_tostring(dimensions):""
+ , after
+ , before
+ , points
+ , group
+ , format
+ , options
+ );
+
+ if(outFileName && *outFileName) {
+ buffer_sprintf(w->response.header, "Content-Disposition: attachment; filename=\"%s\"\r\n", outFileName);
+ debug(D_WEB_CLIENT, "%llu: generating outfilename header: '%s'", w->id, outFileName);
+ }
+
+ if(format == DATASOURCE_DATATABLE_JSONP) {
+ if(responseHandler == NULL)
+ responseHandler = "google.visualization.Query.setResponse";
+
+ debug(D_WEB_CLIENT_ACCESS, "%llu: GOOGLE JSON/JSONP: version = '%s', reqId = '%s', sig = '%s', out = '%s', responseHandler = '%s', outFileName = '%s'",
+ w->id, google_version, google_reqId, google_sig, google_out, responseHandler, outFileName
+ );
+
+ buffer_sprintf(w->response.data,
+ "%s({version:'%s',reqId:'%s',status:'ok',sig:'%ld',table:",
+ responseHandler, google_version, google_reqId, st->last_updated.tv_sec);
+ }
+ else if(format == DATASOURCE_JSONP) {
+ if(responseHandler == NULL)
+ responseHandler = "callback";
+
+ buffer_strcat(w->response.data, responseHandler);
+ buffer_strcat(w->response.data, "(");
+ }
+
+ ret = rrd2format(st, w->response.data, dimensions, format, points, after, before, group, options, &last_timestamp_in_data);
+
+ if(format == DATASOURCE_DATATABLE_JSONP) {
+ if(google_timestamp < last_timestamp_in_data)
+ buffer_strcat(w->response.data, "});");
+
+ else {
+ // the client already has the latest data
+ buffer_flush(w->response.data);
+ buffer_sprintf(w->response.data,
+ "%s({version:'%s',reqId:'%s',status:'error',errors:[{reason:'not_modified',message:'Data not modified'}]});",
+ responseHandler, google_version, google_reqId);
+ }
+ }
+ else if(format == DATASOURCE_JSONP)
+ buffer_strcat(w->response.data, ");");
cleanup:
- if(dimensions) buffer_free(dimensions);
- return ret;
+ if(dimensions) buffer_free(dimensions);
+ return ret;
}
int web_client_api_request_v1_registry(struct web_client *w, char *url)
{
- char person_guid[36 + 1] = "";
-
- debug(D_WEB_CLIENT, "%llu: API v1 registry with URL '%s'", w->id, url);
-
- // FIXME
- // The browser may send multiple cookies with our id
-
- char *cookie = strstr(w->response.data->buffer, " " NETDATA_REGISTRY_COOKIE_NAME "=");
- if(cookie)
- strncpyz(person_guid, &cookie[sizeof(NETDATA_REGISTRY_COOKIE_NAME) + 1], 36);
-
- char action = '\0';
- char *machine_guid = NULL,
- *machine_url = NULL,
- *url_name = NULL,
- *search_machine_guid = NULL,
- *delete_url = NULL,
- *to_person_guid = NULL;
-
- while(url) {
- char *value = mystrsep(&url, "?&[]");
- if (!value || !*value) continue;
-
- char *name = mystrsep(&value, "=");
- if (!name || !*name) continue;
- if (!value || !*value) continue;
-
- debug(D_WEB_CLIENT, "%llu: API v1 registry query param '%s' with value '%s'", w->id, name, value);
-
- if(!strcmp(name, "action")) {
- if(!strcmp(value, "access")) action = 'A';
- else if(!strcmp(value, "hello")) action = 'H';
- else if(!strcmp(value, "delete")) action = 'D';
- else if(!strcmp(value, "search")) action = 'S';
- else if(!strcmp(value, "switch")) action = 'W';
- }
- else if(!strcmp(name, "machine"))
- machine_guid = value;
-
- else if(!strcmp(name, "url"))
- machine_url = value;
-
- else if(action == 'A') {
- if(!strcmp(name, "name"))
- url_name = value;
- }
- else if(action == 'D') {
- if(!strcmp(name, "delete_url"))
- delete_url = value;
- }
- else if(action == 'S') {
- if(!strcmp(name, "for"))
- search_machine_guid = value;
- }
- else if(action == 'W') {
- if(!strcmp(name, "to"))
- to_person_guid = value;
- }
- }
-
- if(action == 'A' && (!machine_guid || !machine_url || !url_name)) {
- buffer_flush(w->response.data);
- buffer_sprintf(w->response.data, "Invalid registry request - access requires these parameters: machine ('%s'), url ('%s'), name ('%s')",
- machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", url_name?url_name:"UNSET");
- return 400;
- }
- else if(action == 'D' && (!machine_guid || !machine_url || !delete_url)) {
- buffer_flush(w->response.data);
- buffer_sprintf(w->response.data, "Invalid registry request - delete requires these parameters: machine ('%s'), url ('%s'), delete_url ('%s')",
- machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", delete_url?delete_url:"UNSET");
- return 400;
- }
- else if(action == 'S' && (!machine_guid || !machine_url || !search_machine_guid)) {
- buffer_flush(w->response.data);
- buffer_sprintf(w->response.data, "Invalid registry request - search requires these parameters: machine ('%s'), url ('%s'), for ('%s')",
- machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", search_machine_guid?search_machine_guid:"UNSET");
- return 400;
- }
- else if(action == 'W' && (!machine_guid || !machine_url || !to_person_guid)) {
- buffer_flush(w->response.data);
- buffer_sprintf(w->response.data, "Invalid registry request - switching identity requires these parameters: machine ('%s'), url ('%s'), to ('%s')",
- machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", to_person_guid?to_person_guid:"UNSET");
- return 400;
- }
-
- switch(action) {
- case 'A':
- return registry_request_access_json(w, person_guid, machine_guid, machine_url, url_name, time(NULL));
-
- case 'D':
- return registry_request_delete_json(w, person_guid, machine_guid, machine_url, delete_url, time(NULL));
-
- case 'S':
- return registry_request_search_json(w, person_guid, machine_guid, machine_url, search_machine_guid, time(NULL));
-
- case 'W':
- return registry_request_switch_json(w, person_guid, machine_guid, machine_url, to_person_guid, time(NULL));
-
- case 'H':
- return registry_request_hello_json(w);
-
- default:
- buffer_flush(w->response.data);
- buffer_sprintf(w->response.data, "Invalid registry request - you need to set an action: hello, access, delete, search");
- return 400;
- }
-
- buffer_flush(w->response.data);
- buffer_sprintf(w->response.data, "Invalid or no registry action.");
- return 400;
+ static uint32_t hash_action = 0, hash_access = 0, hash_hello = 0, hash_delete = 0, hash_search = 0,
+ hash_switch = 0, hash_machine = 0, hash_url = 0, hash_name = 0, hash_delete_url = 0, hash_for = 0,
+ hash_to = 0 /*, hash_redirects = 0 */;
+
+ if(unlikely(!hash_action)) {
+ hash_action = simple_hash("action");
+ hash_access = simple_hash("access");
+ hash_hello = simple_hash("hello");
+ hash_delete = simple_hash("delete");
+ hash_search = simple_hash("search");
+ hash_switch = simple_hash("switch");
+ hash_machine = simple_hash("machine");
+ hash_url = simple_hash("url");
+ hash_name = simple_hash("name");
+ hash_delete_url = simple_hash("delete_url");
+ hash_for = simple_hash("for");
+ hash_to = simple_hash("to");
+/*
+ hash_redirects = simple_hash("redirects");
+*/
+ }
+
+ char person_guid[36 + 1] = "";
+
+ debug(D_WEB_CLIENT, "%llu: API v1 registry with URL '%s'", w->id, url);
+
+ // FIXME
+ // The browser may send multiple cookies with our id
+
+ char *cookie = strstr(w->response.data->buffer, NETDATA_REGISTRY_COOKIE_NAME "=");
+ if(cookie)
+ strncpyz(person_guid, &cookie[sizeof(NETDATA_REGISTRY_COOKIE_NAME)], 36);
+
+ char action = '\0';
+ char *machine_guid = NULL,
+ *machine_url = NULL,
+ *url_name = NULL,
+ *search_machine_guid = NULL,
+ *delete_url = NULL,
+ *to_person_guid = NULL;
+/*
+ int redirects = 0;
+*/
+
+ while(url) {
+ char *value = mystrsep(&url, "?&[]");
+ if (!value || !*value) continue;
+
+ char *name = mystrsep(&value, "=");
+ if (!name || !*name) continue;
+ if (!value || !*value) continue;
+
+ debug(D_WEB_CLIENT, "%llu: API v1 registry query param '%s' with value '%s'", w->id, name, value);
+
+ uint32_t hash = simple_hash(name);
+
+ if(hash == hash_action && !strcmp(name, "action")) {
+ uint32_t vhash = simple_hash(value);
+
+ if(vhash == hash_access && !strcmp(value, "access")) action = 'A';
+ else if(vhash == hash_hello && !strcmp(value, "hello")) action = 'H';
+ else if(vhash == hash_delete && !strcmp(value, "delete")) action = 'D';
+ else if(vhash == hash_search && !strcmp(value, "search")) action = 'S';
+ else if(vhash == hash_switch && !strcmp(value, "switch")) action = 'W';
+#ifdef NETDATA_INTERNAL_CHECKS
+ else error("unknown registry action '%s'", value);
+#endif /* NETDATA_INTERNAL_CHECKS */
+ }
+/*
+ else if(hash == hash_redirects && !strcmp(name, "redirects"))
+ redirects = atoi(value);
+*/
+ else if(hash == hash_machine && !strcmp(name, "machine"))
+ machine_guid = value;
+
+ else if(hash == hash_url && !strcmp(name, "url"))
+ machine_url = value;
+
+ else if(action == 'A') {
+ if(hash == hash_name && !strcmp(name, "name"))
+ url_name = value;
+ }
+ else if(action == 'D') {
+ if(hash == hash_delete_url && !strcmp(name, "delete_url"))
+ delete_url = value;
+ }
+ else if(action == 'S') {
+ if(hash == hash_for && !strcmp(name, "for"))
+ search_machine_guid = value;
+ }
+ else if(action == 'W') {
+ if(hash == hash_to && !strcmp(name, "to"))
+ to_person_guid = value;
+ }
+#ifdef NETDATA_INTERNAL_CHECKS
+ else error("unused registry URL parameter '%s' with value '%s'", name, value);
+#endif /* NETDATA_INTERNAL_CHECKS */
+ }
+
+ if(web_donotrack_comply && w->donottrack) {
+ buffer_flush(w->response.data);
+ buffer_sprintf(w->response.data, "Your web browser is sending 'DNT: 1' (Do Not Track). The registry requires persistent cookies on your browser to work.");
+ return 400;
+ }
+
+ if(action == 'A' && (!machine_guid || !machine_url || !url_name)) {
+ buffer_flush(w->response.data);
+ buffer_sprintf(w->response.data, "Invalid registry request - access requires these parameters: machine ('%s'), url ('%s'), name ('%s')",
+ machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", url_name?url_name:"UNSET");
+ return 400;
+ }
+ else if(action == 'D' && (!machine_guid || !machine_url || !delete_url)) {
+ buffer_flush(w->response.data);
+ buffer_sprintf(w->response.data, "Invalid registry request - delete requires these parameters: machine ('%s'), url ('%s'), delete_url ('%s')",
+ machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", delete_url?delete_url:"UNSET");
+ return 400;
+ }
+ else if(action == 'S' && (!machine_guid || !machine_url || !search_machine_guid)) {
+ buffer_flush(w->response.data);
+ buffer_sprintf(w->response.data, "Invalid registry request - search requires these parameters: machine ('%s'), url ('%s'), for ('%s')",
+ machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", search_machine_guid?search_machine_guid:"UNSET");
+ return 400;
+ }
+ else if(action == 'W' && (!machine_guid || !machine_url || !to_person_guid)) {
+ buffer_flush(w->response.data);
+ buffer_sprintf(w->response.data, "Invalid registry request - switching identity requires these parameters: machine ('%s'), url ('%s'), to ('%s')",
+ machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", to_person_guid?to_person_guid:"UNSET");
+ return 400;
+ }
+
+ switch(action) {
+ case 'A':
+ w->tracking_required = 1;
+ if(registry_verify_cookies_redirects() > 0 && (!cookie || !person_guid[0])) {
+ buffer_flush(w->response.data);
+
+ registry_set_cookie(w, "give-me-back-this-cookie-please");
+ w->response.data->contenttype = CT_APPLICATION_JSON;
+ buffer_sprintf(w->response.data, "{ \"status\": \"redirect\", \"registry\": \"%s\" }", registry_to_announce());
+ return 200;
+
+/*
+ * it seems that web browsers are ignoring 307 (Moved Temporarily)
+ * under certain conditions, when using CORS
+ * so this is commented and we use application level redirects instead
+ *
+ redirects++;
+
+ if(redirects > registry_verify_cookies_redirects()) {
+ buffer_flush(w->response.data);
+ buffer_sprintf(w->response.data, "Your browser does not support cookies");
+ return 400;
+ }
+
+ char *encoded_url = url_encode(machine_url);
+ if(!encoded_url) {
+ error("%llu: Cannot URL encode string '%s'", w->id, machine_url);
+ return 500;
+ }
+
+ char *encoded_name = url_encode(url_name);
+ if(!encoded_name) {
+ free(encoded_url);
+ error("%llu: Cannot URL encode string '%s'", w->id, url_name);
+ return 500;
+ }
+
+ char *encoded_guid = url_encode(machine_guid);
+ if(!encoded_guid) {
+ free(encoded_url);
+ free(encoded_name);
+ error("%llu: Cannot URL encode string '%s'", w->id, machine_guid);
+ return 500;
+ }
+
+ buffer_sprintf(w->response.header, "Location: %s/api/v1/registry?action=access&machine=%s&name=%s&url=%s&redirects=%d\r\n",
+ registry_to_announce(), encoded_guid, encoded_name, encoded_url, redirects);
+
+ free(encoded_guid);
+ free(encoded_name);
+ free(encoded_url);
+ return 307
+*/
+ }
+ return registry_request_access_json(w, person_guid, machine_guid, machine_url, url_name, time(NULL));
+
+ case 'D':
+ w->tracking_required = 1;
+ return registry_request_delete_json(w, person_guid, machine_guid, machine_url, delete_url, time(NULL));
+
+ case 'S':
+ w->tracking_required = 1;
+ return registry_request_search_json(w, person_guid, machine_guid, machine_url, search_machine_guid, time(NULL));
+
+ case 'W':
+ w->tracking_required = 1;
+ return registry_request_switch_json(w, person_guid, machine_guid, machine_url, to_person_guid, time(NULL));
+
+ case 'H':
+ return registry_request_hello_json(w);
+
+ default:
+ buffer_flush(w->response.data);
+ buffer_strcat(w->response.data, "Invalid registry request - you need to set an action: hello, access, delete, search");
+ return 400;
+ }
}
-int web_client_api_request_v1(struct web_client *w, char *url)
-{
- static uint32_t data_hash = 0, chart_hash = 0, charts_hash = 0, registry_hash = 0;
-
- if(unlikely(data_hash == 0)) {
- data_hash = simple_hash("data");
- chart_hash = simple_hash("chart");
- charts_hash = simple_hash("charts");
- registry_hash = simple_hash("registry");
- }
-
- // get the command
- char *tok = mystrsep(&url, "/?&");
- if(tok && *tok) {
- debug(D_WEB_CLIENT, "%llu: Searching for API v1 command '%s'.", w->id, tok);
- uint32_t hash = simple_hash(tok);
-
- if(hash == data_hash && !strcmp(tok, "data"))
- return web_client_api_request_v1_data(w, url);
-
- else if(hash == chart_hash && !strcmp(tok, "chart"))
- return web_client_api_request_v1_chart(w, url);
-
- else if(hash == charts_hash && !strcmp(tok, "charts"))
- return web_client_api_request_v1_charts(w, url);
-
- else if(hash == registry_hash && !strcmp(tok, "registry"))
- return web_client_api_request_v1_registry(w, url);
-
- else {
- buffer_flush(w->response.data);
- buffer_sprintf(w->response.data, "Unsupported v1 API command: %s", tok);
- return 404;
- }
- }
- else {
- buffer_flush(w->response.data);
- buffer_sprintf(w->response.data, "API v1 command?");
- return 400;
- }
+int web_client_api_request_v1(struct web_client *w, char *url) {
+ static uint32_t hash_data = 0, hash_chart = 0, hash_charts = 0, hash_registry = 0, hash_badge = 0, hash_alarms = 0, hash_alarm_log = 0;
+
+ if(unlikely(hash_data == 0)) {
+ hash_data = simple_hash("data");
+ hash_chart = simple_hash("chart");
+ hash_charts = simple_hash("charts");
+ hash_registry = simple_hash("registry");
+ hash_badge = simple_hash("badge.svg");
+ hash_alarms = simple_hash("alarms");
+ hash_alarm_log = simple_hash("alarm_log");
+ }
+
+ // get the command
+ char *tok = mystrsep(&url, "/?&");
+ if(tok && *tok) {
+ debug(D_WEB_CLIENT, "%llu: Searching for API v1 command '%s'.", w->id, tok);
+ uint32_t hash = simple_hash(tok);
+
+ if(hash == hash_data && !strcmp(tok, "data"))
+ return web_client_api_request_v1_data(w, url);
+
+ else if(hash == hash_chart && !strcmp(tok, "chart"))
+ return web_client_api_request_v1_chart(w, url);
+
+ else if(hash == hash_charts && !strcmp(tok, "charts"))
+ return web_client_api_request_v1_charts(w, url);
+
+ else if(hash == hash_registry && !strcmp(tok, "registry"))
+ return web_client_api_request_v1_registry(w, url);
+
+ else if(hash == hash_badge && !strcmp(tok, "badge.svg"))
+ return web_client_api_request_v1_badge(w, url);
+
+ else if(hash == hash_alarms && !strcmp(tok, "alarms"))
+ return web_client_api_request_v1_alarms(w, url);
+
+ else if(hash == hash_alarm_log && !strcmp(tok, "alarm_log"))
+ return web_client_api_request_v1_alarm_log(w, url);
+
+ else {
+ buffer_flush(w->response.data);
+ buffer_sprintf(w->response.data, "Unsupported v1 API command: %s", tok);
+ return 404;
+ }
+ }
+ else {
+ buffer_flush(w->response.data);
+ buffer_sprintf(w->response.data, "API v1 command?");
+ return 400;
+ }
}
int web_client_api_request(struct web_client *w, char *url)
{
- // get the api version
- char *tok = mystrsep(&url, "/?&");
- if(tok && *tok) {
- debug(D_WEB_CLIENT, "%llu: Searching for API version '%s'.", w->id, tok);
- if(strcmp(tok, "v1") == 0)
- return web_client_api_request_v1(w, url);
- else {
- buffer_flush(w->response.data);
- buffer_sprintf(w->response.data, "Unsupported API version: %s", tok);
- return 404;
- }
- }
- else {
- buffer_flush(w->response.data);
- buffer_sprintf(w->response.data, "Which API version?");
- return 400;
- }
+ // get the api version
+ char *tok = mystrsep(&url, "/?&");
+ if(tok && *tok) {
+ debug(D_WEB_CLIENT, "%llu: Searching for API version '%s'.", w->id, tok);
+ if(strcmp(tok, "v1") == 0)
+ return web_client_api_request_v1(w, url);
+ else {
+ buffer_flush(w->response.data);
+ buffer_sprintf(w->response.data, "Unsupported API version: %s", tok);
+ return 404;
+ }
+ }
+ else {
+ buffer_flush(w->response.data);
+ buffer_sprintf(w->response.data, "Which API version?");
+ return 400;
+ }
}
-int web_client_data_request(struct web_client *w, char *url, int datasource_type)
+int web_client_api_old_data_request(struct web_client *w, char *url, int datasource_type)
{
- RRDSET *st = NULL;
-
- char *args = strchr(url, '?');
- if(args) {
- *args='\0';
- args = &args[1];
- }
-
- // get the name of the data to show
- char *tok = mystrsep(&url, "/");
-
- // do we have such a data set?
- if(tok && *tok) {
- debug(D_WEB_CLIENT, "%llu: Searching for RRD data with name '%s'.", w->id, tok);
- st = rrdset_find_byname(tok);
- if(!st) st = rrdset_find(tok);
- }
-
- if(!st) {
- // we don't have it
- // try to send a file with that name
- buffer_flush(w->response.data);
- return(mysendfile(w, tok));
- }
-
- // we have it
- debug(D_WEB_CLIENT, "%llu: Found RRD data with name '%s'.", w->id, tok);
-
- // how many entries does the client want?
- long lines = rrd_default_history_entries;
- long group_count = 1;
- time_t after = 0, before = 0;
- int group_method = GROUP_AVERAGE;
- int nonzero = 0;
-
- if(url) {
- // parse the lines required
- tok = mystrsep(&url, "/");
- if(tok) lines = atoi(tok);
- if(lines < 1) lines = 1;
- }
- if(url) {
- // parse the group count required
- tok = mystrsep(&url, "/");
- if(tok && *tok) group_count = atoi(tok);
- if(group_count < 1) group_count = 1;
- //if(group_count > save_history / 20) group_count = save_history / 20;
- }
- if(url) {
- // parse the grouping method required
- tok = mystrsep(&url, "/");
- if(tok && *tok) {
- if(strcmp(tok, "max") == 0) group_method = GROUP_MAX;
- else if(strcmp(tok, "average") == 0) group_method = GROUP_AVERAGE;
- else if(strcmp(tok, "sum") == 0) group_method = GROUP_SUM;
- else debug(D_WEB_CLIENT, "%llu: Unknown group method '%s'", w->id, tok);
- }
- }
- if(url) {
- // parse after time
- tok = mystrsep(&url, "/");
- if(tok && *tok) after = strtoul(tok, NULL, 10);
- if(after < 0) after = 0;
- }
- if(url) {
- // parse before time
- tok = mystrsep(&url, "/");
- if(tok && *tok) before = strtoul(tok, NULL, 10);
- if(before < 0) before = 0;
- }
- if(url) {
- // parse nonzero
- tok = mystrsep(&url, "/");
- if(tok && *tok && strcmp(tok, "nonzero") == 0) nonzero = 1;
- }
-
- w->response.data->contenttype = CT_APPLICATION_JSON;
- buffer_flush(w->response.data);
-
- char *google_version = "0.6";
- char *google_reqId = "0";
- char *google_sig = "0";
- char *google_out = "json";
- char *google_responseHandler = "google.visualization.Query.setResponse";
- char *google_outFileName = NULL;
- time_t last_timestamp_in_data = 0;
- if(datasource_type == DATASOURCE_DATATABLE_JSON || datasource_type == DATASOURCE_DATATABLE_JSONP) {
-
- w->response.data->contenttype = CT_APPLICATION_X_JAVASCRIPT;
-
- while(args) {
- tok = mystrsep(&args, "&");
- if(tok && *tok) {
- char *name = mystrsep(&tok, "=");
- if(name && *name && strcmp(name, "tqx") == 0) {
- char *key = mystrsep(&tok, ":");
- char *value = mystrsep(&tok, ";");
- if(key && value && *key && *value) {
- if(strcmp(key, "version") == 0)
- google_version = value;
-
- else if(strcmp(key, "reqId") == 0)
- google_reqId = value;
-
- else if(strcmp(key, "sig") == 0)
- google_sig = value;
-
- else if(strcmp(key, "out") == 0)
- google_out = value;
-
- else if(strcmp(key, "responseHandler") == 0)
- google_responseHandler = value;
-
- else if(strcmp(key, "outFileName") == 0)
- google_outFileName = value;
- }
- }
- }
- }
-
- debug(D_WEB_CLIENT_ACCESS, "%llu: GOOGLE JSONP: version = '%s', reqId = '%s', sig = '%s', out = '%s', responseHandler = '%s', outFileName = '%s'",
- w->id, google_version, google_reqId, google_sig, google_out, google_responseHandler, google_outFileName
- );
-
- if(datasource_type == DATASOURCE_DATATABLE_JSONP) {
- last_timestamp_in_data = strtoul(google_sig, NULL, 0);
-
- // check the client wants json
- if(strcmp(google_out, "json") != 0) {
- buffer_sprintf(w->response.data,
- "%s({version:'%s',reqId:'%s',status:'error',errors:[{reason:'invalid_query',message:'output format is not supported',detailed_message:'the format %s requested is not supported by netdata.'}]});",
- google_responseHandler, google_version, google_reqId, google_out);
- return 200;
- }
- }
- }
-
- if(datasource_type == DATASOURCE_DATATABLE_JSONP) {
- buffer_sprintf(w->response.data,
- "%s({version:'%s',reqId:'%s',status:'ok',sig:'%lu',table:",
- google_responseHandler, google_version, google_reqId, st->last_updated.tv_sec);
- }
-
- debug(D_WEB_CLIENT_ACCESS, "%llu: Sending RRD data '%s' (id %s, %d lines, %d group, %d group_method, %lu after, %lu before).", w->id, st->name, st->id, lines, group_count, group_method, after, before);
- time_t timestamp_in_data = rrd_stats_json(datasource_type, st, w->response.data, lines, group_count, group_method, after, before, nonzero);
-
- if(datasource_type == DATASOURCE_DATATABLE_JSONP) {
- if(timestamp_in_data > last_timestamp_in_data)
- buffer_strcat(w->response.data, "});");
-
- else {
- // the client already has the latest data
- buffer_flush(w->response.data);
- buffer_sprintf(w->response.data,
- "%s({version:'%s',reqId:'%s',status:'error',errors:[{reason:'not_modified',message:'Data not modified'}]});",
- google_responseHandler, google_version, google_reqId);
- }
- }
-
- return 200;
+ RRDSET *st = NULL;
+
+ char *args = strchr(url, '?');
+ if(args) {
+ *args='\0';
+ args = &args[1];
+ }
+
+ // get the name of the data to show
+ char *tok = mystrsep(&url, "/");
+ if(!tok) tok = "";
+
+ // do we have such a data set?
+ if(*tok) {
+ debug(D_WEB_CLIENT, "%llu: Searching for RRD data with name '%s'.", w->id, tok);
+ st = rrdset_find_byname(tok);
+ if(!st) st = rrdset_find(tok);
+ }
+
+ if(!st) {
+ // we don't have it
+ // try to send a file with that name
+ buffer_flush(w->response.data);
+ return(mysendfile(w, tok));
+ }
+
+ // we have it
+ debug(D_WEB_CLIENT, "%llu: Found RRD data with name '%s'.", w->id, tok);
+
+ // how many entries does the client want?
+ int lines = rrd_default_history_entries;
+ int group_count = 1;
+ time_t after = 0, before = 0;
+ int group_method = GROUP_AVERAGE;
+ int nonzero = 0;
+
+ if(url) {
+ // parse the lines required
+ tok = mystrsep(&url, "/");
+ if(tok) lines = atoi(tok);
+ if(lines < 1) lines = 1;
+ }
+ if(url) {
+ // parse the group count required
+ tok = mystrsep(&url, "/");
+ if(tok && *tok) group_count = atoi(tok);
+ if(group_count < 1) group_count = 1;
+ //if(group_count > save_history / 20) group_count = save_history / 20;
+ }
+ if(url) {
+ // parse the grouping method required
+ tok = mystrsep(&url, "/");
+ if(tok && *tok) {
+ if(strcmp(tok, "max") == 0) group_method = GROUP_MAX;
+ else if(strcmp(tok, "average") == 0) group_method = GROUP_AVERAGE;
+ else if(strcmp(tok, "sum") == 0) group_method = GROUP_SUM;
+ else debug(D_WEB_CLIENT, "%llu: Unknown group method '%s'", w->id, tok);
+ }
+ }
+ if(url) {
+ // parse after time
+ tok = mystrsep(&url, "/");
+ if(tok && *tok) after = strtoul(tok, NULL, 10);
+ if(after < 0) after = 0;
+ }
+ if(url) {
+ // parse before time
+ tok = mystrsep(&url, "/");
+ if(tok && *tok) before = strtoul(tok, NULL, 10);
+ if(before < 0) before = 0;
+ }
+ if(url) {
+ // parse nonzero
+ tok = mystrsep(&url, "/");
+ if(tok && *tok && strcmp(tok, "nonzero") == 0) nonzero = 1;
+ }
+
+ w->response.data->contenttype = CT_APPLICATION_JSON;
+ buffer_flush(w->response.data);
+
+ char *google_version = "0.6";
+ char *google_reqId = "0";
+ char *google_sig = "0";
+ char *google_out = "json";
+ char *google_responseHandler = "google.visualization.Query.setResponse";
+ char *google_outFileName = NULL;
+ time_t last_timestamp_in_data = 0;
+ if(datasource_type == DATASOURCE_DATATABLE_JSON || datasource_type == DATASOURCE_DATATABLE_JSONP) {
+
+ w->response.data->contenttype = CT_APPLICATION_X_JAVASCRIPT;
+
+ while(args) {
+ tok = mystrsep(&args, "&");
+ if(tok && *tok) {
+ char *name = mystrsep(&tok, "=");
+ if(name && *name && strcmp(name, "tqx") == 0) {
+ char *key = mystrsep(&tok, ":");
+ char *value = mystrsep(&tok, ";");
+ if(key && value && *key && *value) {
+ if(strcmp(key, "version") == 0)
+ google_version = value;
+
+ else if(strcmp(key, "reqId") == 0)
+ google_reqId = value;
+
+ else if(strcmp(key, "sig") == 0)
+ google_sig = value;
+
+ else if(strcmp(key, "out") == 0)
+ google_out = value;
+
+ else if(strcmp(key, "responseHandler") == 0)
+ google_responseHandler = value;
+
+ else if(strcmp(key, "outFileName") == 0)
+ google_outFileName = value;
+ }
+ }
+ }
+ }
+
+ debug(D_WEB_CLIENT_ACCESS, "%llu: GOOGLE JSONP: version = '%s', reqId = '%s', sig = '%s', out = '%s', responseHandler = '%s', outFileName = '%s'",
+ w->id, google_version, google_reqId, google_sig, google_out, google_responseHandler, google_outFileName
+ );
+
+ if(datasource_type == DATASOURCE_DATATABLE_JSONP) {
+ last_timestamp_in_data = strtoul(google_sig, NULL, 0);
+
+ // check the client wants json
+ if(strcmp(google_out, "json") != 0) {
+ buffer_sprintf(w->response.data,
+ "%s({version:'%s',reqId:'%s',status:'error',errors:[{reason:'invalid_query',message:'output format is not supported',detailed_message:'the format %s requested is not supported by netdata.'}]});",
+ google_responseHandler, google_version, google_reqId, google_out);
+ return 200;
+ }
+ }
+ }
+
+ if(datasource_type == DATASOURCE_DATATABLE_JSONP) {
+ buffer_sprintf(w->response.data,
+ "%s({version:'%s',reqId:'%s',status:'ok',sig:'%ld',table:",
+ google_responseHandler, google_version, google_reqId, st->last_updated.tv_sec);
+ }
+
+ debug(D_WEB_CLIENT_ACCESS, "%llu: Sending RRD data '%s' (id %s, %d lines, %d group, %d group_method, %ld after, %ld before).",
+ w->id, st->name, st->id, lines, group_count, group_method, after, before);
+
+ time_t timestamp_in_data = rrd_stats_json(datasource_type, st, w->response.data, lines, group_count, group_method, (unsigned long)after, (unsigned long)before, nonzero);
+
+ if(datasource_type == DATASOURCE_DATATABLE_JSONP) {
+ if(timestamp_in_data > last_timestamp_in_data)
+ buffer_strcat(w->response.data, "});");
+
+ else {
+ // the client already has the latest data
+ buffer_flush(w->response.data);
+ buffer_sprintf(w->response.data,
+ "%s({version:'%s',reqId:'%s',status:'error',errors:[{reason:'not_modified',message:'Data not modified'}]});",
+ google_responseHandler, google_version, google_reqId);
+ }
+ }
+
+ return 200;
}
-/*
-int web_client_parse_request(struct web_client *w) {
- // protocol
- // hostname
- // path
- // query string name-value
- // http version
- // method
- // http request headers name-value
+const char *web_content_type_to_string(uint8_t contenttype) {
+ switch(contenttype) {
+ case CT_TEXT_HTML:
+ return "text/html; charset=utf-8";
- web_client_clean_request(w);
+ case CT_APPLICATION_XML:
+ return "application/xml; charset=utf-8";
- debug(D_WEB_DATA, "%llu: Processing data buffer of %d bytes: '%s'.", w->id, w->response.data->bytes, w->response.data->buffer);
+ case CT_APPLICATION_JSON:
+ return "application/json; charset=utf-8";
- char *buf = w->response.data->buffer;
- char *line, *tok;
+ case CT_APPLICATION_X_JAVASCRIPT:
+ return "application/x-javascript; charset=utf-8";
- // ------------------------------------------------------------------------
- // the first line
+ case CT_TEXT_CSS:
+ return "text/css; charset=utf-8";
- if(buf && (line = strsep(&buf, "\r\n"))) {
- // method
- if(line && (tok = strsep(&line, " "))) {
- w->request.protocol = strdup(tok);
- }
- else goto cleanup;
+ case CT_TEXT_XML:
+ return "text/xml; charset=utf-8";
- // url
- }
- else goto cleanup;
+ case CT_TEXT_XSL:
+ return "text/xsl; charset=utf-8";
- // ------------------------------------------------------------------------
- // the rest of the lines
+ case CT_APPLICATION_OCTET_STREAM:
+ return "application/octet-stream";
- while(buf && (line = strsep(&buf, "\r\n"))) {
- while(line && (tok = strsep(&line, ": "))) {
- }
- }
+ case CT_IMAGE_SVG_XML:
+ return "image/svg+xml";
- char *url = NULL;
+ case CT_APPLICATION_X_FONT_TRUETYPE:
+ return "application/x-font-truetype";
+ case CT_APPLICATION_X_FONT_OPENTYPE:
+ return "application/x-font-opentype";
-cleanup:
- web_client_clean_request(w);
- return 0;
+ case CT_APPLICATION_FONT_WOFF:
+ return "application/font-woff";
+
+ case CT_APPLICATION_FONT_WOFF2:
+ return "application/font-woff2";
+
+ case CT_APPLICATION_VND_MS_FONTOBJ:
+ return "application/vnd.ms-fontobject";
+
+ case CT_IMAGE_PNG:
+ return "image/png";
+
+ case CT_IMAGE_JPG:
+ return "image/jpeg";
+
+ case CT_IMAGE_GIF:
+ return "image/gif";
+
+ case CT_IMAGE_XICON:
+ return "image/x-icon";
+
+ case CT_IMAGE_BMP:
+ return "image/bmp";
+
+ case CT_IMAGE_ICNS:
+ return "image/icns";
+
+ default:
+ case CT_TEXT_PLAIN:
+ return "text/plain; charset=utf-8";
+ }
}
-*/
-static inline char *http_header_parse(struct web_client *w, char *s) {
- char *e = s;
+const char *web_response_code_to_string(int code) {
+ switch(code) {
+ case 200:
+ return "OK";
+
+ case 307:
+ return "Temporary Redirect";
- // find the :
- while(*e && *e != ':') e++;
- if(!*e || e[1] != ' ') return e;
+ case 400:
+ return "Bad Request";
- // get the name
- *e = '\0';
+ case 403:
+ return "Forbidden";
- // find the value
- char *v, *ve;
- v = ve = e + 2;
+ case 404:
+ return "Not Found";
- // find the \r
- while(*ve && *ve != '\r') ve++;
- if(!*ve || ve[1] != '\n') {
- *e = ':';
- return ve;
- }
+ case 412:
+ return "Preconditions Failed";
- // terminate the value
- *ve = '\0';
+ default:
+ if(code >= 100 && code < 200)
+ return "Informational";
- // fprintf(stderr, "HEADER: '%s' = '%s'\n", s, v);
+ if(code >= 200 && code < 300)
+ return "Successful";
- if(!strcasecmp(s, "Origin"))
- strncpyz(w->origin, v, ORIGIN_MAX);
+ if(code >= 300 && code < 400)
+ return "Redirection";
+
+ if(code >= 400 && code < 500)
+ return "Bad Request";
+
+ if(code >= 500 && code < 600)
+ return "Server Error";
+
+ return "Undefined Error";
+ }
+}
- else if(!strcasecmp(s, "Connection")) {
- if(strcasestr(v, "keep-alive"))
- w->keepalive = 1;
- }
+static inline char *http_header_parse(struct web_client *w, char *s) {
+ static uint32_t hash_origin = 0, hash_connection = 0, hash_accept_encoding = 0, hash_donottrack = 0;
+
+ if(unlikely(!hash_origin)) {
+ hash_origin = simple_uhash("Origin");
+ hash_connection = simple_uhash("Connection");
+ hash_accept_encoding = simple_uhash("Accept-Encoding");
+ hash_donottrack = simple_uhash("DNT");
+ }
+
+ char *e = s;
+
+ // find the :
+ while(*e && *e != ':') e++;
+ if(!*e) return e;
+
+ // get the name
+ *e = '\0';
+
+ // find the value
+ char *v = e + 1, *ve;
+
+ // skip leading spaces from value
+ while(*v == ' ') v++;
+ ve = v;
+
+ // find the \r
+ while(*ve && *ve != '\r') ve++;
+ if(!*ve || ve[1] != '\n') {
+ *e = ':';
+ return ve;
+ }
+
+ // terminate the value
+ *ve = '\0';
+
+ // fprintf(stderr, "HEADER: '%s' = '%s'\n", s, v);
+ uint32_t hash = simple_uhash(s);
+
+ if(hash == hash_origin && !strcasecmp(s, "Origin"))
+ strncpyz(w->origin, v, ORIGIN_MAX);
+
+ else if(hash == hash_connection && !strcasecmp(s, "Connection")) {
+ if(strcasestr(v, "keep-alive"))
+ w->keepalive = 1;
+ }
+ else if(web_donotrack_comply && hash == hash_donottrack && !strcasecmp(s, "DNT")) {
+ if(*v == '0') w->donottrack = 0;
+ else if(*v == '1') w->donottrack = 1;
+ }
#ifdef NETDATA_WITH_ZLIB
- else if(!strcasecmp(s, "Accept-Encoding")) {
- if(web_enable_gzip && strcasestr(v, "gzip")) {
- w->enable_gzip = 1;
- }
- }
+ else if(hash == hash_accept_encoding && !strcasecmp(s, "Accept-Encoding")) {
+ if(web_enable_gzip) {
+ if(strcasestr(v, "gzip"))
+ web_client_enable_deflate(w, 1);
+ //
+ // does not seem to work
+ // else if(strcasestr(v, "deflate"))
+ // web_client_enable_deflate(w, 0);
+ }
+ }
#endif /* NETDATA_WITH_ZLIB */
- *e = ':';
- *ve = '\r';
- return ve;
+ *e = ':';
+ *ve = '\r';
+ return ve;
}
// http_request_validate()
// returns:
// = 0 : all good, process the request
-// > 0 : request is complete, but is not supported
+// > 0 : request is not supported
// < 0 : request is incomplete - wait for more data
static inline int http_request_validate(struct web_client *w) {
- char *s = w->response.data->buffer, *encoded_url = NULL;
-
- // is is a valid request?
- if(!strncmp(s, "GET ", 4)) {
- encoded_url = s = &s[4];
- w->mode = WEB_CLIENT_MODE_NORMAL;
- }
- else if(!strncmp(s, "OPTIONS ", 8)) {
- encoded_url = s = &s[8];
- w->mode = WEB_CLIENT_MODE_OPTIONS;
- }
- else {
- w->wait_receive = 0;
- return 1;
- }
-
- // find the SPACE + "HTTP/"
- while(*s) {
- // find the space
- while (*s && *s != ' ') s++;
-
- // is it SPACE + "HTTP/" ?
- if(*s && !strncmp(s, " HTTP/", 6)) break;
- else s++;
- }
-
- // incomplete requests
- if(!*s) {
- w->wait_receive = 1;
- return -2;
- }
-
- // we have the end of encoded_url - remember it
- char *ue = s;
-
- while(*s) {
- // find a line feed
- while (*s && *s != '\r') s++;
-
- // did we reach the end?
- if(unlikely(!*s)) break;
-
- // is it \r\n ?
- if (likely(s[1] == '\n')) {
-
- // is it again \r\n ? (header end)
- if(unlikely(s[2] == '\r' && s[3] == '\n')) {
- // a valid complete HTTP request found
-
- *ue = '\0';
- w->decoded_url = url_decode(encoded_url);
- *ue = ' ';
-
- w->wait_receive = 0;
- return 0;
- }
-
- // another header line
- s = http_header_parse(w, &s[2]);
- }
- else s++;
- }
-
- // incomplete request
- w->wait_receive = 1;
- return -3;
+ char *s = w->response.data->buffer, *encoded_url = NULL;
+
+ // is is a valid request?
+ if(!strncmp(s, "GET ", 4)) {
+ encoded_url = s = &s[4];
+ w->mode = WEB_CLIENT_MODE_NORMAL;
+ }
+ else if(!strncmp(s, "OPTIONS ", 8)) {
+ encoded_url = s = &s[8];
+ w->mode = WEB_CLIENT_MODE_OPTIONS;
+ }
+ else {
+ w->wait_receive = 0;
+ return 1;
+ }
+
+ // find the SPACE + "HTTP/"
+ while(*s) {
+ // find the next space
+ while (*s && *s != ' ') s++;
+
+ // is it SPACE + "HTTP/" ?
+ if(*s && !strncmp(s, " HTTP/", 6)) break;
+ else s++;
+ }
+
+ // incomplete requests
+ if(unlikely(!*s)) {
+ w->wait_receive = 1;
+ return -2;
+ }
+
+ // we have the end of encoded_url - remember it
+ char *ue = s;
+
+ // make sure we have complete request
+ // complete requests contain: \r\n\r\n
+ while(*s) {
+ // find a line feed
+ while(*s && *s++ != '\r');
+
+ // did we reach the end?
+ if(unlikely(!*s)) break;
+
+ // is it \r\n ?
+ if(likely(*s++ == '\n')) {
+
+ // is it again \r\n ? (header end)
+ if(unlikely(*s == '\r' && s[1] == '\n')) {
+ // a valid complete HTTP request found
+
+ *ue = '\0';
+ url_decode_r(w->decoded_url, encoded_url, URL_MAX + 1);
+ *ue = ' ';
+
+ // copy the URL - we are going to overwrite parts of it
+ // FIXME -- we should avoid it
+ strncpyz(w->last_url, w->decoded_url, URL_MAX);
+
+ w->wait_receive = 0;
+ return 0;
+ }
+
+ // another header line
+ s = http_header_parse(w, s);
+ }
+ }
+
+ // incomplete request
+ w->wait_receive = 1;
+ return -3;
}
void web_client_process(struct web_client *w) {
- int code = 500;
- ssize_t bytes;
-
- int what_to_do = http_request_validate(w);
-
- // wait for more data
- if(what_to_do < 0) {
- if(w->response.data->len > TOO_BIG_REQUEST) {
- strcpy(w->last_url, "too big request");
-
- debug(D_WEB_CLIENT_ACCESS, "%llu: Received request is too big (%zd bytes).", w->id, w->response.data->len);
-
- code = 400;
- buffer_flush(w->response.data);
- buffer_sprintf(w->response.data, "Received request is too big (%zd bytes).\r\n", w->response.data->len);
- }
- else {
- // wait for more data
- return;
- }
- }
- else if(what_to_do > 0) {
- strcpy(w->last_url, "not a valid response");
-
- debug(D_WEB_CLIENT_ACCESS, "%llu: Cannot understand '%s'.", w->id, w->response.data->buffer);
-
- code = 500;
- buffer_flush(w->response.data);
- buffer_strcat(w->response.data, "I don't understand you...\r\n");
- }
- else { // what_to_do == 0
- gettimeofday(&w->tv_in, NULL);
-
- global_statistics_lock();
- global_statistics.web_requests++;
- global_statistics_unlock();
-
- // copy the URL - we are going to overwrite parts of it
- // FIXME -- we should avoid it
- strncpyz(w->last_url, w->decoded_url, URL_MAX);
-
- if(w->mode == WEB_CLIENT_MODE_OPTIONS) {
- code = 200;
- w->response.data->contenttype = CT_TEXT_PLAIN;
- buffer_flush(w->response.data);
- buffer_strcat(w->response.data, "OK");
- }
- else {
-#ifdef NETDATA_WITH_ZLIB
- if(w->enable_gzip)
- web_client_enable_deflate(w);
-#endif
-
- char *url = w->decoded_url;
- char *tok = mystrsep(&url, "/?");
- if(tok && *tok) {
- debug(D_WEB_CLIENT, "%llu: Processing command '%s'.", w->id, tok);
-
- if(strcmp(tok, "api") == 0) {
- // the client is requesting api access
- code = web_client_api_request(w, url);
- }
- else if(strcmp(tok, "netdata.conf") == 0) {
- code = 200;
- debug(D_WEB_CLIENT_ACCESS, "%llu: Sending netdata.conf ...", w->id);
-
- w->response.data->contenttype = CT_TEXT_PLAIN;
- buffer_flush(w->response.data);
- generate_config(w->response.data, 0);
- }
- else if(strcmp(tok, WEB_PATH_DATA) == 0) { // "data"
- // the client is requesting rrd data -- OLD API
- code = web_client_data_request(w, url, DATASOURCE_JSON);
- }
- else if(strcmp(tok, WEB_PATH_DATASOURCE) == 0) { // "datasource"
- // the client is requesting google datasource -- OLD API
- code = web_client_data_request(w, url, DATASOURCE_DATATABLE_JSONP);
- }
- else if(strcmp(tok, WEB_PATH_GRAPH) == 0) { // "graph"
- // the client is requesting an rrd graph -- OLD API
-
- // get the name of the data to show
- tok = mystrsep(&url, "/?&");
- if(tok && *tok) {
- debug(D_WEB_CLIENT, "%llu: Searching for RRD data with name '%s'.", w->id, tok);
-
- // do we have such a data set?
- RRDSET *st = rrdset_find_byname(tok);
- if(!st) st = rrdset_find(tok);
- if(!st) {
- // we don't have it
- // try to send a file with that name
- buffer_flush(w->response.data);
- code = mysendfile(w, tok);
- }
- else {
- code = 200;
- debug(D_WEB_CLIENT_ACCESS, "%llu: Sending %s.json of RRD_STATS...", w->id, st->name);
- w->response.data->contenttype = CT_APPLICATION_JSON;
- buffer_flush(w->response.data);
- rrd_stats_graph_json(st, url, w->response.data);
- }
- }
- else {
- code = 400;
- buffer_flush(w->response.data);
- buffer_strcat(w->response.data, "Graph name?\r\n");
- }
- }
- else if(strcmp(tok, "list") == 0) {
- // OLD API
- code = 200;
-
- debug(D_WEB_CLIENT_ACCESS, "%llu: Sending list of RRD_STATS...", w->id);
-
- buffer_flush(w->response.data);
- RRDSET *st = rrdset_root;
-
- for ( ; st ; st = st->next )
- buffer_sprintf(w->response.data, "%s\n", st->name);
- }
- else if(strcmp(tok, "all.json") == 0) {
- // OLD API
- code = 200;
- debug(D_WEB_CLIENT_ACCESS, "%llu: Sending JSON list of all monitors of RRD_STATS...", w->id);
-
- w->response.data->contenttype = CT_APPLICATION_JSON;
- buffer_flush(w->response.data);
- rrd_stats_all_json(w->response.data);
- }
+ static uint32_t hash_api = 0, hash_netdata_conf = 0, hash_data = 0, hash_datasource = 0, hash_graph = 0,
+ hash_list = 0, hash_all_json = 0, hash_exit = 0, hash_debug = 0, hash_mirror = 0;
+
+ // start timing us
+ gettimeofday(&w->tv_in, NULL);
+
+ if(unlikely(!hash_api)) {
+ hash_api = simple_hash("api");
+ hash_netdata_conf = simple_hash("netdata.conf");
+ hash_data = simple_hash(WEB_PATH_DATA);
+ hash_datasource = simple_hash(WEB_PATH_DATASOURCE);
+ hash_graph = simple_hash(WEB_PATH_GRAPH);
+ hash_list = simple_hash("list");
+ hash_all_json = simple_hash("all.json");
+ hash_exit = simple_hash("exit");
+ hash_debug = simple_hash("debug");
+ hash_mirror = simple_hash("mirror");
+ }
+
+ int code = 500;
+ ssize_t bytes;
+
+ int what_to_do = http_request_validate(w);
+
+ // wait for more data
+ if(what_to_do < 0) {
+ if(w->response.data->len > TOO_BIG_REQUEST) {
+ strcpy(w->last_url, "too big request");
+
+ debug(D_WEB_CLIENT_ACCESS, "%llu: Received request is too big (%zu bytes).", w->id, w->response.data->len);
+
+ code = 400;
+ buffer_flush(w->response.data);
+ buffer_sprintf(w->response.data, "Received request is too big (%zu bytes).\r\n", w->response.data->len);
+ }
+ else {
+ // wait for more data
+ return;
+ }
+ }
+ else if(what_to_do > 0) {
+ // strcpy(w->last_url, "not a valid request");
+
+ debug(D_WEB_CLIENT_ACCESS, "%llu: Cannot understand '%s'.", w->id, w->response.data->buffer);
+
+ code = 500;
+ buffer_flush(w->response.data);
+ buffer_strcat(w->response.data, "I don't understand you...\r\n");
+ }
+ else { // what_to_do == 0
+ if(w->mode == WEB_CLIENT_MODE_OPTIONS) {
+ code = 200;
+ w->response.data->contenttype = CT_TEXT_PLAIN;
+ buffer_flush(w->response.data);
+ buffer_strcat(w->response.data, "OK");
+ }
+ else {
+ char *url = w->decoded_url;
+ char *tok = mystrsep(&url, "/?");
+ if(tok && *tok) {
+ uint32_t hash = simple_hash(tok);
+ debug(D_WEB_CLIENT, "%llu: Processing command '%s'.", w->id, tok);
+
+ if(hash == hash_api && strcmp(tok, "api") == 0) {
+ // the client is requesting api access
+ code = web_client_api_request(w, url);
+ }
+ else if(hash == hash_netdata_conf && strcmp(tok, "netdata.conf") == 0) {
+ code = 200;
+ debug(D_WEB_CLIENT_ACCESS, "%llu: Sending netdata.conf ...", w->id);
+
+ w->response.data->contenttype = CT_TEXT_PLAIN;
+ buffer_flush(w->response.data);
+ generate_config(w->response.data, 0);
+ }
+ else if(hash == hash_data && strcmp(tok, WEB_PATH_DATA) == 0) { // "data"
+ // the client is requesting rrd data -- OLD API
+ code = web_client_api_old_data_request(w, url, DATASOURCE_JSON);
+ }
+ else if(hash == hash_datasource && strcmp(tok, WEB_PATH_DATASOURCE) == 0) { // "datasource"
+ // the client is requesting google datasource -- OLD API
+ code = web_client_api_old_data_request(w, url, DATASOURCE_DATATABLE_JSONP);
+ }
+ else if(hash == hash_graph && strcmp(tok, WEB_PATH_GRAPH) == 0) { // "graph"
+ // the client is requesting an rrd graph -- OLD API
+
+ // get the name of the data to show
+ tok = mystrsep(&url, "/?&");
+ if(tok && *tok) {
+ debug(D_WEB_CLIENT, "%llu: Searching for RRD data with name '%s'.", w->id, tok);
+
+ // do we have such a data set?
+ RRDSET *st = rrdset_find_byname(tok);
+ if(!st) st = rrdset_find(tok);
+ if(!st) {
+ // we don't have it
+ // try to send a file with that name
+ buffer_flush(w->response.data);
+ code = mysendfile(w, tok);
+ }
+ else {
+ code = 200;
+ debug(D_WEB_CLIENT_ACCESS, "%llu: Sending %s.json of RRD_STATS...", w->id, st->name);
+ w->response.data->contenttype = CT_APPLICATION_JSON;
+ buffer_flush(w->response.data);
+ rrd_stats_graph_json(st, url, w->response.data);
+ }
+ }
+ else {
+ code = 400;
+ buffer_flush(w->response.data);
+ buffer_strcat(w->response.data, "Graph name?\r\n");
+ }
+ }
+ else if(hash == hash_list && strcmp(tok, "list") == 0) {
+ // OLD API
+ code = 200;
+
+ debug(D_WEB_CLIENT_ACCESS, "%llu: Sending list of RRD_STATS...", w->id);
+
+ buffer_flush(w->response.data);
+ RRDSET *st = localhost.rrdset_root;
+
+ for ( ; st ; st = st->next )
+ buffer_sprintf(w->response.data, "%s\n", st->name);
+ }
+ else if(hash == hash_all_json && strcmp(tok, "all.json") == 0) {
+ // OLD API
+ code = 200;
+ debug(D_WEB_CLIENT_ACCESS, "%llu: Sending JSON list of all monitors of RRD_STATS...", w->id);
+
+ w->response.data->contenttype = CT_APPLICATION_JSON;
+ buffer_flush(w->response.data);
+ rrd_stats_all_json(w->response.data);
+ }
#ifdef NETDATA_INTERNAL_CHECKS
- else if(strcmp(tok, "exit") == 0) {
- code = 200;
- w->response.data->contenttype = CT_TEXT_PLAIN;
- buffer_flush(w->response.data);
-
- if(!netdata_exit)
- buffer_strcat(w->response.data, "ok, will do...");
- else
- buffer_strcat(w->response.data, "I am doing it already");
-
- netdata_exit = 1;
- }
- else if(strcmp(tok, "debug") == 0) {
- buffer_flush(w->response.data);
-
- // get the name of the data to show
- tok = mystrsep(&url, "/?&");
- if(tok && *tok) {
- debug(D_WEB_CLIENT, "%llu: Searching for RRD data with name '%s'.", w->id, tok);
-
- // do we have such a data set?
- RRDSET *st = rrdset_find_byname(tok);
- if(!st) st = rrdset_find(tok);
- if(!st) {
- code = 404;
- buffer_sprintf(w->response.data, "Chart %s is not found.\r\n", tok);
- debug(D_WEB_CLIENT_ACCESS, "%llu: %s is not found.", w->id, tok);
- }
- else {
- code = 200;
- debug_flags |= D_RRD_STATS;
- st->debug = !st->debug;
- buffer_sprintf(w->response.data, "Chart %s has now debug %s.\r\n", tok, st->debug?"enabled":"disabled");
- debug(D_WEB_CLIENT_ACCESS, "%llu: debug for %s is %s.", w->id, tok, st->debug?"enabled":"disabled");
- }
- }
- else {
- code = 500;
- buffer_flush(w->response.data);
- buffer_strcat(w->response.data, "debug which chart?\r\n");
- }
- }
- else if(strcmp(tok, "mirror") == 0) {
- code = 200;
-
- debug(D_WEB_CLIENT_ACCESS, "%llu: Mirroring...", w->id);
-
- // replace the zero bytes with spaces
- buffer_char_replace(w->response.data, '\0', ' ');
-
- // just leave the buffer as is
- // it will be copied back to the client
- }
-#endif /* NETDATA_INTERNAL_CHECKS */
- else {
- char filename[FILENAME_MAX+1];
- url = filename;
- strncpyz(filename, w->last_url, FILENAME_MAX);
- tok = mystrsep(&url, "?");
- buffer_flush(w->response.data);
- code = mysendfile(w, (tok && *tok)?tok:"/");
- }
- }
- else {
- char filename[FILENAME_MAX+1];
- url = filename;
- strncpyz(filename, w->last_url, FILENAME_MAX);
- tok = mystrsep(&url, "?");
- buffer_flush(w->response.data);
- code = mysendfile(w, (tok && *tok)?tok:"/");
- }
- }
- }
-
- gettimeofday(&w->tv_ready, NULL);
- w->response.data->date = time(NULL);
- w->response.sent = 0;
- w->response.code = code;
-
- // prepare the HTTP response header
- debug(D_WEB_CLIENT, "%llu: Generating HTTP header with response %d.", w->id, code);
-
- char *content_type_string;
- switch(w->response.data->contenttype) {
- case CT_TEXT_HTML:
- content_type_string = "text/html; charset=utf-8";
- break;
-
- case CT_APPLICATION_XML:
- content_type_string = "application/xml; charset=utf-8";
- break;
-
- case CT_APPLICATION_JSON:
- content_type_string = "application/json; charset=utf-8";
- break;
-
- case CT_APPLICATION_X_JAVASCRIPT:
- content_type_string = "application/x-javascript; charset=utf-8";
- break;
-
- case CT_TEXT_CSS:
- content_type_string = "text/css; charset=utf-8";
- break;
-
- case CT_TEXT_XML:
- content_type_string = "text/xml; charset=utf-8";
- break;
-
- case CT_TEXT_XSL:
- content_type_string = "text/xsl; charset=utf-8";
- break;
-
- case CT_APPLICATION_OCTET_STREAM:
- content_type_string = "application/octet-stream";
- break;
-
- case CT_IMAGE_SVG_XML:
- content_type_string = "image/svg+xml";
- break;
-
- case CT_APPLICATION_X_FONT_TRUETYPE:
- content_type_string = "application/x-font-truetype";
- break;
-
- case CT_APPLICATION_X_FONT_OPENTYPE:
- content_type_string = "application/x-font-opentype";
- break;
-
- case CT_APPLICATION_FONT_WOFF:
- content_type_string = "application/font-woff";
- break;
-
- case CT_APPLICATION_FONT_WOFF2:
- content_type_string = "application/font-woff2";
- break;
-
- case CT_APPLICATION_VND_MS_FONTOBJ:
- content_type_string = "application/vnd.ms-fontobject";
- break;
-
- case CT_IMAGE_PNG:
- content_type_string = "image/png";
- break;
-
- case CT_IMAGE_JPG:
- content_type_string = "image/jpeg";
- break;
-
- case CT_IMAGE_GIF:
- content_type_string = "image/gif";
- break;
-
- case CT_IMAGE_XICON:
- content_type_string = "image/x-icon";
- break;
-
- case CT_IMAGE_BMP:
- content_type_string = "image/bmp";
- break;
-
- case CT_IMAGE_ICNS:
- content_type_string = "image/icns";
- break;
-
- default:
- case CT_TEXT_PLAIN:
- content_type_string = "text/plain; charset=utf-8";
- break;
- }
-
- char *code_msg;
- switch(code) {
- case 200:
- code_msg = "OK";
- break;
-
- case 307:
- code_msg = "Temporary Redirect";
- break;
-
- case 400:
- code_msg = "Bad Request";
- break;
-
- case 403:
- code_msg = "Forbidden";
- break;
-
- case 404:
- code_msg = "Not Found";
- break;
-
- case 412:
- code_msg = "Preconditions Failed";
- break;
-
- default:
- code_msg = "Internal Server Error";
- break;
- }
-
- char date[100];
- struct tm tmbuf, *tm = gmtime_r(&w->response.data->date, &tmbuf);
- strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %Z", tm);
-
- buffer_sprintf(w->response.header_output,
- "HTTP/1.1 %d %s\r\n"
- "Connection: %s\r\n"
- "Server: NetData Embedded HTTP Server\r\n"
- "Access-Control-Allow-Origin: %s\r\n"
- "Access-Control-Allow-Credentials: true\r\n"
- "Content-Type: %s\r\n"
- "Date: %s\r\n"
- , code, code_msg
- , w->keepalive?"keep-alive":"close"
- , w->origin
- , content_type_string
- , date
- );
-
- if(w->cookie1[0]) {
- buffer_sprintf(w->response.header_output,
- "Set-Cookie: %s\r\n",
- w->cookie1);
- }
-
- if(w->cookie2[0]) {
- buffer_sprintf(w->response.header_output,
- "Set-Cookie: %s\r\n",
- w->cookie2);
- }
-
- if(w->mode == WEB_CLIENT_MODE_OPTIONS) {
- buffer_strcat(w->response.header_output,
- "Access-Control-Allow-Methods: GET, OPTIONS\r\n"
- "Access-Control-Allow-Headers: accept, x-requested-with, origin, content-type, cookie\r\n"
- "Access-Control-Max-Age: 1209600\r\n" // 86400 * 14
- );
- }
-
- if(buffer_strlen(w->response.header))
- buffer_strcat(w->response.header_output, buffer_tostring(w->response.header));
-
- if(w->mode == WEB_CLIENT_MODE_NORMAL && (w->response.data->options & WB_CONTENT_NO_CACHEABLE)) {
- buffer_sprintf(w->response.header_output,
- "Expires: %s\r\n"
- "Cache-Control: no-cache\r\n"
- , date);
- }
- else if(w->mode != WEB_CLIENT_MODE_OPTIONS) {
- char edate[100];
- time_t et = w->response.data->date + (86400 * 14);
- struct tm etmbuf, *etm = gmtime_r(&et, &etmbuf);
- strftime(edate, sizeof(edate), "%a, %d %b %Y %H:%M:%S %Z", etm);
-
- buffer_sprintf(w->response.header_output,
- "Expires: %s\r\n"
- "Cache-Control: public\r\n"
- , edate);
- }
-
- // if we know the content length, put it
- if(!w->response.zoutput && (w->response.data->len || w->response.rlen))
- buffer_sprintf(w->response.header_output,
- "Content-Length: %ld\r\n"
- , w->response.data->len? w->response.data->len: w->response.rlen
- );
- else if(!w->response.zoutput)
- w->keepalive = 0; // content-length is required for keep-alive
-
- if(w->response.zoutput) {
- buffer_strcat(w->response.header_output,
- "Content-Encoding: gzip\r\n"
- "Transfer-Encoding: chunked\r\n"
- );
- }
-
- buffer_strcat(w->response.header_output, "\r\n");
-
- // disable TCP_NODELAY, to buffer the header
- int flag = 0;
- if(setsockopt(w->ofd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)) != 0)
- error("%llu: failed to disable TCP_NODELAY on socket.", w->id);
-
- // sent the HTTP header
- debug(D_WEB_DATA, "%llu: Sending response HTTP header of size %d: '%s'"
- , w->id
- , buffer_strlen(w->response.header_output)
- , buffer_tostring(w->response.header_output)
- );
-
- bytes = send(w->ofd, buffer_tostring(w->response.header_output), buffer_strlen(w->response.header_output), 0);
- if(bytes != (ssize_t) buffer_strlen(w->response.header_output))
- error("%llu: HTTP Header failed to be sent (I sent %d bytes but the system sent %d bytes)."
- , w->id
- , buffer_strlen(w->response.header_output)
- , bytes);
- else {
- global_statistics_lock();
- global_statistics.bytes_sent += bytes;
- global_statistics_unlock();
- }
-
- // enable TCP_NODELAY, to send all data immediately at the next send()
- flag = 1;
- if(setsockopt(w->ofd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)) != 0) error("%llu: failed to enable TCP_NODELAY on socket.", w->id);
-
- // enable sending immediately if we have data
- if(w->response.data->len) w->wait_send = 1;
- else w->wait_send = 0;
-
- // pretty logging
- switch(w->mode) {
- case WEB_CLIENT_MODE_OPTIONS:
- debug(D_WEB_CLIENT, "%llu: Done preparing the OPTIONS response. Sending data (%d bytes) to client.", w->id, w->response.data->len);
- break;
-
- case WEB_CLIENT_MODE_NORMAL:
- debug(D_WEB_CLIENT, "%llu: Done preparing the response. Sending data (%d bytes) to client.", w->id, w->response.data->len);
- break;
-
- case WEB_CLIENT_MODE_FILECOPY:
- if(w->response.rlen) {
- debug(D_WEB_CLIENT, "%llu: Done preparing the response. Will be sending data file of %d bytes to client.", w->id, w->response.rlen);
- w->wait_receive = 1;
-
- /*
- // utilize the kernel sendfile() for copying the file to the socket.
- // this block of code can be commented, without anything missing.
- // when it is commented, the program will copy the data using async I/O.
- {
- long len = sendfile(w->ofd, w->ifd, NULL, w->response.data->rbytes);
- if(len != w->response.data->rbytes) error("%llu: sendfile() should copy %ld bytes, but copied %ld. Falling back to manual copy.", w->id, w->response.data->rbytes, len);
- else web_client_reset(w);
- }
- */
- }
- else
- debug(D_WEB_CLIENT, "%llu: Done preparing the response. Will be sending an unknown amount of bytes to client.", w->id);
- break;
-
- default:
- fatal("%llu: Unknown client mode %d.", w->id, w->mode);
- break;
- }
+ else if(hash == hash_exit && strcmp(tok, "exit") == 0) {
+ code = 200;
+ w->response.data->contenttype = CT_TEXT_PLAIN;
+ buffer_flush(w->response.data);
+
+ if(!netdata_exit)
+ buffer_strcat(w->response.data, "ok, will do...");
+ else
+ buffer_strcat(w->response.data, "I am doing it already");
+
+ error("web request to exit received.");
+ netdata_cleanup_and_exit(0);
+ netdata_exit = 1;
+ }
+ else if(hash == hash_debug && strcmp(tok, "debug") == 0) {
+ buffer_flush(w->response.data);
+
+ // get the name of the data to show
+ tok = mystrsep(&url, "/?&");
+ if(tok && *tok) {
+ debug(D_WEB_CLIENT, "%llu: Searching for RRD data with name '%s'.", w->id, tok);
+
+ // do we have such a data set?
+ RRDSET *st = rrdset_find_byname(tok);
+ if(!st) st = rrdset_find(tok);
+ if(!st) {
+ code = 404;
+ buffer_sprintf(w->response.data, "Chart %s is not found.\r\n", tok);
+ debug(D_WEB_CLIENT_ACCESS, "%llu: %s is not found.", w->id, tok);
+ }
+ else {
+ code = 200;
+ debug_flags |= D_RRD_STATS;
+ st->debug = !st->debug;
+ buffer_sprintf(w->response.data, "Chart %s has now debug %s.\r\n", tok, st->debug?"enabled":"disabled");
+ debug(D_WEB_CLIENT_ACCESS, "%llu: debug for %s is %s.", w->id, tok, st->debug?"enabled":"disabled");
+ }
+ }
+ else {
+ code = 500;
+ buffer_flush(w->response.data);
+ buffer_strcat(w->response.data, "debug which chart?\r\n");
+ }
+ }
+ else if(hash == hash_mirror && strcmp(tok, "mirror") == 0) {
+ code = 200;
+
+ debug(D_WEB_CLIENT_ACCESS, "%llu: Mirroring...", w->id);
+
+ // replace the zero bytes with spaces
+ buffer_char_replace(w->response.data, '\0', ' ');
+
+ // just leave the buffer as is
+ // it will be copied back to the client
+ }
+#endif /* NETDATA_INTERNAL_CHECKS */
+ else {
+ char filename[FILENAME_MAX+1];
+ url = filename;
+ strncpyz(filename, w->last_url, FILENAME_MAX);
+ tok = mystrsep(&url, "?");
+ buffer_flush(w->response.data);
+ code = mysendfile(w, (tok && *tok)?tok:"/");
+ }
+ }
+ else {
+ char filename[FILENAME_MAX+1];
+ url = filename;
+ strncpyz(filename, w->last_url, FILENAME_MAX);
+ tok = mystrsep(&url, "?");
+ buffer_flush(w->response.data);
+ code = mysendfile(w, (tok && *tok)?tok:"/");
+ }
+ }
+ }
+
+ gettimeofday(&w->tv_ready, NULL);
+ w->response.data->date = time(NULL);
+ w->response.sent = 0;
+ w->response.code = code;
+
+ // prepare the HTTP response header
+ debug(D_WEB_CLIENT, "%llu: Generating HTTP header with response %d.", w->id, code);
+
+ const char *content_type_string = web_content_type_to_string(w->response.data->contenttype);
+ const char *code_msg = web_response_code_to_string(code);
+
+ char date[32];
+ struct tm tmbuf, *tm = gmtime_r(&w->response.data->date, &tmbuf);
+ strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %Z", tm);
+
+ buffer_sprintf(w->response.header_output,
+ "HTTP/1.1 %d %s\r\n"
+ "Connection: %s\r\n"
+ "Server: NetData Embedded HTTP Server\r\n"
+ "Access-Control-Allow-Origin: %s\r\n"
+ "Access-Control-Allow-Credentials: true\r\n"
+ "Content-Type: %s\r\n"
+ "Date: %s\r\n"
+ , code, code_msg
+ , w->keepalive?"keep-alive":"close"
+ , w->origin
+ , content_type_string
+ , date
+ );
+
+ if(w->cookie1[0] || w->cookie2[0]) {
+ if(w->cookie1[0]) {
+ buffer_sprintf(w->response.header_output,
+ "Set-Cookie: %s\r\n",
+ w->cookie1);
+ }
+
+ if(w->cookie2[0]) {
+ buffer_sprintf(w->response.header_output,
+ "Set-Cookie: %s\r\n",
+ w->cookie2);
+ }
+
+ if(web_donotrack_comply)
+ buffer_sprintf(w->response.header_output,
+ "Tk: T;cookies\r\n");
+ }
+ else {
+ if(web_donotrack_comply) {
+ if(w->tracking_required)
+ buffer_sprintf(w->response.header_output,
+ "Tk: T;cookies\r\n");
+ else
+ buffer_sprintf(w->response.header_output,
+ "Tk: N\r\n");
+ }
+ }
+
+ if(w->mode == WEB_CLIENT_MODE_OPTIONS) {
+ buffer_strcat(w->response.header_output,
+ "Access-Control-Allow-Methods: GET, OPTIONS\r\n"
+ "Access-Control-Allow-Headers: accept, x-requested-with, origin, content-type, cookie\r\n"
+ "Access-Control-Max-Age: 1209600\r\n" // 86400 * 14
+ );
+ }
+
+ if(buffer_strlen(w->response.header))
+ buffer_strcat(w->response.header_output, buffer_tostring(w->response.header));
+
+ if(w->mode == WEB_CLIENT_MODE_NORMAL && (w->response.data->options & WB_CONTENT_NO_CACHEABLE)) {
+ buffer_sprintf(w->response.header_output,
+ "Expires: %s\r\n"
+ "Cache-Control: no-cache\r\n"
+ , date);
+ }
+ else if(w->mode != WEB_CLIENT_MODE_OPTIONS) {
+ char edate[32];
+ time_t et = w->response.data->date + (86400 * 14);
+ struct tm etmbuf, *etm = gmtime_r(&et, &etmbuf);
+ strftime(edate, sizeof(edate), "%a, %d %b %Y %H:%M:%S %Z", etm);
+
+ buffer_sprintf(w->response.header_output,
+ "Expires: %s\r\n"
+ "Cache-Control: public\r\n"
+ , edate);
+ }
+
+ // if we know the content length, put it
+ if(!w->response.zoutput && (w->response.data->len || w->response.rlen))
+ buffer_sprintf(w->response.header_output,
+ "Content-Length: %zu\r\n"
+ , w->response.data->len? w->response.data->len: w->response.rlen
+ );
+ else if(!w->response.zoutput)
+ w->keepalive = 0; // content-length is required for keep-alive
+
+ if(w->response.zoutput) {
+ buffer_strcat(w->response.header_output,
+ "Content-Encoding: gzip\r\n"
+ "Transfer-Encoding: chunked\r\n"
+ );
+ }
+
+ buffer_strcat(w->response.header_output, "\r\n");
+
+ // sent the HTTP header
+ debug(D_WEB_DATA, "%llu: Sending response HTTP header of size %zu: '%s'"
+ , w->id
+ , buffer_strlen(w->response.header_output)
+ , buffer_tostring(w->response.header_output)
+ );
+
+ web_client_crock_socket(w);
+
+ bytes = send(w->ofd, buffer_tostring(w->response.header_output), buffer_strlen(w->response.header_output), 0);
+ if(bytes != (ssize_t) buffer_strlen(w->response.header_output)) {
+ if(bytes > 0)
+ w->stats_sent_bytes += bytes;
+
+ debug(D_WEB_CLIENT, "%llu: HTTP Header failed to be sent (I sent %zu bytes but the system sent %zd bytes). Closing web client."
+ , w->id
+ , buffer_strlen(w->response.header_output)
+ , bytes);
+
+ WEB_CLIENT_IS_DEAD(w);
+ return;
+ }
+ else
+ w->stats_sent_bytes += bytes;
+
+ // enable sending immediately if we have data
+ if(w->response.data->len) w->wait_send = 1;
+ else w->wait_send = 0;
+
+ // pretty logging
+ switch(w->mode) {
+ case WEB_CLIENT_MODE_OPTIONS:
+ debug(D_WEB_CLIENT, "%llu: Done preparing the OPTIONS response. Sending data (%zu bytes) to client.", w->id, w->response.data->len);
+ break;
+
+ case WEB_CLIENT_MODE_NORMAL:
+ debug(D_WEB_CLIENT, "%llu: Done preparing the response. Sending data (%zu bytes) to client.", w->id, w->response.data->len);
+ break;
+
+ case WEB_CLIENT_MODE_FILECOPY:
+ if(w->response.rlen) {
+ debug(D_WEB_CLIENT, "%llu: Done preparing the response. Will be sending data file of %zu bytes to client.", w->id, w->response.rlen);
+ w->wait_receive = 1;
+
+ /*
+ // utilize the kernel sendfile() for copying the file to the socket.
+ // this block of code can be commented, without anything missing.
+ // when it is commented, the program will copy the data using async I/O.
+ {
+ long len = sendfile(w->ofd, w->ifd, NULL, w->response.data->rbytes);
+ if(len != w->response.data->rbytes)
+ error("%llu: sendfile() should copy %ld bytes, but copied %ld. Falling back to manual copy.", w->id, w->response.data->rbytes, len);
+ else
+ web_client_reset(w);
+ }
+ */
+ }
+ else
+ debug(D_WEB_CLIENT, "%llu: Done preparing the response. Will be sending an unknown amount of bytes to client.", w->id);
+ break;
+
+ default:
+ fatal("%llu: Unknown client mode %d.", w->id, w->mode);
+ break;
+ }
}
-long web_client_send_chunk_header(struct web_client *w, long len)
+ssize_t web_client_send_chunk_header(struct web_client *w, size_t len)
{
- debug(D_DEFLATE, "%llu: OPEN CHUNK of %d bytes (hex: %x).", w->id, len, len);
- char buf[1024];
- sprintf(buf, "%lX\r\n", len);
- ssize_t bytes = send(w->ofd, buf, strlen(buf), MSG_DONTWAIT);
-
- if(bytes > 0) debug(D_DEFLATE, "%llu: Sent chunk header %d bytes.", w->id, bytes);
- else if(bytes == 0) debug(D_DEFLATE, "%llu: Did not send chunk header to the client.", w->id);
- else debug(D_DEFLATE, "%llu: Failed to send chunk header to client.", w->id);
-
- return bytes;
+ debug(D_DEFLATE, "%llu: OPEN CHUNK of %zu bytes (hex: %zx).", w->id, len, len);
+ char buf[24];
+ sprintf(buf, "%zX\r\n", len);
+
+ ssize_t bytes = send(w->ofd, buf, strlen(buf), 0);
+ if(bytes > 0) {
+ debug(D_DEFLATE, "%llu: Sent chunk header %zd bytes.", w->id, bytes);
+ w->stats_sent_bytes += bytes;
+ }
+
+ else if(bytes == 0) {
+ debug(D_WEB_CLIENT, "%llu: Did not send chunk header to the client.", w->id);
+ WEB_CLIENT_IS_DEAD(w);
+ }
+ else {
+ debug(D_WEB_CLIENT, "%llu: Failed to send chunk header to client.", w->id);
+ WEB_CLIENT_IS_DEAD(w);
+ }
+
+ return bytes;
}
-long web_client_send_chunk_close(struct web_client *w)
+ssize_t web_client_send_chunk_close(struct web_client *w)
{
- //debug(D_DEFLATE, "%llu: CLOSE CHUNK.", w->id);
-
- ssize_t bytes = send(w->ofd, "\r\n", 2, MSG_DONTWAIT);
-
- if(bytes > 0) debug(D_DEFLATE, "%llu: Sent chunk suffix %d bytes.", w->id, bytes);
- else if(bytes == 0) debug(D_DEFLATE, "%llu: Did not send chunk suffix to the client.", w->id);
- else debug(D_DEFLATE, "%llu: Failed to send chunk suffix to client.", w->id);
-
- return bytes;
+ //debug(D_DEFLATE, "%llu: CLOSE CHUNK.", w->id);
+
+ ssize_t bytes = send(w->ofd, "\r\n", 2, 0);
+ if(bytes > 0) {
+ debug(D_DEFLATE, "%llu: Sent chunk suffix %zd bytes.", w->id, bytes);
+ w->stats_sent_bytes += bytes;
+ }
+
+ else if(bytes == 0) {
+ debug(D_WEB_CLIENT, "%llu: Did not send chunk suffix to the client.", w->id);
+ WEB_CLIENT_IS_DEAD(w);
+ }
+ else {
+ debug(D_WEB_CLIENT, "%llu: Failed to send chunk suffix to client.", w->id);
+ WEB_CLIENT_IS_DEAD(w);
+ }
+
+ return bytes;
}
-long web_client_send_chunk_finalize(struct web_client *w)
+ssize_t web_client_send_chunk_finalize(struct web_client *w)
{
- //debug(D_DEFLATE, "%llu: FINALIZE CHUNK.", w->id);
-
- ssize_t bytes = send(w->ofd, "\r\n0\r\n\r\n", 7, MSG_DONTWAIT);
-
- if(bytes > 0) debug(D_DEFLATE, "%llu: Sent chunk suffix %d bytes.", w->id, bytes);
- else if(bytes == 0) debug(D_DEFLATE, "%llu: Did not send chunk suffix to the client.", w->id);
- else debug(D_DEFLATE, "%llu: Failed to send chunk suffix to client.", w->id);
-
- return bytes;
+ //debug(D_DEFLATE, "%llu: FINALIZE CHUNK.", w->id);
+
+ ssize_t bytes = send(w->ofd, "\r\n0\r\n\r\n", 7, 0);
+ if(bytes > 0) {
+ debug(D_DEFLATE, "%llu: Sent chunk suffix %zd bytes.", w->id, bytes);
+ w->stats_sent_bytes += bytes;
+ }
+
+ else if(bytes == 0) {
+ debug(D_WEB_CLIENT, "%llu: Did not send chunk finalize suffix to the client.", w->id);
+ WEB_CLIENT_IS_DEAD(w);
+ }
+ else {
+ debug(D_WEB_CLIENT, "%llu: Failed to send chunk finalize suffix to client.", w->id);
+ WEB_CLIENT_IS_DEAD(w);
+ }
+
+ return bytes;
}
#ifdef NETDATA_WITH_ZLIB
-long web_client_send_deflate(struct web_client *w)
+ssize_t web_client_send_deflate(struct web_client *w)
{
- long len = 0, t = 0;
-
- // when using compression,
- // w->response.sent is the amount of bytes passed through compression
-
- debug(D_DEFLATE, "%llu: web_client_send_deflate(): w->response.data->len = %d, w->response.sent = %d, w->response.zhave = %d, w->response.zsent = %d, w->response.zstream.avail_in = %d, w->response.zstream.avail_out = %d, w->response.zstream.total_in = %d, w->response.zstream.total_out = %d.", w->id, w->response.data->len, w->response.sent, w->response.zhave, w->response.zsent, w->response.zstream.avail_in, w->response.zstream.avail_out, w->response.zstream.total_in, w->response.zstream.total_out);
-
- if(w->response.data->len - w->response.sent == 0 && w->response.zstream.avail_in == 0 && w->response.zhave == w->response.zsent && w->response.zstream.avail_out != 0) {
- // there is nothing to send
-
- debug(D_WEB_CLIENT, "%llu: Out of output data.", w->id);
-
- // finalize the chunk
- if(w->response.sent != 0)
- t += web_client_send_chunk_finalize(w);
-
- // there can be two cases for this
- // A. we have done everything
- // B. we temporarily have nothing to send, waiting for the buffer to be filled by ifd
-
- if(w->mode == WEB_CLIENT_MODE_FILECOPY && w->wait_receive && w->ifd != w->ofd && w->response.rlen && w->response.rlen > w->response.data->len) {
- // we have to wait, more data will come
- debug(D_WEB_CLIENT, "%llu: Waiting for more data to become available.", w->id);
- w->wait_send = 0;
- return(0);
- }
-
- if(w->keepalive == 0) {
- debug(D_WEB_CLIENT, "%llu: Closing (keep-alive is not enabled). %ld bytes sent.", w->id, w->response.sent);
- errno = 0;
- return(-1);
- }
-
- // reset the client
- web_client_reset(w);
- debug(D_WEB_CLIENT, "%llu: Done sending all data on socket. Waiting for next request on the same socket.", w->id);
- return(0);
- }
-
- if(w->response.zhave == w->response.zsent) {
- // compress more input data
-
- // close the previous open chunk
- if(w->response.sent != 0) t += web_client_send_chunk_close(w);
-
- debug(D_DEFLATE, "%llu: Compressing %d new bytes starting from %d (and %d left behind).", w->id, (w->response.data->len - w->response.sent), w->response.sent, w->response.zstream.avail_in);
-
- // give the compressor all the data not passed through the compressor yet
- if(w->response.data->len > w->response.sent) {
-#ifdef NETDATA_INTERNAL_CHECKS
- if((long)w->response.sent - (long)w->response.zstream.avail_in < 0)
- error("internal error: avail_in is corrupted.");
-#endif
- w->response.zstream.next_in = (Bytef *)&w->response.data->buffer[w->response.sent - w->response.zstream.avail_in];
- w->response.zstream.avail_in += (uInt) (w->response.data->len - w->response.sent);
- }
-
- // reset the compressor output buffer
- w->response.zstream.next_out = w->response.zbuffer;
- w->response.zstream.avail_out = ZLIB_CHUNK;
-
- // ask for FINISH if we have all the input
- int flush = Z_SYNC_FLUSH;
- if(w->mode == WEB_CLIENT_MODE_NORMAL
- || (w->mode == WEB_CLIENT_MODE_FILECOPY && !w->wait_receive && w->response.data->len == w->response.rlen)) {
- flush = Z_FINISH;
- debug(D_DEFLATE, "%llu: Requesting Z_FINISH, if possible.", w->id);
- }
- else {
- debug(D_DEFLATE, "%llu: Requesting Z_SYNC_FLUSH.", w->id);
- }
-
- // compress
- if(deflate(&w->response.zstream, flush) == Z_STREAM_ERROR) {
- error("%llu: Compression failed. Closing down client.", w->id);
- web_client_reset(w);
- return(-1);
- }
-
- w->response.zhave = ZLIB_CHUNK - w->response.zstream.avail_out;
- w->response.zsent = 0;
-
- // keep track of the bytes passed through the compressor
- w->response.sent = w->response.data->len;
-
- debug(D_DEFLATE, "%llu: Compression produced %d bytes.", w->id, w->response.zhave);
-
- // open a new chunk
- t += web_client_send_chunk_header(w, w->response.zhave);
- }
-
- debug(D_WEB_CLIENT, "%llu: Sending %d bytes of data (+%d of chunk header).", w->id, w->response.zhave - w->response.zsent, t);
-
- len = send(w->ofd, &w->response.zbuffer[w->response.zsent], (size_t) (w->response.zhave - w->response.zsent), MSG_DONTWAIT);
- if(len > 0) {
- w->response.zsent += len;
- if(t > 0) len += t;
- debug(D_WEB_CLIENT, "%llu: Sent %d bytes.", w->id, len);
- }
- else if(len == 0) debug(D_WEB_CLIENT, "%llu: Did not send any bytes to the client (zhave = %ld, zsent = %ld, need to send = %ld).", w->id, w->response.zhave, w->response.zsent, w->response.zhave - w->response.zsent);
- else debug(D_WEB_CLIENT, "%llu: Failed to send data to client. Reason: %s", w->id, strerror(errno));
-
- return(len);
+ ssize_t len = 0, t = 0;
+
+ // when using compression,
+ // w->response.sent is the amount of bytes passed through compression
+
+ debug(D_DEFLATE, "%llu: web_client_send_deflate(): w->response.data->len = %zu, w->response.sent = %zu, w->response.zhave = %zu, w->response.zsent = %zu, w->response.zstream.avail_in = %u, w->response.zstream.avail_out = %u, w->response.zstream.total_in = %lu, w->response.zstream.total_out = %lu.",
+ w->id, w->response.data->len, w->response.sent, w->response.zhave, w->response.zsent, w->response.zstream.avail_in, w->response.zstream.avail_out, w->response.zstream.total_in, w->response.zstream.total_out);
+
+ if(w->response.data->len - w->response.sent == 0 && w->response.zstream.avail_in == 0 && w->response.zhave == w->response.zsent && w->response.zstream.avail_out != 0) {
+ // there is nothing to send
+
+ debug(D_WEB_CLIENT, "%llu: Out of output data.", w->id);
+
+ // finalize the chunk
+ if(w->response.sent != 0) {
+ t = web_client_send_chunk_finalize(w);
+ if(t < 0) return t;
+ }
+
+ if(w->mode == WEB_CLIENT_MODE_FILECOPY && w->wait_receive && w->response.rlen && w->response.rlen > w->response.data->len) {
+ // we have to wait, more data will come
+ debug(D_WEB_CLIENT, "%llu: Waiting for more data to become available.", w->id);
+ w->wait_send = 0;
+ return t;
+ }
+
+ if(unlikely(!w->keepalive)) {
+ debug(D_WEB_CLIENT, "%llu: Closing (keep-alive is not enabled). %zu bytes sent.", w->id, w->response.sent);
+ WEB_CLIENT_IS_DEAD(w);
+ return t;
+ }
+
+ // reset the client
+ web_client_reset(w);
+ debug(D_WEB_CLIENT, "%llu: Done sending all data on socket.", w->id);
+ return t;
+ }
+
+ if(w->response.zhave == w->response.zsent) {
+ // compress more input data
+
+ // close the previous open chunk
+ if(w->response.sent != 0) {
+ t = web_client_send_chunk_close(w);
+ if(t < 0) return t;
+ }
+
+ debug(D_DEFLATE, "%llu: Compressing %zu new bytes starting from %zu (and %u left behind).", w->id, (w->response.data->len - w->response.sent), w->response.sent, w->response.zstream.avail_in);
+
+ // give the compressor all the data not passed through the compressor yet
+ if(w->response.data->len > w->response.sent) {
+ w->response.zstream.next_in = (Bytef *)&w->response.data->buffer[w->response.sent - w->response.zstream.avail_in];
+ w->response.zstream.avail_in += (uInt) (w->response.data->len - w->response.sent);
+ }
+
+ // reset the compressor output buffer
+ w->response.zstream.next_out = w->response.zbuffer;
+ w->response.zstream.avail_out = ZLIB_CHUNK;
+
+ // ask for FINISH if we have all the input
+ int flush = Z_SYNC_FLUSH;
+ if(w->mode == WEB_CLIENT_MODE_NORMAL
+ || (w->mode == WEB_CLIENT_MODE_FILECOPY && !w->wait_receive && w->response.data->len == w->response.rlen)) {
+ flush = Z_FINISH;
+ debug(D_DEFLATE, "%llu: Requesting Z_FINISH, if possible.", w->id);
+ }
+ else {
+ debug(D_DEFLATE, "%llu: Requesting Z_SYNC_FLUSH.", w->id);
+ }
+
+ // compress
+ if(deflate(&w->response.zstream, flush) == Z_STREAM_ERROR) {
+ error("%llu: Compression failed. Closing down client.", w->id);
+ web_client_reset(w);
+ return(-1);
+ }
+
+ w->response.zhave = ZLIB_CHUNK - w->response.zstream.avail_out;
+ w->response.zsent = 0;
+
+ // keep track of the bytes passed through the compressor
+ w->response.sent = w->response.data->len;
+
+ debug(D_DEFLATE, "%llu: Compression produced %zu bytes.", w->id, w->response.zhave);
+
+ // open a new chunk
+ ssize_t t2 = web_client_send_chunk_header(w, w->response.zhave);
+ if(t2 < 0) return t2;
+ t += t2;
+ }
+
+ debug(D_WEB_CLIENT, "%llu: Sending %zu bytes of data (+%zd of chunk header).", w->id, w->response.zhave - w->response.zsent, t);
+
+ len = send(w->ofd, &w->response.zbuffer[w->response.zsent], (size_t) (w->response.zhave - w->response.zsent), MSG_DONTWAIT);
+ if(len > 0) {
+ w->stats_sent_bytes += len;
+ w->response.zsent += len;
+ len += t;
+ debug(D_WEB_CLIENT, "%llu: Sent %zd bytes.", w->id, len);
+ }
+ else if(len == 0) {
+ debug(D_WEB_CLIENT, "%llu: Did not send any bytes to the client (zhave = %zu, zsent = %zu, need to send = %zu).",
+ w->id, w->response.zhave, w->response.zsent, w->response.zhave - w->response.zsent);
+
+ WEB_CLIENT_IS_DEAD(w);
+ }
+ else {
+ debug(D_WEB_CLIENT, "%llu: Failed to send data to client.", w->id);
+ WEB_CLIENT_IS_DEAD(w);
+ }
+
+ return(len);
}
#endif // NETDATA_WITH_ZLIB
-long web_client_send(struct web_client *w)
-{
+ssize_t web_client_send(struct web_client *w) {
#ifdef NETDATA_WITH_ZLIB
- if(likely(w->response.zoutput)) return web_client_send_deflate(w);
+ if(likely(w->response.zoutput)) return web_client_send_deflate(w);
#endif // NETDATA_WITH_ZLIB
- long bytes;
-
- if(unlikely(w->response.data->len - w->response.sent == 0)) {
- // there is nothing to send
-
- debug(D_WEB_CLIENT, "%llu: Out of output data.", w->id);
-
- // there can be two cases for this
- // A. we have done everything
- // B. we temporarily have nothing to send, waiting for the buffer to be filled by ifd
-
- if(w->mode == WEB_CLIENT_MODE_FILECOPY && w->wait_receive && w->ifd != w->ofd && w->response.rlen && w->response.rlen > w->response.data->len) {
- // we have to wait, more data will come
- debug(D_WEB_CLIENT, "%llu: Waiting for more data to become available.", w->id);
- w->wait_send = 0;
- return(0);
- }
-
- if(unlikely(w->keepalive == 0)) {
- debug(D_WEB_CLIENT, "%llu: Closing (keep-alive is not enabled). %ld bytes sent.", w->id, w->response.sent);
- errno = 0;
- return(-1);
- }
-
- web_client_reset(w);
- debug(D_WEB_CLIENT, "%llu: Done sending all data on socket. Waiting for next request on the same socket.", w->id);
- return(0);
- }
-
- bytes = send(w->ofd, &w->response.data->buffer[w->response.sent], w->response.data->len - w->response.sent, MSG_DONTWAIT);
- if(likely(bytes > 0)) {
- w->response.sent += bytes;
- debug(D_WEB_CLIENT, "%llu: Sent %d bytes.", w->id, bytes);
- }
- else if(likely(bytes == 0)) debug(D_WEB_CLIENT, "%llu: Did not send any bytes to the client.", w->id);
- else debug(D_WEB_CLIENT, "%llu: Failed to send data to client.", w->id);
-
- return(bytes);
+ ssize_t bytes;
+
+ if(unlikely(w->response.data->len - w->response.sent == 0)) {
+ // there is nothing to send
+
+ debug(D_WEB_CLIENT, "%llu: Out of output data.", w->id);
+
+ // there can be two cases for this
+ // A. we have done everything
+ // B. we temporarily have nothing to send, waiting for the buffer to be filled by ifd
+
+ if(w->mode == WEB_CLIENT_MODE_FILECOPY && w->wait_receive && w->response.rlen && w->response.rlen > w->response.data->len) {
+ // we have to wait, more data will come
+ debug(D_WEB_CLIENT, "%llu: Waiting for more data to become available.", w->id);
+ w->wait_send = 0;
+ return 0;
+ }
+
+ if(unlikely(!w->keepalive)) {
+ debug(D_WEB_CLIENT, "%llu: Closing (keep-alive is not enabled). %zu bytes sent.", w->id, w->response.sent);
+ WEB_CLIENT_IS_DEAD(w);
+ return 0;
+ }
+
+ web_client_reset(w);
+ debug(D_WEB_CLIENT, "%llu: Done sending all data on socket. Waiting for next request on the same socket.", w->id);
+ return 0;
+ }
+
+ bytes = send(w->ofd, &w->response.data->buffer[w->response.sent], w->response.data->len - w->response.sent, MSG_DONTWAIT);
+ if(likely(bytes > 0)) {
+ w->stats_sent_bytes += bytes;
+ w->response.sent += bytes;
+ debug(D_WEB_CLIENT, "%llu: Sent %zd bytes.", w->id, bytes);
+ }
+ else if(likely(bytes == 0)) {
+ debug(D_WEB_CLIENT, "%llu: Did not send any bytes to the client.", w->id);
+ WEB_CLIENT_IS_DEAD(w);
+ }
+ else {
+ debug(D_WEB_CLIENT, "%llu: Failed to send data to client.", w->id);
+ WEB_CLIENT_IS_DEAD(w);
+ }
+
+ return(bytes);
}
-long web_client_receive(struct web_client *w)
+ssize_t web_client_receive(struct web_client *w)
{
- // do we have any space for more data?
- buffer_need_bytes(w->response.data, WEB_REQUEST_LENGTH);
-
- long left = w->response.data->size - w->response.data->len;
- long bytes;
-
- if(unlikely(w->mode == WEB_CLIENT_MODE_FILECOPY))
- bytes = read(w->ifd, &w->response.data->buffer[w->response.data->len], (size_t) (left - 1));
- else
- bytes = recv(w->ifd, &w->response.data->buffer[w->response.data->len], (size_t) (left - 1), MSG_DONTWAIT);
-
- if(likely(bytes > 0)) {
- size_t old = w->response.data->len;
- w->response.data->len += bytes;
- w->response.data->buffer[w->response.data->len] = '\0';
-
- debug(D_WEB_CLIENT, "%llu: Received %d bytes.", w->id, bytes);
- debug(D_WEB_DATA, "%llu: Received data: '%s'.", w->id, &w->response.data->buffer[old]);
-
- if(w->mode == WEB_CLIENT_MODE_FILECOPY) {
- w->wait_send = 1;
- if(w->response.rlen && w->response.data->len >= w->response.rlen) w->wait_receive = 0;
- }
- }
- else if(likely(bytes == 0)) {
- debug(D_WEB_CLIENT, "%llu: Out of input data.", w->id);
-
- // if we cannot read, it means we have an error on input.
- // if however, we are copying a file from ifd to ofd, we should not return an error.
- // in this case, the error should be generated when the file has been sent to the client.
-
- if(w->mode == WEB_CLIENT_MODE_FILECOPY) {
- // we are copying data from ifd to ofd
- // let it finish copying...
- w->wait_receive = 0;
- debug(D_WEB_CLIENT, "%llu: Disabling input.", w->id);
- }
- else {
- bytes = -1;
- errno = 0;
- }
- }
-
- return(bytes);
+ // do we have any space for more data?
+ buffer_need_bytes(w->response.data, WEB_REQUEST_LENGTH);
+
+ ssize_t left = w->response.data->size - w->response.data->len;
+ ssize_t bytes;
+
+ if(unlikely(w->mode == WEB_CLIENT_MODE_FILECOPY))
+ bytes = read(w->ifd, &w->response.data->buffer[w->response.data->len], (size_t) (left - 1));
+ else
+ bytes = recv(w->ifd, &w->response.data->buffer[w->response.data->len], (size_t) (left - 1), MSG_DONTWAIT);
+
+ if(likely(bytes > 0)) {
+ if(w->mode != WEB_CLIENT_MODE_FILECOPY)
+ w->stats_received_bytes += bytes;
+
+ size_t old = w->response.data->len;
+ w->response.data->len += bytes;
+ w->response.data->buffer[w->response.data->len] = '\0';
+
+ debug(D_WEB_CLIENT, "%llu: Received %zd bytes.", w->id, bytes);
+ debug(D_WEB_DATA, "%llu: Received data: '%s'.", w->id, &w->response.data->buffer[old]);
+
+ if(w->mode == WEB_CLIENT_MODE_FILECOPY) {
+ w->wait_send = 1;
+
+ if(w->response.rlen && w->response.data->len >= w->response.rlen)
+ w->wait_receive = 0;
+ }
+ }
+ else if(likely(bytes == 0)) {
+ debug(D_WEB_CLIENT, "%llu: Out of input data.", w->id);
+
+ // if we cannot read, it means we have an error on input.
+ // if however, we are copying a file from ifd to ofd, we should not return an error.
+ // in this case, the error should be generated when the file has been sent to the client.
+
+ if(w->mode == WEB_CLIENT_MODE_FILECOPY) {
+ // we are copying data from ifd to ofd
+ // let it finish copying...
+ w->wait_receive = 0;
+
+ debug(D_WEB_CLIENT, "%llu: Read the whole file.", w->id);
+ if(w->ifd != w->ofd) close(w->ifd);
+ w->ifd = w->ofd;
+ }
+ else {
+ debug(D_WEB_CLIENT, "%llu: failed to receive data.", w->id);
+ WEB_CLIENT_IS_DEAD(w);
+ }
+ }
+ else {
+ debug(D_WEB_CLIENT, "%llu: receive data failed.", w->id);
+ WEB_CLIENT_IS_DEAD(w);
+ }
+
+ return(bytes);
}
@@ -2040,104 +2545,131 @@ long web_client_receive(struct web_client *w)
void *web_client_main(void *ptr)
{
- if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
- error("Cannot set pthread cancel type to DEFERRED.");
-
- if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
- error("Cannot set pthread cancel state to ENABLE.");
-
- struct timeval tv;
- struct web_client *w = ptr;
- int retval;
- fd_set ifds, ofds, efds;
- int fdmax = 0;
-
- log_access("%llu: %s port %s connected on thread task id %d", w->id, w->client_ip, w->client_port, gettid());
-
- for(;;) {
- FD_ZERO (&ifds);
- FD_ZERO (&ofds);
- FD_ZERO (&efds);
-
- FD_SET(w->ifd, &efds);
-
- if(w->ifd != w->ofd)
- FD_SET(w->ofd, &efds);
-
- if (w->wait_receive) {
- FD_SET(w->ifd, &ifds);
- if(w->ifd > fdmax) fdmax = w->ifd;
- }
-
- if (w->wait_send) {
- FD_SET(w->ofd, &ofds);
- if(w->ofd > fdmax) fdmax = w->ofd;
- }
-
- tv.tv_sec = web_client_timeout;
- tv.tv_usec = 0;
-
- debug(D_WEB_CLIENT, "%llu: Waiting socket async I/O for %s %s", w->id, w->wait_receive?"INPUT":"", w->wait_send?"OUTPUT":"");
- retval = select(fdmax+1, &ifds, &ofds, &efds, &tv);
-
- if(retval == -1) {
- debug(D_WEB_CLIENT_ACCESS, "%llu: LISTENER: select() failed.", w->id);
- continue;
- }
- else if(!retval) {
- // timeout
- debug(D_WEB_CLIENT_ACCESS, "%llu: LISTENER: timeout.", w->id);
- break;
- }
-
- if(FD_ISSET(w->ifd, &efds)) {
- debug(D_WEB_CLIENT_ACCESS, "%llu: Received error on input socket.", w->id);
- break;
- }
-
- if(FD_ISSET(w->ofd, &efds)) {
- debug(D_WEB_CLIENT_ACCESS, "%llu: Received error on output socket.", w->id);
- break;
- }
-
- if(w->wait_send && FD_ISSET(w->ofd, &ofds)) {
- long bytes;
- if((bytes = web_client_send(w)) < 0) {
- debug(D_WEB_CLIENT, "%llu: Cannot send data to client. Closing client.", w->id);
- errno = 0;
- break;
- }
-
- global_statistics_lock();
- global_statistics.bytes_sent += bytes;
- global_statistics_unlock();
- }
-
- if(w->wait_receive && FD_ISSET(w->ifd, &ifds)) {
- long bytes;
- if((bytes = web_client_receive(w)) < 0) {
- debug(D_WEB_CLIENT, "%llu: Cannot receive data from client. Closing client.", w->id);
- errno = 0;
- break;
- }
-
- if(w->mode == WEB_CLIENT_MODE_NORMAL) {
- debug(D_WEB_CLIENT, "%llu: Attempting to process received data (%ld bytes).", w->id, bytes);
- // info("%llu: Attempting to process received data (%ld bytes).", w->id, bytes);
- web_client_process(w);
- }
-
- global_statistics_lock();
- global_statistics.bytes_received += bytes;
- global_statistics_unlock();
- }
- }
-
- log_access("%llu: %s port %s disconnected from thread task id %d", w->id, w->client_ip, w->client_port, gettid());
- debug(D_WEB_CLIENT, "%llu: done...", w->id);
-
- web_client_reset(w);
- w->obsolete = 1;
-
- return NULL;
+ if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
+ error("Cannot set pthread cancel type to DEFERRED.");
+
+ if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
+ error("Cannot set pthread cancel state to ENABLE.");
+
+ struct web_client *w = ptr;
+ struct pollfd fds[2], *ifd, *ofd;
+ int retval, fdmax = 0, timeout;
+
+ log_access("%llu: %s port %s connected on thread task id %d", w->id, w->client_ip, w->client_port, gettid());
+
+ for(;;) {
+ if(unlikely(w->dead)) {
+ debug(D_WEB_CLIENT, "%llu: client is dead.", w->id);
+ break;
+ }
+ else if(unlikely(!w->wait_receive && !w->wait_send)) {
+ debug(D_WEB_CLIENT, "%llu: client is not set for neither receiving nor sending data.", w->id);
+ break;
+ }
+
+ if(unlikely(w->ifd < 0 || w->ofd < 0)) {
+ error("%llu: invalid file descriptor, ifd = %d, ofd = %d (required 0 <= fd", w->id, w->ifd, w->ofd);
+ break;
+ }
+
+ if(w->ifd == w->ofd) {
+ fds[0].fd = w->ifd;
+ fds[0].events = 0;
+ fds[0].revents = 0;
+
+ if(w->wait_receive) fds[0].events |= POLLIN;
+ if(w->wait_send) fds[0].events |= POLLOUT;
+
+ fds[1].fd = -1;
+ fds[1].events = 0;
+ fds[1].revents = 0;
+
+ ifd = ofd = &fds[0];
+
+ fdmax = 1;
+ }
+ else {
+ fds[0].fd = w->ifd;
+ fds[0].events = 0;
+ fds[0].revents = 0;
+ if(w->wait_receive) fds[0].events |= POLLIN;
+ ifd = &fds[0];
+
+ fds[1].fd = w->ofd;
+ fds[1].events = 0;
+ fds[1].revents = 0;
+ if(w->wait_send) fds[1].events |= POLLOUT;
+ ofd = &fds[1];
+
+ fdmax = 2;
+ }
+
+ debug(D_WEB_CLIENT, "%llu: Waiting socket async I/O for %s %s", w->id, w->wait_receive?"INPUT":"", w->wait_send?"OUTPUT":"");
+ errno = 0;
+ timeout = web_client_timeout * 1000;
+ retval = poll(fds, fdmax, timeout);
+
+ if(unlikely(retval == -1)) {
+ if(errno == EAGAIN || errno == EINTR) {
+ debug(D_WEB_CLIENT, "%llu: EAGAIN received.", w->id);
+ continue;
+ }
+
+ debug(D_WEB_CLIENT, "%llu: LISTENER: poll() failed (input fd = %d, output fd = %d). Closing client.", w->id, w->ifd, w->ofd);
+ break;
+ }
+ else if(unlikely(!retval)) {
+ debug(D_WEB_CLIENT, "%llu: Timeout while waiting socket async I/O for %s %s", w->id, w->wait_receive?"INPUT":"", w->wait_send?"OUTPUT":"");
+ break;
+ }
+
+ int used = 0;
+ if(w->wait_send && ofd->revents & POLLOUT) {
+ used++;
+ if(web_client_send(w) < 0) {
+ debug(D_WEB_CLIENT, "%llu: Cannot send data to client. Closing client.", w->id);
+ break;
+ }
+ }
+
+ if(w->wait_receive && (ifd->revents & POLLIN || ifd->revents & POLLPRI)) {
+ used++;
+ if(web_client_receive(w) < 0) {
+ debug(D_WEB_CLIENT, "%llu: Cannot receive data from client. Closing client.", w->id);
+ break;
+ }
+
+ if(w->mode == WEB_CLIENT_MODE_NORMAL) {
+ debug(D_WEB_CLIENT, "%llu: Attempting to process received data.", w->id);
+ web_client_process(w);
+ }
+ }
+
+ if(unlikely(!used)) {
+ debug(D_WEB_CLIENT_ACCESS, "%llu: Received error on socket.", w->id);
+ break;
+ }
+ }
+
+ web_client_reset(w);
+
+ log_access("%llu: %s port %s disconnected from thread task id %d", w->id, w->client_ip, w->client_port, gettid());
+ debug(D_WEB_CLIENT, "%llu: done...", w->id);
+
+ // close the sockets/files now
+ // to free file descriptors
+ if(w->ifd == w->ofd) {
+ if(w->ifd != -1) close(w->ifd);
+ }
+ else {
+ if(w->ifd != -1) close(w->ifd);
+ if(w->ofd != -1) close(w->ofd);
+ }
+ w->ifd = -1;
+ w->ofd = -1;
+
+ w->obsolete = 1;
+
+ pthread_exit(NULL);
+ return NULL;
}
diff --git a/src/web_client.h b/src/web_client.h
index f663be4a1..2555a0c24 100644
--- a/src/web_client.h
+++ b/src/web_client.h
@@ -1,92 +1,94 @@
-
-#ifdef NETDATA_WITH_ZLIB
-#include <zlib.h>
-#endif
-
-#include <sys/time.h>
-#include <string.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <netdb.h>
-
-#include "web_buffer.h"
-#include "dictionary.h"
+#ifndef NETDATA_WEB_CLIENT_H
+#define NETDATA_WEB_CLIENT_H 1
#define DEFAULT_DISCONNECT_IDLE_WEB_CLIENTS_AFTER_SECONDS 60
extern int web_client_timeout;
-extern int web_enable_gzip;
-#ifndef NETDATA_WEB_CLIENT_H
-#define NETDATA_WEB_CLIENT_H 1
+#ifdef NETDATA_WITH_ZLIB
+extern int web_enable_gzip, web_gzip_level, web_gzip_strategy, web_donotrack_comply;
+#endif /* NETDATA_WITH_ZLIB */
-#define WEB_CLIENT_MODE_NORMAL 0
-#define WEB_CLIENT_MODE_FILECOPY 1
-#define WEB_CLIENT_MODE_OPTIONS 2
+#define WEB_CLIENT_MODE_NORMAL 0
+#define WEB_CLIENT_MODE_FILECOPY 1
+#define WEB_CLIENT_MODE_OPTIONS 2
#define URL_MAX 8192
-#define ZLIB_CHUNK 16384
+#define ZLIB_CHUNK 16384
#define HTTP_RESPONSE_HEADER_SIZE 4096
#define COOKIE_MAX 1024
#define ORIGIN_MAX 1024
struct response {
- BUFFER *header; // our response header
- BUFFER *header_output; // internal use
- BUFFER *data; // our response data buffer
+ BUFFER *header; // our response header
+ BUFFER *header_output; // internal use
+ BUFFER *data; // our response data buffer
- int code; // the HTTP response code
+ int code; // the HTTP response code
- size_t rlen; // if non-zero, the excepted size of ifd (input)
- size_t sent; // current data length sent to output
+ size_t rlen; // if non-zero, the excepted size of ifd (input of firecopy)
+ size_t sent; // current data length sent to output
- int zoutput; // if set to 1, web_client_send() will send compressed data
+ int zoutput; // if set to 1, web_client_send() will send compressed data
#ifdef NETDATA_WITH_ZLIB
- z_stream zstream; // zlib stream for sending compressed output to client
- Bytef zbuffer[ZLIB_CHUNK]; // temporary buffer for storing compressed output
- long zsent; // the compressed bytes we have sent to the client
- long zhave; // the compressed bytes that we have to send
- int zinitialized;
-#endif
+ z_stream zstream; // zlib stream for sending compressed output to client
+ Bytef zbuffer[ZLIB_CHUNK]; // temporary buffer for storing compressed output
+ size_t zsent; // the compressed bytes we have sent to the client
+ size_t zhave; // the compressed bytes that we have received from zlib
+ int zinitialized:1;
+#endif /* NETDATA_WITH_ZLIB */
};
struct web_client {
- unsigned long long id;
+ unsigned long long id;
+
+ uint8_t obsolete:1; // if set to 1, the listener will remove this client
+ // after setting this to 1, you should not touch
+ // this web_client
+
+ uint8_t dead:1; // if set to 1, this client is dead
+
+ uint8_t keepalive:1; // if set to 1, the web client will be re-used
- char client_ip[NI_MAXHOST+1];
- char client_port[NI_MAXSERV+1];
+ uint8_t mode:3; // the operational mode of the client
- char last_url[URL_MAX+1];
+ uint8_t wait_receive:1; // 1 = we are waiting more input data
+ uint8_t wait_send:1; // 1 = we have data to send to the client
- struct timeval tv_in, tv_ready;
+ uint8_t donottrack:1; // 1 = we should not set cookies on this client
+ uint8_t tracking_required:1; // 1 = if the request requires cookies
- char cookie1[COOKIE_MAX+1];
- char cookie2[COOKIE_MAX+1];
- char origin[ORIGIN_MAX+1];
+ int tcp_cork; // 1 = we have a cork on the socket
- int mode;
- int keepalive;
- int enable_gzip;
- char *decoded_url;
+ int ifd;
+ int ofd;
- struct sockaddr_storage clientaddr;
+ char client_ip[NI_MAXHOST+1];
+ char client_port[NI_MAXSERV+1];
- pthread_t thread; // the thread servicing this client
- int obsolete; // if set to 1, the listener will remove this client
+ char decoded_url[URL_MAX + 1]; // we decode the URL in this buffer
+ char last_url[URL_MAX+1]; // we keep a copy of the decoded URL here
- int ifd;
- int ofd;
+ struct timeval tv_in, tv_ready;
- struct response response;
+ char cookie1[COOKIE_MAX+1];
+ char cookie2[COOKIE_MAX+1];
+ char origin[ORIGIN_MAX+1];
- int wait_receive;
- int wait_send;
+ struct sockaddr_storage clientaddr;
+ struct response response;
- struct web_client *prev;
- struct web_client *next;
+ size_t stats_received_bytes;
+ size_t stats_sent_bytes;
+
+ pthread_t thread; // the thread servicing this client
+
+ struct web_client *prev;
+ struct web_client *next;
};
+#define WEB_CLIENT_IS_DEAD(w) (w)->dead=1
+
extern struct web_client *web_clients;
extern uid_t web_files_uid(void);
@@ -94,7 +96,15 @@ extern uid_t web_files_gid(void);
extern struct web_client *web_client_create(int listener);
extern struct web_client *web_client_free(struct web_client *w);
+extern ssize_t web_client_send(struct web_client *w);
+extern ssize_t web_client_receive(struct web_client *w);
+extern void web_client_process(struct web_client *w);
+extern void web_client_reset(struct web_client *w);
extern void *web_client_main(void *ptr);
+extern int web_client_api_request_v1_data_group(char *name, int def);
+extern const char *group_method2string(int group);
+
+extern void buffer_data_options2string(BUFFER *wb, uint32_t options);
#endif
diff --git a/src/web_server.c b/src/web_server.c
index 0da72b5be..cf3687f3e 100644
--- a/src/web_server.c
+++ b/src/web_server.c
@@ -1,270 +1,601 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <unistd.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <errno.h>
-#include <pthread.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <netinet/tcp.h>
-#include <malloc.h>
-
#include "common.h"
-#include "log.h"
-#include "appconfig.h"
-#include "url.h"
-#include "web_buffer.h"
-#include "web_client.h"
-#include "web_server.h"
-#include "global_statistics.h"
-#include "rrd.h"
-#include "rrd2json.h"
-#include "../config.h"
int listen_backlog = LISTEN_BACKLOG;
-
-int listen_fd = -1;
+size_t listen_fds_count = 0;
+int listen_fds[MAX_LISTEN_FDS] = { [0 ... 99] = -1 };
+char *listen_fds_names[MAX_LISTEN_FDS] = { [0 ... 99] = NULL };
int listen_port = LISTEN_PORT;
+int web_server_mode = WEB_SERVER_MODE_MULTI_THREADED;
#ifdef NETDATA_INTERNAL_CHECKS
static void log_allocations(void)
{
- static int mem = 0;
+ static int mem = 0;
- struct mallinfo mi;
+ struct mallinfo mi;
- mi = mallinfo();
- if(mi.uordblks > mem) {
- int clients = 0;
- struct web_client *w;
- for(w = web_clients; w ; w = w->next) clients++;
+ mi = mallinfo();
+ if(mi.uordblks > mem) {
+ int clients = 0;
+ struct web_client *w;
+ for(w = web_clients; w ; w = w->next) clients++;
- info("Allocated memory increased from %d to %d (increased by %d bytes). There are %d web clients connected.", mem, mi.uordblks, mi.uordblks - mem, clients);
- mem = mi.uordblks;
- }
+ info("Allocated memory increased from %d to %d (increased by %d bytes). There are %d web clients connected.", mem, mi.uordblks, mi.uordblks - mem, clients);
+ mem = mi.uordblks;
+ }
}
#endif
-static int is_ip_anything(const char *ip)
-{
- if(!ip || !*ip
- || !strcmp(ip, "any")
- || !strcmp(ip, "all")
- || !strcmp(ip, "*")
- || !strcmp(ip, "::")
- || !strcmp(ip, "0.0.0.0")
- ) return 1;
-
- return 0;
+#ifndef HAVE_ACCEPT4
+int accept4(int sock, struct sockaddr *addr, socklen_t *addrlen, int flags) {
+ int fd = accept(sock, addr, addrlen);
+ int newflags = 0;
+
+ if (fd < 0) return fd;
+
+ if (flags & SOCK_NONBLOCK) {
+ newflags |= O_NONBLOCK;
+ flags &= ~SOCK_NONBLOCK;
+ }
+
+ if (flags & SOCK_CLOEXEC) {
+ newflags |= O_CLOEXEC;
+ flags &= ~SOCK_CLOEXEC;
+ }
+
+ if (flags) {
+ errno = -EINVAL;
+ return -1;
+ }
+
+ if (fcntl(fd, F_SETFL, newflags) < 0) {
+ int saved_errno = errno;
+ close(fd);
+ errno = saved_errno;
+ return -1;
+ }
+
+ return fd;
}
+#endif
-int create_listen_socket4(const char *ip, int port, int listen_backlog)
-{
- int sock;
- int sockopt = 1;
-
- debug(D_LISTENER, "IPv4 creating new listening socket on port %d", port);
-
- sock = socket(AF_INET, SOCK_STREAM, 0);
- if(sock < 0) {
- error("IPv4 socket() failed.");
- return -1;
- }
-
- /* avoid "address already in use" */
- setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void*)&sockopt, sizeof(sockopt));
-
- struct sockaddr_in name;
- memset(&name, 0, sizeof(struct sockaddr_in));
- name.sin_family = AF_INET;
- name.sin_port = htons (port);
-
- if(is_ip_anything(ip)) {
- name.sin_addr.s_addr = htonl(INADDR_ANY);
- info("Listening on any IPs (IPv4).");
- }
- else {
- int ret = inet_pton(AF_INET, ip, (void *)&name.sin_addr.s_addr);
- if(ret != 1) {
- error("Failed to convert IP '%s' to a valid IPv4 address.", ip);
- close(sock);
- return -1;
- }
- info("Listening on IP '%s' (IPv4).", ip);
- }
-
- if(bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0) {
- close(sock);
- error("IPv4 bind() failed.");
- return -1;
- }
-
- if(listen(sock, listen_backlog) < 0) {
- close(sock);
- fatal("IPv4 listen() failed.");
- return -1;
- }
-
- debug(D_LISTENER, "IPv4 listening port %d created", port);
- return sock;
+int create_listen_socket4(const char *ip, int port, int listen_backlog) {
+ int sock;
+ int sockopt = 1;
+
+ debug(D_LISTENER, "IPv4 creating new listening socket on ip '%s' port %d", ip, port);
+
+ sock = socket(AF_INET, SOCK_STREAM, 0);
+ if(sock < 0) {
+ error("IPv4 socket() on ip '%s' port %d failed.", ip, port);
+ return -1;
+ }
+
+ /* avoid "address already in use" */
+ if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void*)&sockopt, sizeof(sockopt)) != 0)
+ error("Cannot set SO_REUSEADDR on ip '%s' port's %d.", ip, port);
+
+ struct sockaddr_in name;
+ memset(&name, 0, sizeof(struct sockaddr_in));
+ name.sin_family = AF_INET;
+ name.sin_port = htons (port);
+
+ int ret = inet_pton(AF_INET, ip, (void *)&name.sin_addr.s_addr);
+ if(ret != 1) {
+ error("Failed to convert IP '%s' to a valid IPv4 address.", ip);
+ close(sock);
+ return -1;
+ }
+
+ if(bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0) {
+ close(sock);
+ error("IPv4 bind() on ip '%s' port %d failed.", ip, port);
+ return -1;
+ }
+
+ if(listen(sock, listen_backlog) < 0) {
+ close(sock);
+ fatal("IPv4 listen() on ip '%s' port %d failed.", ip, port);
+ return -1;
+ }
+
+ debug(D_LISTENER, "Listening on IPv4 ip '%s' port %d", ip, port);
+ return sock;
}
-int create_listen_socket6(const char *ip, int port, int listen_backlog)
-{
- int sock = -1;
- int sockopt = 1;
-
- debug(D_LISTENER, "IPv6 creating new listening socket on port %d", port);
-
- sock = socket(AF_INET6, SOCK_STREAM, 0);
- if (sock < 0) {
- error("IPv6 socket() failed. Disabling IPv6.");
- return -1;
- }
-
- /* avoid "address already in use" */
- setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void*)&sockopt, sizeof(sockopt));
-
- struct sockaddr_in6 name;
- memset(&name, 0, sizeof(struct sockaddr_in6));
- name.sin6_family = AF_INET6;
- name.sin6_port = htons ((uint16_t) port);
-
- if(is_ip_anything(ip)) {
- name.sin6_addr = in6addr_any;
- info("Listening on all IPs (IPv6 and IPv4)");
- }
- else {
- int ret = inet_pton(AF_INET6, ip, (void *)&name.sin6_addr.s6_addr);
- if(ret != 1) {
- error("Failed to convert IP '%s' to a valid IPv6 address. Disabling IPv6.", ip);
- close(sock);
- return -1;
- }
- info("Listening on IP '%s' (IPv6)", ip);
- }
-
- name.sin6_scope_id = 0;
-
- if (bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0) {
- close(sock);
- error("IPv6 bind() failed. Disabling IPv6.");
- return -1;
- }
-
- if (listen(sock, listen_backlog) < 0) {
- close(sock);
- error("IPv6 listen() failed. Disabling IPv6.");
- return -1;
- }
-
- debug(D_LISTENER, "IPv6 listening port %d created", port);
- return sock;
+int create_listen_socket6(const char *ip, int port, int listen_backlog) {
+ int sock = -1;
+ int sockopt = 1;
+ int ipv6only = 1;
+
+ debug(D_LISTENER, "IPv6 creating new listening socket on ip '%s' port %d", ip, port);
+
+ sock = socket(AF_INET6, SOCK_STREAM, 0);
+ if (sock < 0) {
+ error("IPv6 socket() on ip '%s' port %d failed.", ip, port);
+ return -1;
+ }
+
+ /* avoid "address already in use" */
+ if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void*)&sockopt, sizeof(sockopt)) != 0)
+ error("Cannot set SO_REUSEADDR on ip '%s' port's %d.", ip, port);
+
+ /* IPv6 only */
+ if(setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&ipv6only, sizeof(ipv6only)) != 0)
+ error("Cannot set IPV6_V6ONLY on ip '%s' port's %d.", ip, port);
+
+ struct sockaddr_in6 name;
+ memset(&name, 0, sizeof(struct sockaddr_in6));
+ name.sin6_family = AF_INET6;
+ name.sin6_port = htons ((uint16_t) port);
+
+ int ret = inet_pton(AF_INET6, ip, (void *)&name.sin6_addr.s6_addr);
+ if(ret != 1) {
+ error("Failed to convert IP '%s' to a valid IPv6 address.", ip);
+ close(sock);
+ return -1;
+ }
+
+ name.sin6_scope_id = 0;
+
+ if (bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0) {
+ close(sock);
+ error("IPv6 bind() on ip '%s' port %d failed.", ip, port);
+ return -1;
+ }
+
+ if (listen(sock, listen_backlog) < 0) {
+ close(sock);
+ error("IPv6 listen() on ip '%s' port %d failed.", ip, port);
+ return -1;
+ }
+
+ debug(D_LISTENER, "Listening on IPv6 ip '%s' port %d", ip, port);
+ return sock;
+}
+
+static inline int add_listen_socket(int fd, const char *ip, int port) {
+ if(listen_fds_count >= MAX_LISTEN_FDS) {
+ error("Too many listening sockets. Failed to add listening socket at ip '%s' port %d", ip, port);
+ close(fd);
+ return -1;
+ }
+
+ listen_fds[listen_fds_count] = fd;
+
+ char buffer[100 + 1];
+ snprintfz(buffer, 100, "[%s]:%d", ip, port);
+ listen_fds_names[listen_fds_count] = strdupz(buffer);
+
+ listen_fds_count++;
+ return 0;
+}
+
+int is_listen_socket(int fd) {
+ size_t i;
+ for(i = 0; i < listen_fds_count ;i++)
+ if(listen_fds[i] == fd) return 1;
+
+ return 0;
+}
+
+static inline void close_listen_sockets(void) {
+ size_t i;
+ for(i = 0; i < listen_fds_count ;i++) {
+ close(listen_fds[i]);
+ listen_fds[i] = -1;
+
+ freez(listen_fds_names[i]);
+ listen_fds_names[i] = NULL;
+ }
+
+ listen_fds_count = 0;
+}
+
+static inline int bind_to_one(const char *definition, int default_port, int listen_backlog) {
+ int added = 0;
+ struct addrinfo hints;
+ struct addrinfo *result = NULL, *rp = NULL;
+
+ char buffer[strlen(definition) + 1];
+ strcpy(buffer, definition);
+
+ char buffer2[10 + 1];
+ snprintfz(buffer2, 10, "%d", default_port);
+
+ char *ip = buffer, *port = buffer2;
+
+ char *e = ip;
+ if(*e == '[') {
+ e = ++ip;
+ while(*e && *e != ']') e++;
+ if(*e == ']') {
+ *e = '\0';
+ e++;
+ }
+ }
+ else {
+ while(*e && *e != ':') e++;
+ }
+
+ if(*e == ':') {
+ port = e + 1;
+ *e = '\0';
+ }
+
+ if(!*ip || *ip == '*' || !strcmp(ip, "any") || !strcmp(ip, "all"))
+ ip = NULL;
+ if(!*port)
+ port = buffer2;
+
+ memset(&hints, 0, sizeof(struct addrinfo));
+ hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
+ hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */
+ hints.ai_flags = AI_PASSIVE; /* For wildcard IP address */
+ hints.ai_protocol = 0; /* Any protocol */
+ hints.ai_canonname = NULL;
+ hints.ai_addr = NULL;
+ hints.ai_next = NULL;
+
+ int r = getaddrinfo(ip, port, &hints, &result);
+ if (r != 0) {
+ error("getaddrinfo('%s', '%s'): %s\n", ip, port, gai_strerror(r));
+ return -1;
+ }
+
+ for (rp = result; rp != NULL; rp = rp->ai_next) {
+ int fd = -1;
+
+ char rip[INET_ADDRSTRLEN + INET6_ADDRSTRLEN] = "INVALID";
+ int rport = default_port;
+
+ switch (rp->ai_addr->sa_family) {
+ case AF_INET: {
+ struct sockaddr_in *sin = (struct sockaddr_in *) rp->ai_addr;
+ inet_ntop(AF_INET, &sin->sin_addr, rip, INET_ADDRSTRLEN);
+ rport = ntohs(sin->sin_port);
+ fd = create_listen_socket4(rip, rport, listen_backlog);
+ break;
+ }
+
+ case AF_INET6: {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) rp->ai_addr;
+ inet_ntop(AF_INET6, &sin6->sin6_addr, rip, INET6_ADDRSTRLEN);
+ rport = ntohs(sin6->sin6_port);
+ fd = create_listen_socket6(rip, rport, listen_backlog);
+ break;
+ }
+ }
+
+ if (fd == -1)
+ error("Cannot bind to ip '%s', port %d", rip, default_port);
+ else {
+ add_listen_socket(fd, rip, rport);
+ added++;
+ }
+ }
+
+ freeaddrinfo(result);
+
+ return added;
}
+int create_listen_sockets(void) {
+ listen_backlog = (int) config_get_number("global", "http port listen backlog", LISTEN_BACKLOG);
+
+ if(config_exists("global", "bind socket to IP") && !config_exists("global", "bind to"))
+ config_rename("global", "bind socket to IP", "bind to");
+
+ if(config_exists("global", "port") && !config_exists("global", "default port"))
+ config_rename("global", "port", "default port");
+
+ listen_port = (int) config_get_number("global", "default port", LISTEN_PORT);
+ if(listen_port < 1 || listen_port > 65535) {
+ error("Invalid listen port %d given. Defaulting to %d.", listen_port, LISTEN_PORT);
+ listen_port = (int) config_set_number("global", "default port", LISTEN_PORT);
+ }
+ debug(D_OPTIONS, "Default listen port set to %d.", listen_port);
+
+ char *s = config_get("global", "bind to", "*");
+ while(*s) {
+ char *e = s;
+
+ // skip separators, moving both s(tart) and e(nd)
+ while(isspace(*e) || *e == ',') s = ++e;
+
+ // move e(nd) to the first separator
+ while(*e && !isspace(*e) && *e != ',') e++;
+
+ // is there anything?
+ if(!*s || s == e) break;
+
+ char buf[e - s + 1];
+ strncpyz(buf, s, e - s);
+ bind_to_one(buf, listen_port, listen_backlog);
+
+ s = e;
+ }
+
+ if(!listen_fds_count)
+ fatal("Cannot listen on any socket. Exiting...");
+
+ return (int)listen_fds_count;
+}
// --------------------------------------------------------------------------------------
// the main socket listener
+static inline void cleanup_web_clients(void) {
+ struct web_client *w;
+
+ for (w = web_clients; w;) {
+ if (w->obsolete) {
+ debug(D_WEB_CLIENT, "%llu: Removing client.", w->id);
+ // pthread_cancel(w->thread);
+ // pthread_join(w->thread, NULL);
+ w = web_client_free(w);
+#ifdef NETDATA_INTERNAL_CHECKS
+ log_allocations();
+#endif
+ }
+ else w = w->next;
+ }
+}
+
// 1. it accepts new incoming requests on our port
// 2. creates a new web_client for each connection received
// 3. spawns a new pthread to serve the client (this is optimal for keep-alive clients)
// 4. cleans up old web_clients that their pthreads have been exited
-void *socket_listen_main(void *ptr)
-{
- if(ptr) { ; }
-
- info("WEB SERVER thread created with task id %d", gettid());
-
- struct web_client *w;
- struct timeval tv;
- int retval;
-
- if(ptr) { ; }
-
- if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
- error("Cannot set pthread cancel type to DEFERRED.");
-
- if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
- error("Cannot set pthread cancel state to ENABLE.");
-
- web_client_timeout = (int) config_get_number("global", "disconnect idle web clients after seconds", DEFAULT_DISCONNECT_IDLE_WEB_CLIENTS_AFTER_SECONDS);
- web_enable_gzip = config_get_boolean("global", "enable web responses gzip compression", web_enable_gzip);
-
- if(listen_fd < 0) fatal("LISTENER: Listen socket is not ready.");
-
- fd_set ifds, ofds, efds;
- int fdmax = listen_fd;
-
- FD_ZERO (&ifds);
- FD_ZERO (&ofds);
- FD_ZERO (&efds);
-
- for(;;) {
- tv.tv_sec = 0;
- tv.tv_usec = 200000;
-
- if(listen_fd >= 0) {
- FD_SET(listen_fd, &ifds);
- FD_SET(listen_fd, &efds);
- }
-
- // debug(D_WEB_CLIENT, "LISTENER: Waiting...");
- retval = select(fdmax+1, &ifds, &ofds, &efds, &tv);
-
- if(retval == -1) {
- error("LISTENER: select() failed.");
- continue;
- }
- else if(retval) {
- // check for new incoming connections
- if(FD_ISSET(listen_fd, &ifds)) {
- w = web_client_create(listen_fd);
- if(unlikely(!w)) {
- // no need for error log - web_client_create already logged the error
- continue;
- }
-
- if(pthread_create(&w->thread, NULL, web_client_main, w) != 0) {
- error("%llu: failed to create new thread for web client.");
- w->obsolete = 1;
- }
- else if(pthread_detach(w->thread) != 0) {
- error("%llu: Cannot request detach of newly created web client thread.", w->id);
- w->obsolete = 1;
- }
- }
- else debug(D_WEB_CLIENT, "LISTENER: select() didn't do anything.");
-
- }
- //else {
- // debug(D_WEB_CLIENT, "LISTENER: select() timeout.");
- //}
-
- // cleanup unused clients
- for(w = web_clients; w ; w = w?w->next:NULL) {
- if(w->obsolete) {
- debug(D_WEB_CLIENT, "%llu: Removing client.", w->id);
- // pthread_join(w->thread, NULL);
- w = web_client_free(w);
-#ifdef NETDATA_INTERNAL_CHECKS
- log_allocations();
-#endif
- }
- }
- }
+#define CLEANUP_EVERY_EVENTS 100
+
+void *socket_listen_main_multi_threaded(void *ptr) {
+ (void)ptr;
+
+ web_server_mode = WEB_SERVER_MODE_MULTI_THREADED;
+ info("Multi-threaded WEB SERVER thread created with task id %d", gettid());
+
+ struct web_client *w;
+ int retval, counter = 0;
+
+ if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
+ error("Cannot set pthread cancel type to DEFERRED.");
+
+ if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
+ error("Cannot set pthread cancel state to ENABLE.");
+
+ if(!listen_fds_count)
+ fatal("LISTENER: No sockets to listen to.");
+
+ struct pollfd *fds = callocz(sizeof(struct pollfd), listen_fds_count);
+
+ size_t i;
+ for(i = 0; i < listen_fds_count ;i++) {
+ fds[i].fd = listen_fds[i];
+ fds[i].events = POLLIN;
+ fds[i].revents = 0;
+
+ info("Listening on '%s'", (listen_fds_names[i])?listen_fds_names[i]:"UNKNOWN");
+ }
+
+ int timeout = 10 * 1000;
+
+ for(;;) {
+ // debug(D_WEB_CLIENT, "LISTENER: Waiting...");
+ retval = poll(fds, listen_fds_count, timeout);
+
+ if(unlikely(retval == -1)) {
+ error("LISTENER: poll() failed.");
+ continue;
+ }
+ else if(unlikely(!retval)) {
+ debug(D_WEB_CLIENT, "LISTENER: select() timeout.");
+ counter = 0;
+ cleanup_web_clients();
+ continue;
+ }
+
+ for(i = 0 ; i < listen_fds_count ; i++) {
+ short int revents = fds[i].revents;
+
+ // check for new incoming connections
+ if(revents & POLLIN || revents & POLLPRI) {
+ fds[i].revents = 0;
+
+ w = web_client_create(fds[i].fd);
+ if(unlikely(!w)) {
+ // no need for error log - web_client_create already logged the error
+ continue;
+ }
+
+ if(pthread_create(&w->thread, NULL, web_client_main, w) != 0) {
+ error("%llu: failed to create new thread for web client.", w->id);
+ w->obsolete = 1;
+ }
+ else if(pthread_detach(w->thread) != 0) {
+ error("%llu: Cannot request detach of newly created web client thread.", w->id);
+ w->obsolete = 1;
+ }
+ }
+ }
+
+ // cleanup unused clients
+ counter++;
+ if(counter >= CLEANUP_EVERY_EVENTS) {
+ counter = 0;
+ cleanup_web_clients();
+ }
+ }
+
+ debug(D_WEB_CLIENT, "LISTENER: exit!");
+ close_listen_sockets();
+
+ return NULL;
+}
- error("LISTENER: exit!");
+struct web_client *single_threaded_clients[FD_SETSIZE];
- if(listen_fd >= 0) close(listen_fd);
- exit(2);
+static inline int single_threaded_link_client(struct web_client *w, fd_set *ifds, fd_set *ofds, fd_set *efds, int *max) {
+ if(unlikely(w->obsolete || w->dead || (!w->wait_receive && !w->wait_send)))
+ return 1;
- return NULL;
+ if(unlikely(w->ifd < 0 || w->ifd >= FD_SETSIZE || w->ofd < 0 || w->ofd >= FD_SETSIZE)) {
+ error("%llu: invalid file descriptor, ifd = %d, ofd = %d (required 0 <= fd < FD_SETSIZE (%d)", w->id, w->ifd, w->ofd, FD_SETSIZE);
+ return 1;
+ }
+
+ FD_SET(w->ifd, efds);
+ if(unlikely(*max < w->ifd)) *max = w->ifd;
+
+ if(unlikely(w->ifd != w->ofd)) {
+ if(*max < w->ofd) *max = w->ofd;
+ FD_SET(w->ofd, efds);
+ }
+
+ if(w->wait_receive) FD_SET(w->ifd, ifds);
+ if(w->wait_send) FD_SET(w->ofd, ofds);
+
+ single_threaded_clients[w->ifd] = w;
+ single_threaded_clients[w->ofd] = w;
+
+ return 0;
}
+static inline int single_threaded_unlink_client(struct web_client *w, fd_set *ifds, fd_set *ofds, fd_set *efds) {
+ FD_CLR(w->ifd, efds);
+ if(unlikely(w->ifd != w->ofd)) FD_CLR(w->ofd, efds);
+
+ if(w->wait_receive) FD_CLR(w->ifd, ifds);
+ if(w->wait_send) FD_CLR(w->ofd, ofds);
+
+ single_threaded_clients[w->ifd] = NULL;
+ single_threaded_clients[w->ofd] = NULL;
+
+ if(unlikely(w->obsolete || w->dead || (!w->wait_receive && !w->wait_send)))
+ return 1;
+
+ return 0;
+}
+
+void *socket_listen_main_single_threaded(void *ptr) {
+ (void)ptr;
+
+ web_server_mode = WEB_SERVER_MODE_SINGLE_THREADED;
+
+ info("Single-threaded WEB SERVER thread created with task id %d", gettid());
+
+ struct web_client *w;
+ int retval;
+
+ if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
+ error("Cannot set pthread cancel type to DEFERRED.");
+
+ if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
+ error("Cannot set pthread cancel state to ENABLE.");
+
+ if(!listen_fds_count)
+ fatal("LISTENER: no listen sockets available.");
+
+ size_t i;
+ for(i = 0; i < FD_SETSIZE ; i++)
+ single_threaded_clients[i] = NULL;
+
+ fd_set ifds, ofds, efds, rifds, rofds, refds;
+ FD_ZERO (&ifds);
+ FD_ZERO (&ofds);
+ FD_ZERO (&efds);
+ int fdmax = 0;
+
+ for(i = 0; i < listen_fds_count ; i++) {
+ if (listen_fds[i] < 0 || listen_fds[i] >= FD_SETSIZE)
+ fatal("LISTENER: Listen socket %d is not ready, or invalid.", listen_fds[i]);
+
+ info("Listening on '%s'", (listen_fds_names[i])?listen_fds_names[i]:"UNKNOWN");
+
+ FD_SET(listen_fds[i], &ifds);
+ FD_SET(listen_fds[i], &efds);
+ if(fdmax < listen_fds[i])
+ fdmax = listen_fds[i];
+ }
+
+ for(;;) {
+ debug(D_WEB_CLIENT_ACCESS, "LISTENER: single threaded web server waiting (fdmax = %d)...", fdmax);
+
+ struct timeval tv = { .tv_sec = 1, .tv_usec = 0 };
+ rifds = ifds;
+ rofds = ofds;
+ refds = efds;
+ retval = select(fdmax+1, &rifds, &rofds, &refds, &tv);
+
+ if(unlikely(retval == -1)) {
+ error("LISTENER: select() failed.");
+ continue;
+ }
+ else if(likely(retval)) {
+ debug(D_WEB_CLIENT_ACCESS, "LISTENER: got something.");
+
+ for(i = 0; i < listen_fds_count ; i++) {
+ if (FD_ISSET(listen_fds[i], &rifds)) {
+ debug(D_WEB_CLIENT_ACCESS, "LISTENER: new connection.");
+ w = web_client_create(listen_fds[i]);
+ if (single_threaded_link_client(w, &ifds, &ofds, &ifds, &fdmax) != 0) {
+ web_client_free(w);
+ }
+ }
+ }
+
+ for(i = 0 ; i <= (size_t)fdmax ; i++) {
+ if(likely(!FD_ISSET(i, &rifds) && !FD_ISSET(i, &rofds) && !FD_ISSET(i, &refds)))
+ continue;
+
+ w = single_threaded_clients[i];
+ if(unlikely(!w))
+ continue;
+
+ if(unlikely(single_threaded_unlink_client(w, &ifds, &ofds, &efds) != 0)) {
+ web_client_free(w);
+ continue;
+ }
+
+ if (unlikely(FD_ISSET(w->ifd, &refds) || FD_ISSET(w->ofd, &refds))) {
+ web_client_free(w);
+ continue;
+ }
+
+ if (unlikely(w->wait_receive && FD_ISSET(w->ifd, &rifds))) {
+ if (unlikely(web_client_receive(w) < 0)) {
+ web_client_free(w);
+ continue;
+ }
+
+ if (w->mode != WEB_CLIENT_MODE_FILECOPY) {
+ debug(D_WEB_CLIENT, "%llu: Processing received data.", w->id);
+ web_client_process(w);
+ }
+ }
+
+ if (unlikely(w->wait_send && FD_ISSET(w->ofd, &rofds))) {
+ if (unlikely(web_client_send(w) < 0)) {
+ debug(D_WEB_CLIENT, "%llu: Cannot send data to client. Closing client.", w->id);
+ web_client_free(w);
+ continue;
+ }
+ }
+
+ if(unlikely(single_threaded_link_client(w, &ifds, &ofds, &efds, &fdmax) != 0)) {
+ web_client_free(w);
+ }
+ }
+ }
+ else {
+ debug(D_WEB_CLIENT_ACCESS, "LISTENER: single threaded web server timeout.");
+#ifdef NETDATA_INTERNAL_CHECKS
+ log_allocations();
+#endif
+ }
+ }
+
+ debug(D_WEB_CLIENT, "LISTENER: exit!");
+ close_listen_sockets();
+ return NULL;
+}
diff --git a/src/web_server.h b/src/web_server.h
index 39f3bad91..93adc5b28 100644
--- a/src/web_server.h
+++ b/src/web_server.h
@@ -1,20 +1,38 @@
#ifndef NETDATA_WEB_SERVER_H
#define NETDATA_WEB_SERVER_H 1
-#define WEB_PATH_FILE "file"
-#define WEB_PATH_DATA "data"
-#define WEB_PATH_DATASOURCE "datasource"
-#define WEB_PATH_GRAPH "graph"
+#define WEB_PATH_FILE "file"
+#define WEB_PATH_DATA "data"
+#define WEB_PATH_DATASOURCE "datasource"
+#define WEB_PATH_GRAPH "graph"
#define LISTEN_PORT 19999
#define LISTEN_BACKLOG 100
-extern int listen_backlog;
-extern int listen_fd;
-extern int listen_port;
+#ifndef MAX_LISTEN_FDS
+#define MAX_LISTEN_FDS 100
+#endif
-extern int create_listen_socket4(const char *ip, int port, int listen_backlog);
-extern int create_listen_socket6(const char *ip, int port, int listen_backlog);
-extern void *socket_listen_main(void *ptr);
+#define WEB_SERVER_MODE_MULTI_THREADED 0
+#define WEB_SERVER_MODE_SINGLE_THREADED 1
+extern int web_server_mode;
+
+extern void *socket_listen_main_multi_threaded(void *ptr);
+extern void *socket_listen_main_single_threaded(void *ptr);
+extern int create_listen_sockets(void);
+extern int is_listen_socket(int fd);
+
+#ifndef HAVE_ACCEPT4
+extern int accept4(int sock, struct sockaddr *addr, socklen_t *addrlen, int flags);
+
+#ifndef SOCK_NONBLOCK
+#define SOCK_NONBLOCK 00004000
+#endif /* #ifndef SOCK_NONBLOCK */
+
+#ifndef SOCK_CLOEXEC
+#define SOCK_CLOEXEC 02000000
+#endif /* #ifndef SOCK_CLOEXEC */
+
+#endif /* #ifndef HAVE_ACCEPT4 */
#endif /* NETDATA_WEB_SERVER_H */